diff options
Diffstat (limited to 'libxslt/transform.c')
-rw-r--r-- | libxslt/transform.c | 6449 |
1 files changed, 6449 insertions, 0 deletions
diff --git a/libxslt/transform.c b/libxslt/transform.c new file mode 100644 index 0000000..b3fce80 --- /dev/null +++ b/libxslt/transform.c @@ -0,0 +1,6449 @@ +/* + * transform.c: Implementation of the XSL Transformation 1.0 engine + * transform part, i.e. applying a Stylesheet to a document + * + * References: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * Michael Kay "XSLT Programmer's Reference" pp 637-643 + * Writing Multiple Output Files + * + * XSLT-1.1 Working Draft + * http://www.w3.org/TR/xslt11#multiple-output + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> +#include <stdio.h> + +#include <libxml/xmlmemory.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/valid.h> +#include <libxml/hash.h> +#include <libxml/encoding.h> +#include <libxml/xmlerror.h> +#include <libxml/xpath.h> +#include <libxml/parserInternals.h> +#include <libxml/xpathInternals.h> +#include <libxml/HTMLtree.h> +#include <libxml/debugXML.h> +#include <libxml/uri.h> +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "pattern.h" +#include "transform.h" +#include "variables.h" +#include "numbersInternals.h" +#include "namespaces.h" +#include "attributes.h" +#include "templates.h" +#include "imports.h" +#include "keys.h" +#include "documents.h" +#include "extensions.h" +#include "extra.h" +#include "preproc.h" +#include "security.h" + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_EXTRA +#define WITH_XSLT_DEBUG_PROCESS +#define WITH_XSLT_DEBUG_VARIABLE +#endif + +#define XSLT_GENERATE_HTML_DOCTYPE +#ifdef XSLT_GENERATE_HTML_DOCTYPE +static int xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID, + const xmlChar **systemID); +#endif + +int xsltMaxDepth = 3000; +int xsltMaxVars = 15000; + +/* + * Useful macros + */ + +#ifndef FALSE +# define FALSE (0 == 1) +# define TRUE (!FALSE) +#endif + +#define IS_BLANK_NODE(n) \ + (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content))) + + +/* +* Forward declarations +*/ + +static xmlNsPtr +xsltCopyNamespaceListInternal(xmlNodePtr node, xmlNsPtr cur); + +static xmlNodePtr +xsltCopyTreeInternal(xsltTransformContextPtr ctxt, + xmlNodePtr invocNode, + xmlNodePtr node, + xmlNodePtr insert, int isLRE, int topElemVisited); + +static void +xsltApplySequenceConstructor(xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, xmlNodePtr list, + xsltTemplatePtr templ); + +static void +xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, + xmlNodePtr list, + xsltTemplatePtr templ, + xsltStackElemPtr withParams); + +/** + * templPush: + * @ctxt: the transformation context + * @value: the template to push on the stack + * + * Push a template on the stack + * + * Returns the new index in the stack or 0 in case of error + */ +static int +templPush(xsltTransformContextPtr ctxt, xsltTemplatePtr value) +{ + if (ctxt->templMax == 0) { + ctxt->templMax = 4; + ctxt->templTab = + (xsltTemplatePtr *) xmlMalloc(ctxt->templMax * + sizeof(ctxt->templTab[0])); + if (ctxt->templTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); + return (0); + } + } + else if (ctxt->templNr >= ctxt->templMax) { + ctxt->templMax *= 2; + ctxt->templTab = + (xsltTemplatePtr *) xmlRealloc(ctxt->templTab, + ctxt->templMax * + sizeof(ctxt->templTab[0])); + if (ctxt->templTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); + return (0); + } + } + ctxt->templTab[ctxt->templNr] = value; + ctxt->templ = value; + return (ctxt->templNr++); +} +/** + * templPop: + * @ctxt: the transformation context + * + * Pop a template value from the stack + * + * Returns the stored template value + */ +static xsltTemplatePtr +templPop(xsltTransformContextPtr ctxt) +{ + xsltTemplatePtr ret; + + if (ctxt->templNr <= 0) + return (0); + ctxt->templNr--; + if (ctxt->templNr > 0) + ctxt->templ = ctxt->templTab[ctxt->templNr - 1]; + else + ctxt->templ = (xsltTemplatePtr) 0; + ret = ctxt->templTab[ctxt->templNr]; + ctxt->templTab[ctxt->templNr] = 0; + return (ret); +} + +/** + * xsltLocalVariablePop: + * @ctxt: the transformation context + * @limitNr: number of variables which should remain + * @level: the depth in the xsl:template's tree + * + * Pops all variable values at the given @depth from the stack. + * + * Returns the stored variable value + * **NOTE:** + * This is an internal routine and should not be called by users! + */ +void +xsltLocalVariablePop(xsltTransformContextPtr ctxt, int limitNr, int level) +{ + xsltStackElemPtr variable; + + if (ctxt->varsNr <= 0) + return; + + do { + if (ctxt->varsNr <= limitNr) + break; + variable = ctxt->varsTab[ctxt->varsNr - 1]; + if (variable->level <= level) + break; + if (variable->level >= 0) + xsltFreeStackElemList(variable); + ctxt->varsNr--; + } while (ctxt->varsNr != 0); + if (ctxt->varsNr > 0) + ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1]; + else + ctxt->vars = NULL; +} + +/** + * xsltTemplateParamsCleanup: + * + * Removes xsl:param and xsl:with-param items from the + * variable-stack. Only xsl:with-param items are not freed. + */ +static void +xsltTemplateParamsCleanup(xsltTransformContextPtr ctxt) +{ + xsltStackElemPtr param; + + for (; ctxt->varsNr > ctxt->varsBase; ctxt->varsNr--) { + param = ctxt->varsTab[ctxt->varsNr -1]; + /* + * Free xsl:param items. + * xsl:with-param items will have a level of -1 or -2. + */ + if (param->level >= 0) { + xsltFreeStackElemList(param); + } + } + if (ctxt->varsNr > 0) + ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1]; + else + ctxt->vars = NULL; +} + +/** + * profPush: + * @ctxt: the transformation context + * @value: the profiling value to push on the stack + * + * Push a profiling value on the stack + * + * Returns the new index in the stack or 0 in case of error + */ +static int +profPush(xsltTransformContextPtr ctxt, long value) +{ + if (ctxt->profMax == 0) { + ctxt->profMax = 4; + ctxt->profTab = + (long *) xmlMalloc(ctxt->profMax * sizeof(ctxt->profTab[0])); + if (ctxt->profTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); + return (0); + } + } + else if (ctxt->profNr >= ctxt->profMax) { + ctxt->profMax *= 2; + ctxt->profTab = + (long *) xmlRealloc(ctxt->profTab, + ctxt->profMax * sizeof(ctxt->profTab[0])); + if (ctxt->profTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); + return (0); + } + } + ctxt->profTab[ctxt->profNr] = value; + ctxt->prof = value; + return (ctxt->profNr++); +} +/** + * profPop: + * @ctxt: the transformation context + * + * Pop a profiling value from the stack + * + * Returns the stored profiling value + */ +static long +profPop(xsltTransformContextPtr ctxt) +{ + long ret; + + if (ctxt->profNr <= 0) + return (0); + ctxt->profNr--; + if (ctxt->profNr > 0) + ctxt->prof = ctxt->profTab[ctxt->profNr - 1]; + else + ctxt->prof = (long) 0; + ret = ctxt->profTab[ctxt->profNr]; + ctxt->profTab[ctxt->profNr] = 0; + return (ret); +} + +static void +profCallgraphAdd(xsltTemplatePtr templ, xsltTemplatePtr parent) +{ + int i; + + if (templ->templMax == 0) { + templ->templMax = 4; + templ->templCalledTab = + (xsltTemplatePtr *) xmlMalloc(templ->templMax * + sizeof(templ->templCalledTab[0])); + templ->templCountTab = + (int *) xmlMalloc(templ->templMax * + sizeof(templ->templCountTab[0])); + if (templ->templCalledTab == NULL || templ->templCountTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); + return; + } + } + else if (templ->templNr >= templ->templMax) { + templ->templMax *= 2; + templ->templCalledTab = + (xsltTemplatePtr *) xmlRealloc(templ->templCalledTab, + templ->templMax * + sizeof(templ->templCalledTab[0])); + templ->templCountTab = + (int *) xmlRealloc(templ->templCountTab, + templ->templMax * + sizeof(templ->templCountTab[0])); + if (templ->templCalledTab == NULL || templ->templCountTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); + return; + } + } + + for (i = 0; i < templ->templNr; i++) { + if (templ->templCalledTab[i] == parent) { + templ->templCountTab[i]++; + break; + } + } + if (i == templ->templNr) { + /* not found, add new one */ + templ->templCalledTab[templ->templNr] = parent; + templ->templCountTab[templ->templNr] = 1; + templ->templNr++; + } +} + +/** + * xsltPreCompEval: + * @ctxt: transform context + * @node: context node + * @comp: precompiled expression + * + * Evaluate a precompiled XPath expression. + */ +static xmlXPathObjectPtr +xsltPreCompEval(xsltTransformContextPtr ctxt, xmlNodePtr node, + xsltStylePreCompPtr comp) { + xmlXPathObjectPtr res; + xmlXPathContextPtr xpctxt; + xmlNodePtr oldXPContextNode; + xmlNsPtr *oldXPNamespaces; + int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; + + xpctxt = ctxt->xpathCtxt; + oldXPContextNode = xpctxt->node; + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPContextSize = xpctxt->contextSize; + oldXPNsNr = xpctxt->nsNr; + oldXPNamespaces = xpctxt->namespaces; + + xpctxt->node = node; +#ifdef XSLT_REFACTORED + if (comp->inScopeNs != NULL) { + xpctxt->namespaces = comp->inScopeNs->list; + xpctxt->nsNr = comp->inScopeNs->xpathNumber; + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } +#else + xpctxt->namespaces = comp->nsList; + xpctxt->nsNr = comp->nsNr; +#endif + + res = xmlXPathCompiledEval(comp->comp, xpctxt); + + xpctxt->node = oldXPContextNode; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->contextSize = oldXPContextSize; + xpctxt->nsNr = oldXPNsNr; + xpctxt->namespaces = oldXPNamespaces; + + return(res); +} + +/** + * xsltPreCompEvalToBoolean: + * @ctxt: transform context + * @node: context node + * @comp: precompiled expression + * + * Evaluate a precompiled XPath expression as boolean. + */ +static int +xsltPreCompEvalToBoolean(xsltTransformContextPtr ctxt, xmlNodePtr node, + xsltStylePreCompPtr comp) { + int res; + xmlXPathContextPtr xpctxt; + xmlNodePtr oldXPContextNode; + xmlNsPtr *oldXPNamespaces; + int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; + + xpctxt = ctxt->xpathCtxt; + oldXPContextNode = xpctxt->node; + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPContextSize = xpctxt->contextSize; + oldXPNsNr = xpctxt->nsNr; + oldXPNamespaces = xpctxt->namespaces; + + xpctxt->node = node; +#ifdef XSLT_REFACTORED + if (comp->inScopeNs != NULL) { + xpctxt->namespaces = comp->inScopeNs->list; + xpctxt->nsNr = comp->inScopeNs->xpathNumber; + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } +#else + xpctxt->namespaces = comp->nsList; + xpctxt->nsNr = comp->nsNr; +#endif + + res = xmlXPathCompiledEvalToBoolean(comp->comp, xpctxt); + + xpctxt->node = oldXPContextNode; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->contextSize = oldXPContextSize; + xpctxt->nsNr = oldXPNsNr; + xpctxt->namespaces = oldXPNamespaces; + + return(res); +} + +/************************************************************************ + * * + * XInclude default settings * + * * + ************************************************************************/ + +static int xsltDoXIncludeDefault = 0; + +/** + * xsltSetXIncludeDefault: + * @xinclude: whether to do XInclude processing + * + * Set whether XInclude should be processed on document being loaded by default + */ +void +xsltSetXIncludeDefault(int xinclude) { + xsltDoXIncludeDefault = (xinclude != 0); +} + +/** + * xsltGetXIncludeDefault: + * + * Provides the default state for XInclude processing + * + * Returns 0 if there is no processing 1 otherwise + */ +int +xsltGetXIncludeDefault(void) { + return(xsltDoXIncludeDefault); +} + +unsigned long xsltDefaultTrace = (unsigned long) XSLT_TRACE_ALL; + +/** + * xsltDebugSetDefaultTrace: + * @val: tracing level mask + * + * Set the default debug tracing level mask + */ +void xsltDebugSetDefaultTrace(xsltDebugTraceCodes val) { + xsltDefaultTrace = val; +} + +/** + * xsltDebugGetDefaultTrace: + * + * Get the current default debug tracing level mask + * + * Returns the current default debug tracing level mask + */ +xsltDebugTraceCodes xsltDebugGetDefaultTrace() { + return xsltDefaultTrace; +} + +/************************************************************************ + * * + * Handling of Transformation Contexts * + * * + ************************************************************************/ + +static xsltTransformCachePtr +xsltTransformCacheCreate(void) +{ + xsltTransformCachePtr ret; + + ret = (xsltTransformCachePtr) xmlMalloc(sizeof(xsltTransformCache)); + if (ret == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltTransformCacheCreate : malloc failed\n"); + return(NULL); + } + memset(ret, 0, sizeof(xsltTransformCache)); + return(ret); +} + +static void +xsltTransformCacheFree(xsltTransformCachePtr cache) +{ + if (cache == NULL) + return; + /* + * Free tree fragments. + */ + if (cache->RVT) { + xmlDocPtr tmp, cur = cache->RVT; + while (cur) { + tmp = cur; + cur = (xmlDocPtr) cur->next; + if (tmp->_private != NULL) { + /* + * Tree the document info. + */ + xsltFreeDocumentKeys((xsltDocumentPtr) tmp->_private); + xmlFree(tmp->_private); + } + xmlFreeDoc(tmp); + } + } + /* + * Free vars/params. + */ + if (cache->stackItems) { + xsltStackElemPtr tmp, cur = cache->stackItems; + while (cur) { + tmp = cur; + cur = cur->next; + /* + * REVISIT TODO: Should be call a destruction-function + * instead? + */ + xmlFree(tmp); + } + } + xmlFree(cache); +} + +/** + * xsltNewTransformContext: + * @style: a parsed XSLT stylesheet + * @doc: the input document + * + * Create a new XSLT TransformContext + * + * Returns the newly allocated xsltTransformContextPtr or NULL in case of error + */ +xsltTransformContextPtr +xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) { + xsltTransformContextPtr cur; + xsltDocumentPtr docu; + int i; + + xsltInitGlobals(); + + cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, (xmlNodePtr)doc, + "xsltNewTransformContext : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltTransformContext)); + + cur->cache = xsltTransformCacheCreate(); + if (cur->cache == NULL) + goto internal_err; + /* + * setup of the dictionary must be done early as some of the + * processing later like key handling may need it. + */ + cur->dict = xmlDictCreateSub(style->dict); + cur->internalized = ((style->internalized) && (cur->dict != NULL)); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "Creating sub-dictionary from stylesheet for transformation\n"); +#endif + + /* + * initialize the template stack + */ + cur->templTab = (xsltTemplatePtr *) + xmlMalloc(10 * sizeof(xsltTemplatePtr)); + if (cur->templTab == NULL) { + xsltTransformError(NULL, NULL, (xmlNodePtr) doc, + "xsltNewTransformContext: out of memory\n"); + goto internal_err; + } + cur->templNr = 0; + cur->templMax = 5; + cur->templ = NULL; + cur->maxTemplateDepth = xsltMaxDepth; + + /* + * initialize the variables stack + */ + cur->varsTab = (xsltStackElemPtr *) + xmlMalloc(10 * sizeof(xsltStackElemPtr)); + if (cur->varsTab == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltNewTransformContext: out of memory\n"); + goto internal_err; + } + cur->varsNr = 0; + cur->varsMax = 10; + cur->vars = NULL; + cur->varsBase = 0; + cur->maxTemplateVars = xsltMaxVars; + + /* + * the profiling stack is not initialized by default + */ + cur->profTab = NULL; + cur->profNr = 0; + cur->profMax = 0; + cur->prof = 0; + + cur->style = style; + xmlXPathInit(); + cur->xpathCtxt = xmlXPathNewContext(doc); + if (cur->xpathCtxt == NULL) { + xsltTransformError(NULL, NULL, (xmlNodePtr) doc, + "xsltNewTransformContext : xmlXPathNewContext failed\n"); + goto internal_err; + } + /* + * Create an XPath cache. + */ + if (xmlXPathContextSetCache(cur->xpathCtxt, 1, -1, 0) == -1) + goto internal_err; + /* + * Initialize the extras array + */ + if (style->extrasNr != 0) { + cur->extrasMax = style->extrasNr + 20; + cur->extras = (xsltRuntimeExtraPtr) + xmlMalloc(cur->extrasMax * sizeof(xsltRuntimeExtra)); + if (cur->extras == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltNewTransformContext: out of memory\n"); + goto internal_err; + } + cur->extrasNr = style->extrasNr; + for (i = 0;i < cur->extrasMax;i++) { + cur->extras[i].info = NULL; + cur->extras[i].deallocate = NULL; + cur->extras[i].val.ptr = NULL; + } + } else { + cur->extras = NULL; + cur->extrasNr = 0; + cur->extrasMax = 0; + } + + XSLT_REGISTER_VARIABLE_LOOKUP(cur); + XSLT_REGISTER_FUNCTION_LOOKUP(cur); + cur->xpathCtxt->nsHash = style->nsHash; + /* + * Initialize the registered external modules + */ + xsltInitCtxtExts(cur); + /* + * Setup document element ordering for later efficiencies + * (bug 133289) + */ + if (xslDebugStatus == XSLT_DEBUG_NONE) + xmlXPathOrderDocElems(doc); + /* + * Must set parserOptions before calling xsltNewDocument + * (bug 164530) + */ + cur->parserOptions = XSLT_PARSE_OPTIONS; + docu = xsltNewDocument(cur, doc); + if (docu == NULL) { + xsltTransformError(cur, NULL, (xmlNodePtr)doc, + "xsltNewTransformContext : xsltNewDocument failed\n"); + goto internal_err; + } + docu->main = 1; + cur->document = docu; + cur->inst = NULL; + cur->outputFile = NULL; + cur->sec = xsltGetDefaultSecurityPrefs(); + cur->debugStatus = xslDebugStatus; + cur->traceCode = (unsigned long*) &xsltDefaultTrace; + cur->xinclude = xsltGetXIncludeDefault(); + cur->keyInitLevel = 0; + + return(cur); + +internal_err: + if (cur != NULL) + xsltFreeTransformContext(cur); + return(NULL); +} + +/** + * xsltFreeTransformContext: + * @ctxt: an XSLT parser context + * + * Free up the memory allocated by @ctxt + */ +void +xsltFreeTransformContext(xsltTransformContextPtr ctxt) { + if (ctxt == NULL) + return; + + /* + * Shutdown the extension modules associated to the stylesheet + * used if needed. + */ + xsltShutdownCtxtExts(ctxt); + + if (ctxt->xpathCtxt != NULL) { + ctxt->xpathCtxt->nsHash = NULL; + xmlXPathFreeContext(ctxt->xpathCtxt); + } + if (ctxt->templTab != NULL) + xmlFree(ctxt->templTab); + if (ctxt->varsTab != NULL) + xmlFree(ctxt->varsTab); + if (ctxt->profTab != NULL) + xmlFree(ctxt->profTab); + if ((ctxt->extrasNr > 0) && (ctxt->extras != NULL)) { + int i; + + for (i = 0;i < ctxt->extrasNr;i++) { + if ((ctxt->extras[i].deallocate != NULL) && + (ctxt->extras[i].info != NULL)) + ctxt->extras[i].deallocate(ctxt->extras[i].info); + } + xmlFree(ctxt->extras); + } + xsltFreeGlobalVariables(ctxt); + xsltFreeDocuments(ctxt); + xsltFreeCtxtExts(ctxt); + xsltFreeRVTs(ctxt); + xsltTransformCacheFree(ctxt->cache); + xmlDictFree(ctxt->dict); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "freeing transformation dictionary\n"); +#endif + memset(ctxt, -1, sizeof(xsltTransformContext)); + xmlFree(ctxt); +} + +/************************************************************************ + * * + * Copy of Nodes in an XSLT fashion * + * * + ************************************************************************/ + +xmlNodePtr xsltCopyTree(xsltTransformContextPtr ctxt, + xmlNodePtr node, xmlNodePtr insert, int literal); + +/** + * xsltAddChild: + * @parent: the parent node + * @cur: the child node + * + * Wrapper version of xmlAddChild with a more consistent behaviour on + * error. One expect the use to be child = xsltAddChild(parent, child); + * and the routine will take care of not leaking on errors or node merge + * + * Returns the child is successfully attached or NULL if merged or freed + */ +static xmlNodePtr +xsltAddChild(xmlNodePtr parent, xmlNodePtr cur) { + xmlNodePtr ret; + + if (cur == NULL) + return(NULL); + if (parent == NULL) { + xmlFreeNode(cur); + return(NULL); + } + ret = xmlAddChild(parent, cur); + + return(ret); +} + +/** + * xsltAddTextString: + * @ctxt: a XSLT process context + * @target: the text node where the text will be attached + * @string: the text string + * @len: the string length in byte + * + * Extend the current text node with the new string, it handles coalescing + * + * Returns: the text node + */ +static xmlNodePtr +xsltAddTextString(xsltTransformContextPtr ctxt, xmlNodePtr target, + const xmlChar *string, int len) { + /* + * optimization + */ + if ((len <= 0) || (string == NULL) || (target == NULL)) + return(target); + + if (ctxt->lasttext == target->content) { + + if (ctxt->lasttuse + len >= ctxt->lasttsize) { + xmlChar *newbuf; + int size; + + size = ctxt->lasttsize + len + 100; + size *= 2; + newbuf = (xmlChar *) xmlRealloc(target->content,size); + if (newbuf == NULL) { + xsltTransformError(ctxt, NULL, target, + "xsltCopyText: text allocation failed\n"); + return(NULL); + } + ctxt->lasttsize = size; + ctxt->lasttext = newbuf; + target->content = newbuf; + } + memcpy(&(target->content[ctxt->lasttuse]), string, len); + ctxt->lasttuse += len; + target->content[ctxt->lasttuse] = 0; + } else { + xmlNodeAddContent(target, string); + ctxt->lasttext = target->content; + len = xmlStrlen(target->content); + ctxt->lasttsize = len; + ctxt->lasttuse = len; + } + return(target); +} + +/** + * xsltCopyTextString: + * @ctxt: a XSLT process context + * @target: the element where the text will be attached + * @string: the text string + * @noescape: should disable-escaping be activated for this text node. + * + * Adds @string to a newly created or an existent text node child of + * @target. + * + * Returns: the text node, where the text content of @cur is copied to. + * NULL in case of API or internal errors. + */ +xmlNodePtr +xsltCopyTextString(xsltTransformContextPtr ctxt, xmlNodePtr target, + const xmlChar *string, int noescape) +{ + xmlNodePtr copy; + int len; + + if (string == NULL) + return(NULL); + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopyTextString: copy text %s\n", + string)); +#endif + + /* + * Play safe and reset the merging mechanism for every new + * target node. + */ + if ((target == NULL) || (target->children == NULL)) { + ctxt->lasttext = NULL; + } + + /* handle coalescing of text nodes here */ + len = xmlStrlen(string); + if ((ctxt->type == XSLT_OUTPUT_XML) && + (ctxt->style->cdataSection != NULL) && + (target != NULL) && + (target->type == XML_ELEMENT_NODE) && + (((target->ns == NULL) && + (xmlHashLookup2(ctxt->style->cdataSection, + target->name, NULL) != NULL)) || + ((target->ns != NULL) && + (xmlHashLookup2(ctxt->style->cdataSection, + target->name, target->ns->href) != NULL)))) + { + /* + * Process "cdata-section-elements". + */ + if ((target->last != NULL) && + (target->last->type == XML_CDATA_SECTION_NODE)) + { + return(xsltAddTextString(ctxt, target->last, string, len)); + } + copy = xmlNewCDataBlock(ctxt->output, string, len); + } else if (noescape) { + /* + * Process "disable-output-escaping". + */ + if ((target != NULL) && (target->last != NULL) && + (target->last->type == XML_TEXT_NODE) && + (target->last->name == xmlStringTextNoenc)) + { + return(xsltAddTextString(ctxt, target->last, string, len)); + } + copy = xmlNewTextLen(string, len); + if (copy != NULL) + copy->name = xmlStringTextNoenc; + } else { + /* + * Default processing. + */ + if ((target != NULL) && (target->last != NULL) && + (target->last->type == XML_TEXT_NODE) && + (target->last->name == xmlStringText)) { + return(xsltAddTextString(ctxt, target->last, string, len)); + } + copy = xmlNewTextLen(string, len); + } + if (copy != NULL && target != NULL) + copy = xsltAddChild(target, copy); + if (copy != NULL) { + ctxt->lasttext = copy->content; + ctxt->lasttsize = len; + ctxt->lasttuse = len; + } else { + xsltTransformError(ctxt, NULL, target, + "xsltCopyTextString: text copy failed\n"); + ctxt->lasttext = NULL; + } + return(copy); +} + +/** + * xsltCopyText: + * @ctxt: a XSLT process context + * @target: the element where the text will be attached + * @cur: the text or CDATA node + * @interned: the string is in the target doc dictionary + * + * Copy the text content of @cur and append it to @target's children. + * + * Returns: the text node, where the text content of @cur is copied to. + * NULL in case of API or internal errors. + */ +static xmlNodePtr +xsltCopyText(xsltTransformContextPtr ctxt, xmlNodePtr target, + xmlNodePtr cur, int interned) +{ + xmlNodePtr copy; + + if ((cur->type != XML_TEXT_NODE) && + (cur->type != XML_CDATA_SECTION_NODE)) + return(NULL); + if (cur->content == NULL) + return(NULL); + +#ifdef WITH_XSLT_DEBUG_PROCESS + if (cur->type == XML_CDATA_SECTION_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopyText: copy CDATA text %s\n", + cur->content)); + } else if (cur->name == xmlStringTextNoenc) { + XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopyText: copy unescaped text %s\n", + cur->content)); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopyText: copy text %s\n", + cur->content)); + } +#endif + + /* + * Play save and reset the merging mechanism for every new + * target node. + */ + if ((target == NULL) || (target->children == NULL)) { + ctxt->lasttext = NULL; + } + + if ((ctxt->style->cdataSection != NULL) && + (ctxt->type == XSLT_OUTPUT_XML) && + (target != NULL) && + (target->type == XML_ELEMENT_NODE) && + (((target->ns == NULL) && + (xmlHashLookup2(ctxt->style->cdataSection, + target->name, NULL) != NULL)) || + ((target->ns != NULL) && + (xmlHashLookup2(ctxt->style->cdataSection, + target->name, target->ns->href) != NULL)))) + { + /* + * Process "cdata-section-elements". + */ + /* + * OPTIMIZE TODO: xsltCopyText() is also used for attribute content. + */ + /* + * TODO: Since this doesn't merge adjacent CDATA-section nodes, + * we'll get: <![CDATA[x]]><!CDATA[y]]>. + * TODO: Reported in #321505. + */ + if ((target->last != NULL) && + (target->last->type == XML_CDATA_SECTION_NODE)) + { + /* + * Append to existing CDATA-section node. + */ + copy = xsltAddTextString(ctxt, target->last, cur->content, + xmlStrlen(cur->content)); + goto exit; + } else { + unsigned int len; + + len = xmlStrlen(cur->content); + copy = xmlNewCDataBlock(ctxt->output, cur->content, len); + if (copy == NULL) + goto exit; + ctxt->lasttext = copy->content; + ctxt->lasttsize = len; + ctxt->lasttuse = len; + } + } else if ((target != NULL) && + (target->last != NULL) && + /* both escaped or both non-escaped text-nodes */ + (((target->last->type == XML_TEXT_NODE) && + (target->last->name == cur->name)) || + /* non-escaped text nodes and CDATA-section nodes */ + (((target->last->type == XML_CDATA_SECTION_NODE) && + (cur->name == xmlStringTextNoenc))))) + { + /* + * we are appending to an existing text node + */ + copy = xsltAddTextString(ctxt, target->last, cur->content, + xmlStrlen(cur->content)); + goto exit; + } else if ((interned) && (target != NULL) && + (target->doc != NULL) && + (target->doc->dict == ctxt->dict)) + { + /* + * TODO: DO we want to use this also for "text" output? + */ + copy = xmlNewTextLen(NULL, 0); + if (copy == NULL) + goto exit; + if (cur->name == xmlStringTextNoenc) + copy->name = xmlStringTextNoenc; + + /* + * Must confirm that content is in dict (bug 302821) + * TODO: This check should be not needed for text coming + * from the stylesheets + */ + if (xmlDictOwns(ctxt->dict, cur->content)) + copy->content = cur->content; + else { + if ((copy->content = xmlStrdup(cur->content)) == NULL) + return NULL; + } + } else { + /* + * normal processing. keep counters to extend the text node + * in xsltAddTextString if needed. + */ + unsigned int len; + + len = xmlStrlen(cur->content); + copy = xmlNewTextLen(cur->content, len); + if (copy == NULL) + goto exit; + if (cur->name == xmlStringTextNoenc) + copy->name = xmlStringTextNoenc; + ctxt->lasttext = copy->content; + ctxt->lasttsize = len; + ctxt->lasttuse = len; + } + if (copy != NULL) { + if (target != NULL) { + copy->doc = target->doc; + /* + * MAYBE TODO: Maybe we should reset the ctxt->lasttext here + * to ensure that the optimized text-merging mechanism + * won't interfere with normal node-merging in any case. + */ + copy = xsltAddChild(target, copy); + } + } else { + xsltTransformError(ctxt, NULL, target, + "xsltCopyText: text copy failed\n"); + } + +exit: + if ((copy == NULL) || (copy->content == NULL)) { + xsltTransformError(ctxt, NULL, target, + "Internal error in xsltCopyText(): " + "Failed to copy the string.\n"); + ctxt->state = XSLT_STATE_STOPPED; + } + return(copy); +} + +/** + * xsltShallowCopyAttr: + * @ctxt: a XSLT process context + * @invocNode: responsible node in the stylesheet; used for error reports + * @target: the element where the attribute will be grafted + * @attr: the attribute to be copied + * + * Do a copy of an attribute. + * Called by: + * - xsltCopyTreeInternal() + * - xsltCopyOf() + * - xsltCopy() + * + * Returns: a new xmlAttrPtr, or NULL in case of error. + */ +static xmlAttrPtr +xsltShallowCopyAttr(xsltTransformContextPtr ctxt, xmlNodePtr invocNode, + xmlNodePtr target, xmlAttrPtr attr) +{ + xmlAttrPtr copy; + xmlChar *value; + + if (attr == NULL) + return(NULL); + + if (target->type != XML_ELEMENT_NODE) { + xsltTransformError(ctxt, NULL, invocNode, + "Cannot add an attribute node to a non-element node.\n"); + return(NULL); + } + + if (target->children != NULL) { + xsltTransformError(ctxt, NULL, invocNode, + "Attribute nodes must be added before " + "any child nodes to an element.\n"); + return(NULL); + } + + value = xmlNodeListGetString(attr->doc, attr->children, 1); + if (attr->ns != NULL) { + xmlNsPtr ns; + + ns = xsltGetSpecialNamespace(ctxt, invocNode, + attr->ns->href, attr->ns->prefix, target); + if (ns == NULL) { + xsltTransformError(ctxt, NULL, invocNode, + "Namespace fixup error: Failed to acquire an in-scope " + "namespace binding of the copied attribute '{%s}%s'.\n", + attr->ns->href, attr->name); + /* + * TODO: Should we just stop here? + */ + } + /* + * Note that xmlSetNsProp() will take care of duplicates + * and assigns the new namespace even to a duplicate. + */ + copy = xmlSetNsProp(target, ns, attr->name, value); + } else { + copy = xmlSetNsProp(target, NULL, attr->name, value); + } + if (value != NULL) + xmlFree(value); + + if (copy == NULL) + return(NULL); + +#if 0 + /* + * NOTE: This was optimized according to bug #342695. + * TODO: Can this further be optimized, if source and target + * share the same dict and attr->children is just 1 text node + * which is in the dict? How probable is such a case? + */ + /* + * TODO: Do we need to create an empty text node if the value + * is the empty string? + */ + value = xmlNodeListGetString(attr->doc, attr->children, 1); + if (value != NULL) { + txtNode = xmlNewDocText(target->doc, NULL); + if (txtNode == NULL) + return(NULL); + if ((target->doc != NULL) && + (target->doc->dict != NULL)) + { + txtNode->content = + (xmlChar *) xmlDictLookup(target->doc->dict, + BAD_CAST value, -1); + xmlFree(value); + } else + txtNode->content = value; + copy->children = txtNode; + } +#endif + + return(copy); +} + +/** + * xsltCopyAttrListNoOverwrite: + * @ctxt: a XSLT process context + * @invocNode: responsible node in the stylesheet; used for error reports + * @target: the element where the new attributes will be grafted + * @attr: the first attribute in the list to be copied + * + * Copies a list of attribute nodes, starting with @attr, over to the + * @target element node. + * + * Called by: + * - xsltCopyTreeInternal() + * + * Returns 0 on success and -1 on errors and internal errors. + */ +static int +xsltCopyAttrListNoOverwrite(xsltTransformContextPtr ctxt, + xmlNodePtr invocNode, + xmlNodePtr target, xmlAttrPtr attr) +{ + xmlAttrPtr copy; + xmlNsPtr origNs = NULL, copyNs = NULL; + xmlChar *value; + + /* + * Don't use xmlCopyProp() here, since it will try to + * reconciliate namespaces. + */ + while (attr != NULL) { + /* + * Find a namespace node in the tree of @target. + * Avoid searching for the same ns. + */ + if (attr->ns != origNs) { + origNs = attr->ns; + if (attr->ns != NULL) { + copyNs = xsltGetSpecialNamespace(ctxt, invocNode, + attr->ns->href, attr->ns->prefix, target); + if (copyNs == NULL) + return(-1); + } else + copyNs = NULL; + } + /* + * If attribute has a value, we need to copy it (watching out + * for possible entities) + */ + if ((attr->children) && (attr->children->type == XML_TEXT_NODE) && + (attr->children->next == NULL)) { + copy = xmlNewNsProp(target, copyNs, attr->name, + attr->children->content); + } else if (attr->children != NULL) { + value = xmlNodeListGetString(attr->doc, attr->children, 1); + copy = xmlNewNsProp(target, copyNs, attr->name, BAD_CAST value); + xmlFree(value); + } else { + copy = xmlNewNsProp(target, copyNs, attr->name, NULL); + } + + if (copy == NULL) + return(-1); + + attr = attr->next; + } + return(0); +} + +/** + * xsltShallowCopyElem: + * @ctxt: the XSLT process context + * @node: the element node in the source tree + * or the Literal Result Element + * @insert: the parent in the result tree + * @isLRE: if @node is a Literal Result Element + * + * Make a copy of the element node @node + * and insert it as last child of @insert. + * + * URGENT TODO: The problem with this one (for the non-refactored code) + * is that it is used for both, Literal Result Elements *and* + * copying input nodes. + * + * BIG NOTE: This is only called for XML_ELEMENT_NODEs. + * + * Called from: + * xsltApplySequenceConstructor() + * (for Literal Result Elements - which is a problem) + * xsltCopy() (for shallow-copying elements via xsl:copy) + * + * Returns a pointer to the new node, or NULL in case of error + */ +static xmlNodePtr +xsltShallowCopyElem(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr insert, int isLRE) +{ + xmlNodePtr copy; + + if ((node->type == XML_DTD_NODE) || (insert == NULL)) + return(NULL); + if ((node->type == XML_TEXT_NODE) || + (node->type == XML_CDATA_SECTION_NODE)) + return(xsltCopyText(ctxt, insert, node, 0)); + + copy = xmlDocCopyNode(node, insert->doc, 0); + if (copy != NULL) { + copy->doc = ctxt->output; + copy = xsltAddChild(insert, copy); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, node, + "xsltShallowCopyElem: copy failed\n"); + return (copy); + } + + if (node->type == XML_ELEMENT_NODE) { + /* + * Add namespaces as they are needed + */ + if (node->nsDef != NULL) { + /* + * TODO: Remove the LRE case in the refactored code + * gets enabled. + */ + if (isLRE) + xsltCopyNamespaceList(ctxt, copy, node->nsDef); + else + xsltCopyNamespaceListInternal(copy, node->nsDef); + } + + /* + * URGENT TODO: The problem with this is that it does not + * copy over all namespace nodes in scope. + * The damn thing about this is, that we would need to + * use the xmlGetNsList(), for every single node; this is + * also done in xsltCopyTreeInternal(), but only for the top node. + */ + if (node->ns != NULL) { + if (isLRE) { + /* + * REVISIT TODO: Since the non-refactored code still does + * ns-aliasing, we need to call xsltGetNamespace() here. + * Remove this when ready. + */ + copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy); + } else { + copy->ns = xsltGetSpecialNamespace(ctxt, + node, node->ns->href, node->ns->prefix, copy); + + } + } else if ((insert->type == XML_ELEMENT_NODE) && + (insert->ns != NULL)) + { + /* + * "Undeclare" the default namespace. + */ + xsltGetSpecialNamespace(ctxt, node, NULL, NULL, copy); + } + } + } else { + xsltTransformError(ctxt, NULL, node, + "xsltShallowCopyElem: copy %s failed\n", node->name); + } + return(copy); +} + +/** + * xsltCopyTreeList: + * @ctxt: a XSLT process context + * @invocNode: responsible node in the stylesheet; used for error reports + * @list: the list of element nodes in the source tree. + * @insert: the parent in the result tree. + * @isLRE: is this a literal result element list + * @topElemVisited: indicates if a top-most element was already processed + * + * Make a copy of the full list of tree @list + * and insert it as last children of @insert + * + * NOTE: Not to be used for Literal Result Elements. + * + * Used by: + * - xsltCopyOf() + * + * Returns a pointer to the new list, or NULL in case of error + */ +static xmlNodePtr +xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr invocNode, + xmlNodePtr list, + xmlNodePtr insert, int isLRE, int topElemVisited) +{ + xmlNodePtr copy, ret = NULL; + + while (list != NULL) { + copy = xsltCopyTreeInternal(ctxt, invocNode, + list, insert, isLRE, topElemVisited); + if (copy != NULL) { + if (ret == NULL) { + ret = copy; + } + } + list = list->next; + } + return(ret); +} + +/** + * xsltCopyNamespaceListInternal: + * @node: the target node + * @cur: the first namespace + * + * Do a copy of a namespace list. If @node is non-NULL the + * new namespaces are added automatically. + * Called by: + * xsltCopyTreeInternal() + * + * QUESTION: What is the exact difference between this function + * and xsltCopyNamespaceList() in "namespaces.c"? + * ANSWER: xsltCopyNamespaceList() tries to apply ns-aliases. + * + * Returns: a new xmlNsPtr, or NULL in case of error. + */ +static xmlNsPtr +xsltCopyNamespaceListInternal(xmlNodePtr elem, xmlNsPtr ns) { + xmlNsPtr ret = NULL; + xmlNsPtr p = NULL, q, luNs; + + if (ns == NULL) + return(NULL); + /* + * One can add namespaces only on element nodes + */ + if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE)) + elem = NULL; + + do { + if (ns->type != XML_NAMESPACE_DECL) + break; + /* + * Avoid duplicating namespace declarations on the tree. + */ + if (elem != NULL) { + if ((elem->ns != NULL) && + xmlStrEqual(elem->ns->prefix, ns->prefix) && + xmlStrEqual(elem->ns->href, ns->href)) + { + ns = ns->next; + continue; + } + luNs = xmlSearchNs(elem->doc, elem, ns->prefix); + if ((luNs != NULL) && (xmlStrEqual(luNs->href, ns->href))) + { + ns = ns->next; + continue; + } + } + q = xmlNewNs(elem, ns->href, ns->prefix); + if (p == NULL) { + ret = p = q; + } else if (q != NULL) { + p->next = q; + p = q; + } + ns = ns->next; + } while (ns != NULL); + return(ret); +} + +/** + * xsltShallowCopyNsNode: + * @ctxt: the XSLT transformation context + * @invocNode: responsible node in the stylesheet; used for error reports + * @insert: the target element node in the result tree + * @ns: the namespace node + * + * This is used for copying ns-nodes with xsl:copy-of and xsl:copy. + * + * Returns a new/existing ns-node, or NULL. + */ +static xmlNsPtr +xsltShallowCopyNsNode(xsltTransformContextPtr ctxt, + xmlNodePtr invocNode, + xmlNodePtr insert, + xmlNsPtr ns) +{ + /* + * TODO: Contrary to header comments, this is declared as int. + * be modified to return a node pointer, or NULL if any error + */ + xmlNsPtr tmpns; + + if ((insert == NULL) || (insert->type != XML_ELEMENT_NODE)) + return(NULL); + + if (insert->children != NULL) { + xsltTransformError(ctxt, NULL, invocNode, + "Namespace nodes must be added before " + "any child nodes are added to an element.\n"); + return(NULL); + } + /* + * BIG NOTE: Xalan-J simply overwrites any ns-decls with + * an equal prefix. We definitively won't do that. + * + * MSXML 4.0 and the .NET ignores ns-decls for which an + * equal prefix is already in use. + * + * Saxon raises an error like: + * "net.sf.saxon.xpath.DynamicError: Cannot create two namespace + * nodes with the same name". + * + * NOTE: We'll currently follow MSXML here. + * REVISIT TODO: Check if it's better to follow Saxon here. + */ + if (ns->prefix == NULL) { + /* + * If we are adding ns-nodes to an element using e.g. + * <xsl:copy-of select="/foo/namespace::*">, then we need + * to ensure that we don't incorrectly declare a default + * namespace on an element in no namespace, which otherwise + * would move the element incorrectly into a namespace, if + * the node tree is serialized. + */ + if (insert->ns == NULL) + goto occupied; + } else if ((ns->prefix[0] == 'x') && + xmlStrEqual(ns->prefix, BAD_CAST "xml")) + { + /* + * The XML namespace is built in. + */ + return(NULL); + } + + if (insert->nsDef != NULL) { + tmpns = insert->nsDef; + do { + if ((tmpns->prefix == NULL) == (ns->prefix == NULL)) { + if ((tmpns->prefix == ns->prefix) || + xmlStrEqual(tmpns->prefix, ns->prefix)) + { + /* + * Same prefix. + */ + if (xmlStrEqual(tmpns->href, ns->href)) + return(NULL); + goto occupied; + } + } + tmpns = tmpns->next; + } while (tmpns != NULL); + } + tmpns = xmlSearchNs(insert->doc, insert, ns->prefix); + if ((tmpns != NULL) && xmlStrEqual(tmpns->href, ns->href)) + return(NULL); + /* + * Declare a new namespace. + * TODO: The problem (wrt efficiency) with this xmlNewNs() is + * that it will again search the already declared namespaces + * for a duplicate :-/ + */ + return(xmlNewNs(insert, ns->href, ns->prefix)); + +occupied: + /* + * TODO: We could as well raise an error here (like Saxon does), + * or at least generate a warning. + */ + return(NULL); +} + +/** + * xsltCopyTreeInternal: + * @ctxt: the XSLT transformation context + * @invocNode: responsible node in the stylesheet; used for error reports + * @node: the element node in the source tree + * @insert: the parent in the result tree + * @isLRE: indicates if @node is a Literal Result Element + * @topElemVisited: indicates if a top-most element was already processed + * + * Make a copy of the full tree under the element node @node + * and insert it as last child of @insert + * + * NOTE: Not to be used for Literal Result Elements. + * + * Used by: + * - xsltCopyOf() + * + * Returns a pointer to the new tree, or NULL in case of error + */ +static xmlNodePtr +xsltCopyTreeInternal(xsltTransformContextPtr ctxt, + xmlNodePtr invocNode, + xmlNodePtr node, + xmlNodePtr insert, int isLRE, int topElemVisited) +{ + xmlNodePtr copy; + + if (node == NULL) + return(NULL); + switch (node->type) { + case XML_ELEMENT_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + break; + case XML_TEXT_NODE: { + int noenc = (node->name == xmlStringTextNoenc); + return(xsltCopyTextString(ctxt, insert, node->content, noenc)); + } + case XML_CDATA_SECTION_NODE: + return(xsltCopyTextString(ctxt, insert, node->content, 0)); + case XML_ATTRIBUTE_NODE: + return((xmlNodePtr) + xsltShallowCopyAttr(ctxt, invocNode, insert, (xmlAttrPtr) node)); + case XML_NAMESPACE_DECL: + return((xmlNodePtr) xsltShallowCopyNsNode(ctxt, invocNode, + insert, (xmlNsPtr) node)); + + case XML_DOCUMENT_TYPE_NODE: + case XML_DOCUMENT_FRAG_NODE: + case XML_NOTATION_NODE: + case XML_DTD_NODE: + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + case XML_ENTITY_DECL: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + return(NULL); + } + if (XSLT_IS_RES_TREE_FRAG(node)) { + if (node->children != NULL) + copy = xsltCopyTreeList(ctxt, invocNode, + node->children, insert, 0, 0); + else + copy = NULL; + return(copy); + } + copy = xmlDocCopyNode(node, insert->doc, 0); + if (copy != NULL) { + copy->doc = ctxt->output; + copy = xsltAddChild(insert, copy); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, invocNode, + "xsltCopyTreeInternal: Copying of '%s' failed.\n", node->name); + return (copy); + } + /* + * The node may have been coalesced into another text node. + */ + if (insert->last != copy) + return(insert->last); + copy->next = NULL; + + if (node->type == XML_ELEMENT_NODE) { + /* + * Copy in-scope namespace nodes. + * + * REVISIT: Since we try to reuse existing in-scope ns-decls by + * using xmlSearchNsByHref(), this will eventually change + * the prefix of an original ns-binding; thus it might + * break QNames in element/attribute content. + * OPTIMIZE TODO: If we had a xmlNsPtr * on the transformation + * context, plus a ns-lookup function, which writes directly + * to a given list, then we wouldn't need to create/free the + * nsList every time. + */ + if ((topElemVisited == 0) && + (node->parent != NULL) && + (node->parent->type != XML_DOCUMENT_NODE) && + (node->parent->type != XML_HTML_DOCUMENT_NODE)) + { + xmlNsPtr *nsList, *curns, ns; + + /* + * If this is a top-most element in a tree to be + * copied, then we need to ensure that all in-scope + * namespaces are copied over. For nodes deeper in the + * tree, it is sufficient to reconcile only the ns-decls + * (node->nsDef entries). + */ + + nsList = xmlGetNsList(node->doc, node); + if (nsList != NULL) { + curns = nsList; + do { + /* + * Search by prefix first in order to break as less + * QNames in element/attribute content as possible. + */ + ns = xmlSearchNs(insert->doc, insert, + (*curns)->prefix); + + if ((ns == NULL) || + (! xmlStrEqual(ns->href, (*curns)->href))) + { + ns = NULL; + /* + * Search by namespace name. + * REVISIT TODO: Currently disabled. + */ +#if 0 + ns = xmlSearchNsByHref(insert->doc, + insert, (*curns)->href); +#endif + } + if (ns == NULL) { + /* + * Declare a new namespace on the copied element. + */ + ns = xmlNewNs(copy, (*curns)->href, + (*curns)->prefix); + /* TODO: Handle errors */ + } + if (node->ns == *curns) { + /* + * If this was the original's namespace then set + * the generated counterpart on the copy. + */ + copy->ns = ns; + } + curns++; + } while (*curns != NULL); + xmlFree(nsList); + } + } else if (node->nsDef != NULL) { + /* + * Copy over all namespace declaration attributes. + */ + if (node->nsDef != NULL) { + if (isLRE) + xsltCopyNamespaceList(ctxt, copy, node->nsDef); + else + xsltCopyNamespaceListInternal(copy, node->nsDef); + } + } + /* + * Set the namespace. + */ + if (node->ns != NULL) { + if (copy->ns == NULL) { + /* + * This will map copy->ns to one of the newly created + * in-scope ns-decls, OR create a new ns-decl on @copy. + */ + copy->ns = xsltGetSpecialNamespace(ctxt, invocNode, + node->ns->href, node->ns->prefix, copy); + } + } else if ((insert->type == XML_ELEMENT_NODE) && + (insert->ns != NULL)) + { + /* + * "Undeclare" the default namespace on @copy with xmlns="". + */ + xsltGetSpecialNamespace(ctxt, invocNode, NULL, NULL, copy); + } + /* + * Copy attribute nodes. + */ + if (node->properties != NULL) { + xsltCopyAttrListNoOverwrite(ctxt, invocNode, + copy, node->properties); + } + if (topElemVisited == 0) + topElemVisited = 1; + } + /* + * Copy the subtree. + */ + if (node->children != NULL) { + xsltCopyTreeList(ctxt, invocNode, + node->children, copy, isLRE, topElemVisited); + } + } else { + xsltTransformError(ctxt, NULL, invocNode, + "xsltCopyTreeInternal: Copying of '%s' failed.\n", node->name); + } + return(copy); +} + +/** + * xsltCopyTree: + * @ctxt: the XSLT transformation context + * @node: the element node in the source tree + * @insert: the parent in the result tree + * @literal: indicates if @node is a Literal Result Element + * + * Make a copy of the full tree under the element node @node + * and insert it as last child of @insert + * For literal result element, some of the namespaces may not be copied + * over according to section 7.1. + * TODO: Why is this a public function? + * + * Returns a pointer to the new tree, or NULL in case of error + */ +xmlNodePtr +xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr insert, int literal) +{ + return(xsltCopyTreeInternal(ctxt, node, node, insert, literal, 0)); + +} + +/************************************************************************ + * * + * Error/fallback processing * + * * + ************************************************************************/ + +/** + * xsltApplyFallbacks: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the node generating the error + * + * Process possible xsl:fallback nodes present under @inst + * + * Returns the number of xsl:fallback element found and processed + */ +static int +xsltApplyFallbacks(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst) { + + xmlNodePtr child; + int ret = 0; + + if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || + (inst->children == NULL)) + return(0); + + child = inst->children; + while (child != NULL) { + if ((IS_XSLT_ELEM(child)) && + (xmlStrEqual(child->name, BAD_CAST "fallback"))) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "applying xsl:fallback\n"); +#endif + ret++; + xsltApplySequenceConstructor(ctxt, node, child->children, + NULL); + } + child = child->next; + } + return(ret); +} + +/************************************************************************ + * * + * Default processing * + * * + ************************************************************************/ + +/** + * xsltDefaultProcessOneNode: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @params: extra parameters passed to the template if any + * + * Process the source node with the default built-in template rule: + * <xsl:template match="*|/"> + * <xsl:apply-templates/> + * </xsl:template> + * + * and + * + * <xsl:template match="text()|@*"> + * <xsl:value-of select="."/> + * </xsl:template> + * + * Note also that namespace declarations are copied directly: + * + * the built-in template rule is the only template rule that is applied + * for namespace nodes. + */ +static void +xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node, + xsltStackElemPtr params) { + xmlNodePtr copy; + xmlNodePtr delete = NULL, cur; + int nbchild = 0, oldSize; + int childno = 0, oldPos; + xsltTemplatePtr template; + + CHECK_STOPPED; + /* + * Handling of leaves + */ + switch (node->type) { + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_ELEMENT_NODE: + break; + case XML_CDATA_SECTION_NODE: +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: copy CDATA %s\n", + node->content)); +#endif + copy = xsltCopyText(ctxt, ctxt->insert, node, 0); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, node, + "xsltDefaultProcessOneNode: cdata copy failed\n"); + } + return; + case XML_TEXT_NODE: +#ifdef WITH_XSLT_DEBUG_PROCESS + if (node->content == NULL) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: copy empty text\n")); + return; + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: copy text %s\n", + node->content)); + } +#endif + copy = xsltCopyText(ctxt, ctxt->insert, node, 0); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, node, + "xsltDefaultProcessOneNode: text copy failed\n"); + } + return; + case XML_ATTRIBUTE_NODE: + cur = node->children; + while ((cur != NULL) && (cur->type != XML_TEXT_NODE)) + cur = cur->next; + if (cur == NULL) { + xsltTransformError(ctxt, NULL, node, + "xsltDefaultProcessOneNode: no text for attribute\n"); + } else { +#ifdef WITH_XSLT_DEBUG_PROCESS + if (cur->content == NULL) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: copy empty text\n")); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: copy text %s\n", + cur->content)); + } +#endif + copy = xsltCopyText(ctxt, ctxt->insert, cur, 0); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, node, + "xsltDefaultProcessOneNode: text copy failed\n"); + } + } + return; + default: + return; + } + /* + * Handling of Elements: first pass, cleanup and counting + */ + cur = node->children; + while (cur != NULL) { + switch (cur->type) { + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_ELEMENT_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + nbchild++; + break; + case XML_DTD_NODE: + /* Unlink the DTD, it's still reachable using doc->intSubset */ + if (cur->next != NULL) + cur->next->prev = cur->prev; + if (cur->prev != NULL) + cur->prev->next = cur->next; + break; + default: +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: skipping node type %d\n", + cur->type)); +#endif + delete = cur; + } + cur = cur->next; + if (delete != NULL) { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: removing ignorable blank node\n")); +#endif + xmlUnlinkNode(delete); + xmlFreeNode(delete); + delete = NULL; + } + } + if (delete != NULL) { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: removing ignorable blank node\n")); +#endif + xmlUnlinkNode(delete); + xmlFreeNode(delete); + delete = NULL; + } + + /* + * Handling of Elements: second pass, actual processing + * + * Note that params are passed to the next template. This matches + * XSLT 2.0 behavior but doesn't conform to XSLT 1.0. + */ + oldSize = ctxt->xpathCtxt->contextSize; + oldPos = ctxt->xpathCtxt->proximityPosition; + cur = node->children; + while (cur != NULL) { + childno++; + switch (cur->type) { + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_ELEMENT_NODE: + ctxt->xpathCtxt->contextSize = nbchild; + ctxt->xpathCtxt->proximityPosition = childno; + xsltProcessOneNode(ctxt, cur, params); + break; + case XML_CDATA_SECTION_NODE: + template = xsltGetTemplate(ctxt, cur, NULL); + if (template) { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: applying template for CDATA %s\n", + cur->content)); +#endif + /* + * Instantiate the xsl:template. + */ + xsltApplyXSLTTemplate(ctxt, cur, template->content, + template, params); + } else /* if (ctxt->mode == NULL) */ { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: copy CDATA %s\n", + cur->content)); +#endif + copy = xsltCopyText(ctxt, ctxt->insert, cur, 0); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, cur, + "xsltDefaultProcessOneNode: cdata copy failed\n"); + } + } + break; + case XML_TEXT_NODE: + template = xsltGetTemplate(ctxt, cur, NULL); + if (template) { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: applying template for text %s\n", + cur->content)); +#endif + ctxt->xpathCtxt->contextSize = nbchild; + ctxt->xpathCtxt->proximityPosition = childno; + /* + * Instantiate the xsl:template. + */ + xsltApplyXSLTTemplate(ctxt, cur, template->content, + template, params); + } else /* if (ctxt->mode == NULL) */ { +#ifdef WITH_XSLT_DEBUG_PROCESS + if (cur->content == NULL) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: copy empty text\n")); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: copy text %s\n", + cur->content)); + } +#endif + copy = xsltCopyText(ctxt, ctxt->insert, cur, 0); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, cur, + "xsltDefaultProcessOneNode: text copy failed\n"); + } + } + break; + case XML_PI_NODE: + case XML_COMMENT_NODE: + template = xsltGetTemplate(ctxt, cur, NULL); + if (template) { +#ifdef WITH_XSLT_DEBUG_PROCESS + if (cur->type == XML_PI_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: template found for PI %s\n", + cur->name)); + } else if (cur->type == XML_COMMENT_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: template found for comment\n")); + } +#endif + ctxt->xpathCtxt->contextSize = nbchild; + ctxt->xpathCtxt->proximityPosition = childno; + /* + * Instantiate the xsl:template. + */ + xsltApplyXSLTTemplate(ctxt, cur, template->content, + template, params); + } + break; + default: + break; + } + cur = cur->next; + } + ctxt->xpathCtxt->contextSize = oldSize; + ctxt->xpathCtxt->proximityPosition = oldPos; +} + +/** + * xsltProcessOneNode: + * @ctxt: a XSLT process context + * @contextNode: the "current node" in the source tree + * @withParams: extra parameters (e.g. xsl:with-param) passed to the + * template if any + * + * Process the source node. + */ +void +xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, + xsltStackElemPtr withParams) +{ + xsltTemplatePtr templ; + xmlNodePtr oldNode; + + templ = xsltGetTemplate(ctxt, contextNode, NULL); + /* + * If no template is found, apply the default rule. + */ + if (templ == NULL) { +#ifdef WITH_XSLT_DEBUG_PROCESS + if (contextNode->type == XML_DOCUMENT_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessOneNode: no template found for /\n")); + } else if (contextNode->type == XML_CDATA_SECTION_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessOneNode: no template found for CDATA\n")); + } else if (contextNode->type == XML_ATTRIBUTE_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessOneNode: no template found for attribute %s\n", + ((xmlAttrPtr) contextNode)->name)); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessOneNode: no template found for %s\n", contextNode->name)); + } +#endif + oldNode = ctxt->node; + ctxt->node = contextNode; + xsltDefaultProcessOneNode(ctxt, contextNode, withParams); + ctxt->node = oldNode; + return; + } + + if (contextNode->type == XML_ATTRIBUTE_NODE) { + xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule; + /* + * Set the "current template rule". + */ + ctxt->currentTemplateRule = templ; + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessOneNode: applying template '%s' for attribute %s\n", + templ->match, contextNode->name)); +#endif + xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams); + + ctxt->currentTemplateRule = oldCurTempRule; + } else { + xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule; + /* + * Set the "current template rule". + */ + ctxt->currentTemplateRule = templ; + +#ifdef WITH_XSLT_DEBUG_PROCESS + if (contextNode->type == XML_DOCUMENT_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessOneNode: applying template '%s' for /\n", + templ->match)); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessOneNode: applying template '%s' for %s\n", + templ->match, contextNode->name)); + } +#endif + xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams); + + ctxt->currentTemplateRule = oldCurTempRule; + } +} + +static xmlNodePtr +xsltDebuggerStartSequenceConstructor(xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, + xmlNodePtr list, + xsltTemplatePtr templ, + int *addCallResult) +{ + xmlNodePtr debugedNode = NULL; + + if (ctxt->debugStatus != XSLT_DEBUG_NONE) { + if (templ) { + *addCallResult = xslAddCall(templ, templ->elem); + } else { + *addCallResult = xslAddCall(NULL, list); + } + switch (ctxt->debugStatus) { + case XSLT_DEBUG_RUN_RESTART: + case XSLT_DEBUG_QUIT: + if (*addCallResult) + xslDropCall(); + return(NULL); + } + if (templ) { + xslHandleDebugger(templ->elem, contextNode, templ, ctxt); + debugedNode = templ->elem; + } else if (list) { + xslHandleDebugger(list, contextNode, templ, ctxt); + debugedNode = list; + } else if (ctxt->inst) { + xslHandleDebugger(ctxt->inst, contextNode, templ, ctxt); + debugedNode = ctxt->inst; + } + } + return(debugedNode); +} + +/** + * xsltLocalVariablePush: + * @ctxt: the transformation context + * @variable: variable to be pushed to the variable stack + * @level: new value for variable's level + * + * Places the variable onto the local variable stack + * + * Returns: 0 for success, -1 for any error + * **NOTE:** + * This is an internal routine and should not be called by users! + */ +int +xsltLocalVariablePush(xsltTransformContextPtr ctxt, + xsltStackElemPtr variable, + int level) +{ + if (ctxt->varsMax == 0) { + ctxt->varsMax = 10; + ctxt->varsTab = + (xsltStackElemPtr *) xmlMalloc(ctxt->varsMax * + sizeof(ctxt->varsTab[0])); + if (ctxt->varsTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); + return (-1); + } + } + if (ctxt->varsNr >= ctxt->varsMax) { + ctxt->varsMax *= 2; + ctxt->varsTab = + (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab, + ctxt->varsMax * + sizeof(ctxt->varsTab[0])); + if (ctxt->varsTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); + return (-1); + } + } + ctxt->varsTab[ctxt->varsNr++] = variable; + ctxt->vars = variable; + variable->level = level; + return(0); +} + +/** + * xsltReleaseLocalRVTs: + * + * Fragments which are results of extension instructions + * are preserved; all other fragments are freed/cached. + */ +static void +xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt, xmlDocPtr base) +{ + xmlDocPtr cur = ctxt->localRVT, tmp; + + if (cur == base) + return; + if (cur->prev != NULL) + xsltTransformError(ctxt, NULL, NULL, "localRVT not head of list\n"); + + do { + tmp = cur; + cur = (xmlDocPtr) cur->next; + if (tmp->psvi == XSLT_RVT_LOCAL) { + xsltReleaseRVT(ctxt, tmp); + } else if (tmp->psvi == XSLT_RVT_GLOBAL) { + xsltRegisterPersistRVT(ctxt, tmp); + } else if (tmp->psvi != XSLT_RVT_FUNC_RESULT) { + xmlGenericError(xmlGenericErrorContext, + "xsltReleaseLocalRVTs: Unexpected RVT flag %p\n", + tmp->psvi); + } + } while (cur != base); + + if (base != NULL) + base->prev = NULL; + ctxt->localRVT = base; +} + +/** + * xsltApplySequenceConstructor: + * @ctxt: a XSLT process context + * @contextNode: the "current node" in the source tree + * @list: the nodes of a sequence constructor; + * (plus leading xsl:param elements) + * @templ: the compiled xsl:template (optional) + * + * Processes a sequence constructor. + * + * NOTE: ctxt->currentTemplateRule was introduced to reflect the + * semantics of "current template rule". I.e. the field ctxt->templ + * is not intended to reflect this, thus always pushed onto the + * template stack. + */ +static void +xsltApplySequenceConstructor(xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, xmlNodePtr list, + xsltTemplatePtr templ) +{ + xmlNodePtr oldInsert, oldInst, oldCurInst, oldContextNode; + xmlNodePtr cur, insert, copy = NULL; + int level = 0, oldVarsNr; + xmlDocPtr oldLocalFragmentTop; + +#ifdef XSLT_REFACTORED + xsltStylePreCompPtr info; +#endif + +#ifdef WITH_DEBUGGER + int addCallResult = 0; + xmlNodePtr debuggedNode = NULL; +#endif + + if (ctxt == NULL) + return; + +#ifdef WITH_DEBUGGER + if (ctxt->debugStatus != XSLT_DEBUG_NONE) { + debuggedNode = + xsltDebuggerStartSequenceConstructor(ctxt, contextNode, + list, templ, &addCallResult); + if (debuggedNode == NULL) + return; + } +#endif + + if (list == NULL) + return; + CHECK_STOPPED; + + /* + * Check for infinite recursion: stop if the maximum of nested templates + * is excceeded. Adjust xsltMaxDepth if you need more. + */ + if (ctxt->depth >= ctxt->maxTemplateDepth) { + xsltTransformError(ctxt, NULL, list, + "xsltApplySequenceConstructor: A potential infinite template " + "recursion was detected.\n" + "You can adjust xsltMaxDepth (--maxdepth) in order to " + "raise the maximum number of nested template calls and " + "variables/params (currently set to %d).\n", + ctxt->maxTemplateDepth); + xsltDebug(ctxt, contextNode, list, NULL); + ctxt->state = XSLT_STATE_STOPPED; + return; + } + ctxt->depth++; + + oldLocalFragmentTop = ctxt->localRVT; + oldInsert = insert = ctxt->insert; + oldInst = oldCurInst = ctxt->inst; + oldContextNode = ctxt->node; + /* + * Save current number of variables on the stack; new vars are popped when + * exiting. + */ + oldVarsNr = ctxt->varsNr; + /* + * Process the sequence constructor. + */ + cur = list; + while (cur != NULL) { + ctxt->inst = cur; + +#ifdef WITH_DEBUGGER + switch (ctxt->debugStatus) { + case XSLT_DEBUG_RUN_RESTART: + case XSLT_DEBUG_QUIT: + break; + + } +#endif + /* + * Test; we must have a valid insertion point. + */ + if (insert == NULL) { + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: insert == NULL !\n")); +#endif + goto error; + } + +#ifdef WITH_DEBUGGER + if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (debuggedNode != cur)) + xslHandleDebugger(cur, contextNode, templ, ctxt); +#endif + +#ifdef XSLT_REFACTORED + if (cur->type == XML_ELEMENT_NODE) { + info = (xsltStylePreCompPtr) cur->psvi; + /* + * We expect a compiled representation on: + * 1) XSLT instructions of this XSLT version (1.0) + * (with a few exceptions) + * 2) Literal result elements + * 3) Extension instructions + * 4) XSLT instructions of future XSLT versions + * (forwards-compatible mode). + */ + if (info == NULL) { + /* + * Handle the rare cases where we don't expect a compiled + * representation on an XSLT element. + */ + if (IS_XSLT_ELEM_FAST(cur) && IS_XSLT_NAME(cur, "message")) { + xsltMessage(ctxt, contextNode, cur); + goto skip_children; + } + /* + * Something really went wrong: + */ + xsltTransformError(ctxt, NULL, cur, + "Internal error in xsltApplySequenceConstructor(): " + "The element '%s' in the stylesheet has no compiled " + "representation.\n", + cur->name); + goto skip_children; + } + + if (info->type == XSLT_FUNC_LITERAL_RESULT_ELEMENT) { + xsltStyleItemLRElementInfoPtr lrInfo = + (xsltStyleItemLRElementInfoPtr) info; + /* + * Literal result elements + * -------------------------------------------------------- + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, + xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: copy literal result " + "element '%s'\n", cur->name)); +#endif + /* + * Copy the raw element-node. + * OLD: if ((copy = xsltShallowCopyElem(ctxt, cur, insert)) + * == NULL) + * goto error; + */ + copy = xmlDocCopyNode(cur, insert->doc, 0); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, cur, + "Internal error in xsltApplySequenceConstructor(): " + "Failed to copy literal result element '%s'.\n", + cur->name); + goto error; + } else { + /* + * Add the element-node to the result tree. + */ + copy->doc = ctxt->output; + copy = xsltAddChild(insert, copy); + /* + * Create effective namespaces declarations. + * OLD: xsltCopyNamespaceList(ctxt, copy, cur->nsDef); + */ + if (lrInfo->effectiveNs != NULL) { + xsltEffectiveNsPtr effNs = lrInfo->effectiveNs; + xmlNsPtr ns, lastns = NULL; + + while (effNs != NULL) { + /* + * Avoid generating redundant namespace + * declarations; thus lookup if there is already + * such a ns-decl in the result. + */ + ns = xmlSearchNs(copy->doc, copy, effNs->prefix); + if ((ns != NULL) && + (xmlStrEqual(ns->href, effNs->nsName))) + { + effNs = effNs->next; + continue; + } + ns = xmlNewNs(copy, effNs->nsName, effNs->prefix); + if (ns == NULL) { + xsltTransformError(ctxt, NULL, cur, + "Internal error in " + "xsltApplySequenceConstructor(): " + "Failed to copy a namespace " + "declaration.\n"); + goto error; + } + + if (lastns == NULL) + copy->nsDef = ns; + else + lastns->next =ns; + lastns = ns; + + effNs = effNs->next; + } + + } + /* + * NOTE that we don't need to apply ns-alising: this was + * already done at compile-time. + */ + if (cur->ns != NULL) { + /* + * If there's no such ns-decl in the result tree, + * then xsltGetSpecialNamespace() will + * create a ns-decl on the copied node. + */ + copy->ns = xsltGetSpecialNamespace(ctxt, cur, + cur->ns->href, cur->ns->prefix, copy); + } else { + /* + * Undeclare the default namespace if needed. + * This can be skipped, if the result element has + * no ns-decls, in which case the result element + * obviously does not declare a default namespace; + * AND there's either no parent, or the parent + * element is in no namespace; this means there's no + * default namespace is scope to care about. + * + * REVISIT: This might result in massive + * generation of ns-decls if nodes in a default + * namespaces are mixed with nodes in no namespace. + * + */ + if (copy->nsDef || + ((insert != NULL) && + (insert->type == XML_ELEMENT_NODE) && + (insert->ns != NULL))) + { + xsltGetSpecialNamespace(ctxt, cur, + NULL, NULL, copy); + } + } + } + /* + * SPEC XSLT 2.0 "Each attribute of the literal result + * element, other than an attribute in the XSLT namespace, + * is processed to produce an attribute for the element in + * the result tree." + * NOTE: See bug #341325. + */ + if (cur->properties != NULL) { + xsltAttrListTemplateProcess(ctxt, copy, cur->properties); + } + } else if (IS_XSLT_ELEM_FAST(cur)) { + /* + * XSLT instructions + * -------------------------------------------------------- + */ + if (info->type == XSLT_FUNC_UNKOWN_FORWARDS_COMPAT) { + /* + * We hit an unknown XSLT element. + * Try to apply one of the fallback cases. + */ + ctxt->insert = insert; + if (!xsltApplyFallbacks(ctxt, contextNode, cur)) { + xsltTransformError(ctxt, NULL, cur, + "The is no fallback behaviour defined for " + "the unknown XSLT element '%s'.\n", + cur->name); + } + ctxt->insert = oldInsert; + } else if (info->func != NULL) { + /* + * Execute the XSLT instruction. + */ + ctxt->insert = insert; + + info->func(ctxt, contextNode, cur, + (xsltElemPreCompPtr) info); + + /* + * Cleanup temporary tree fragments. + */ + if (oldLocalFragmentTop != ctxt->localRVT) + xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); + + ctxt->insert = oldInsert; + } else if (info->type == XSLT_FUNC_VARIABLE) { + xsltStackElemPtr tmpvar = ctxt->vars; + + xsltParseStylesheetVariable(ctxt, cur); + + if (tmpvar != ctxt->vars) { + /* + * TODO: Using a @tmpvar is an annoying workaround, but + * the current mechanisms do not provide any other way + * of knowing if the var was really pushed onto the + * stack. + */ + ctxt->vars->level = level; + } + } else if (info->type == XSLT_FUNC_MESSAGE) { + /* + * TODO: Won't be hit, since we don't compile xsl:message. + */ + xsltMessage(ctxt, contextNode, cur); + } else { + xsltTransformError(ctxt, NULL, cur, + "Unexpected XSLT element '%s'.\n", cur->name); + } + goto skip_children; + + } else { + xsltTransformFunction func; + /* + * Extension intructions (elements) + * -------------------------------------------------------- + */ + if (cur->psvi == xsltExtMarker) { + /* + * The xsltExtMarker was set during the compilation + * of extension instructions if there was no registered + * handler for this specific extension function at + * compile-time. + * Libxslt will now lookup if a handler is + * registered in the context of this transformation. + */ + func = (xsltTransformFunction) + xsltExtElementLookup(ctxt, cur->name, cur->ns->href); + } else + func = ((xsltElemPreCompPtr) cur->psvi)->func; + + if (func == NULL) { + /* + * No handler available. + * Try to execute fallback behaviour via xsl:fallback. + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, + xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: unknown extension %s\n", + cur->name)); +#endif + ctxt->insert = insert; + if (!xsltApplyFallbacks(ctxt, contextNode, cur)) { + xsltTransformError(ctxt, NULL, cur, + "Unknown extension instruction '{%s}%s'.\n", + cur->ns->href, cur->name); + } + ctxt->insert = oldInsert; + } else { + /* + * Execute the handler-callback. + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: extension construct %s\n", + cur->name)); +#endif + ctxt->insert = insert; + + func(ctxt, contextNode, cur, cur->psvi); + + /* + * Cleanup temporary tree fragments. + */ + if (oldLocalFragmentTop != ctxt->localRVT) + xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); + + ctxt->insert = oldInsert; + } + goto skip_children; + } + + } else if (XSLT_IS_TEXT_NODE(cur)) { + /* + * Text + * ------------------------------------------------------------ + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + if (cur->name == xmlStringTextNoenc) { + XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, + xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: copy unescaped text '%s'\n", + cur->content)); + } else { + XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, + xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: copy text '%s'\n", + cur->content)); + } +#endif + if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL) + goto error; + } + +#else /* XSLT_REFACTORED */ + + if (IS_XSLT_ELEM(cur)) { + /* + * This is an XSLT node + */ + xsltStylePreCompPtr info = (xsltStylePreCompPtr) cur->psvi; + + if (info == NULL) { + if (IS_XSLT_NAME(cur, "message")) { + xsltMessage(ctxt, contextNode, cur); + } else { + /* + * That's an error try to apply one of the fallback cases + */ + ctxt->insert = insert; + if (!xsltApplyFallbacks(ctxt, contextNode, cur)) { + xsltGenericError(xsltGenericErrorContext, + "xsltApplySequenceConstructor: %s was not compiled\n", + cur->name); + } + ctxt->insert = oldInsert; + } + goto skip_children; + } + + if (info->func != NULL) { + oldCurInst = ctxt->inst; + ctxt->inst = cur; + ctxt->insert = insert; + + info->func(ctxt, contextNode, cur, (xsltElemPreCompPtr) info); + + /* + * Cleanup temporary tree fragments. + */ + if (oldLocalFragmentTop != ctxt->localRVT) + xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); + + ctxt->insert = oldInsert; + ctxt->inst = oldCurInst; + goto skip_children; + } + + if (IS_XSLT_NAME(cur, "variable")) { + xsltStackElemPtr tmpvar = ctxt->vars; + + oldCurInst = ctxt->inst; + ctxt->inst = cur; + + xsltParseStylesheetVariable(ctxt, cur); + + ctxt->inst = oldCurInst; + + if (tmpvar != ctxt->vars) { + /* + * TODO: Using a @tmpvar is an annoying workaround, but + * the current mechanisms do not provide any other way + * of knowing if the var was really pushed onto the + * stack. + */ + ctxt->vars->level = level; + } + } else if (IS_XSLT_NAME(cur, "message")) { + xsltMessage(ctxt, contextNode, cur); + } else { + xsltTransformError(ctxt, NULL, cur, + "Unexpected XSLT element '%s'.\n", cur->name); + } + goto skip_children; + } else if ((cur->type == XML_TEXT_NODE) || + (cur->type == XML_CDATA_SECTION_NODE)) { + + /* + * This text comes from the stylesheet + * For stylesheets, the set of whitespace-preserving + * element names consists of just xsl:text. + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + if (cur->type == XML_CDATA_SECTION_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: copy CDATA text %s\n", + cur->content)); + } else if (cur->name == xmlStringTextNoenc) { + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: copy unescaped text %s\n", + cur->content)); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: copy text %s\n", + cur->content)); + } +#endif + if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL) + goto error; + } else if ((cur->type == XML_ELEMENT_NODE) && + (cur->ns != NULL) && (cur->psvi != NULL)) { + xsltTransformFunction function; + + oldCurInst = ctxt->inst; + ctxt->inst = cur; + /* + * Flagged as an extension element + */ + if (cur->psvi == xsltExtMarker) + function = (xsltTransformFunction) + xsltExtElementLookup(ctxt, cur->name, cur->ns->href); + else + function = ((xsltElemPreCompPtr) cur->psvi)->func; + + if (function == NULL) { + xmlNodePtr child; + int found = 0; + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: unknown extension %s\n", + cur->name)); +#endif + /* + * Search if there are fallbacks + */ + child = cur->children; + while (child != NULL) { + if ((IS_XSLT_ELEM(child)) && + (IS_XSLT_NAME(child, "fallback"))) + { + found = 1; + xsltApplySequenceConstructor(ctxt, contextNode, + child->children, NULL); + } + child = child->next; + } + + if (!found) { + xsltTransformError(ctxt, NULL, cur, + "xsltApplySequenceConstructor: failed to find extension %s\n", + cur->name); + } + } else { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: extension construct %s\n", + cur->name)); +#endif + + ctxt->insert = insert; + + function(ctxt, contextNode, cur, cur->psvi); + /* + * Cleanup temporary tree fragments. + */ + if (oldLocalFragmentTop != ctxt->localRVT) + xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); + + ctxt->insert = oldInsert; + + } + ctxt->inst = oldCurInst; + goto skip_children; + } else if (cur->type == XML_ELEMENT_NODE) { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: copy node %s\n", + cur->name)); +#endif + oldCurInst = ctxt->inst; + ctxt->inst = cur; + + if ((copy = xsltShallowCopyElem(ctxt, cur, insert, 1)) == NULL) + goto error; + /* + * Add extra namespaces inherited from the current template + * if we are in the first level children and this is a + * "real" template. + */ + if ((templ != NULL) && (oldInsert == insert) && + (ctxt->templ != NULL) && (ctxt->templ->inheritedNs != NULL)) { + int i; + xmlNsPtr ns, ret; + + for (i = 0; i < ctxt->templ->inheritedNsNr; i++) { + const xmlChar *URI = NULL; + xsltStylesheetPtr style; + ns = ctxt->templ->inheritedNs[i]; + + /* Note that the XSLT namespace was already excluded + * in xsltGetInheritedNsList(). + */ +#if 0 + if (xmlStrEqual(ns->href, XSLT_NAMESPACE)) + continue; +#endif + style = ctxt->style; + while (style != NULL) { + if (style->nsAliases != NULL) + URI = (const xmlChar *) + xmlHashLookup(style->nsAliases, ns->href); + if (URI != NULL) + break; + + style = xsltNextImport(style); + } + if (URI == UNDEFINED_DEFAULT_NS) + continue; + if (URI == NULL) + URI = ns->href; + /* + * TODO: The following will still be buggy for the + * non-refactored code. + */ + ret = xmlSearchNs(copy->doc, copy, ns->prefix); + if ((ret == NULL) || (!xmlStrEqual(ret->href, URI))) + { + xmlNewNs(copy, URI, ns->prefix); + } + } + if (copy->ns != NULL) { + /* + * Fix the node namespace if needed + */ + copy->ns = xsltGetNamespace(ctxt, cur, copy->ns, copy); + } + } + /* + * all the attributes are directly inherited + */ + if (cur->properties != NULL) { + xsltAttrListTemplateProcess(ctxt, copy, cur->properties); + } + ctxt->inst = oldCurInst; + } +#endif /* else of XSLT_REFACTORED */ + + /* + * Descend into content in document order. + */ + if (cur->children != NULL) { + if (cur->children->type != XML_ENTITY_DECL) { + cur = cur->children; + level++; + if (copy != NULL) + insert = copy; + continue; + } + } + +skip_children: + /* + * If xslt:message was just processed, we might have hit a + * terminate='yes'; if so, then break the loop and clean up. + * TODO: Do we need to check this also before trying to descend + * into the content? + */ + if (ctxt->state == XSLT_STATE_STOPPED) + break; + if (cur->next != NULL) { + cur = cur->next; + continue; + } + + do { + cur = cur->parent; + level--; + /* + * Pop variables/params (xsl:variable and xsl:param). + */ + if ((ctxt->varsNr > oldVarsNr) && (ctxt->vars->level > level)) { + xsltLocalVariablePop(ctxt, oldVarsNr, level); + } + + insert = insert->parent; + if (cur == NULL) + break; + if (cur == list->parent) { + cur = NULL; + break; + } + if (cur->next != NULL) { + cur = cur->next; + break; + } + } while (cur != NULL); + } + +error: + /* + * In case of errors: pop remaining variables. + */ + if (ctxt->varsNr > oldVarsNr) + xsltLocalVariablePop(ctxt, oldVarsNr, -1); + + ctxt->node = oldContextNode; + ctxt->inst = oldInst; + ctxt->insert = oldInsert; + + ctxt->depth--; + +#ifdef WITH_DEBUGGER + if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) { + xslDropCall(); + } +#endif +} + +/* +* xsltApplyXSLTTemplate: +* @ctxt: a XSLT transformation context +* @contextNode: the node in the source tree. +* @list: the nodes of a sequence constructor; +* (plus leading xsl:param elements) +* @templ: the compiled xsl:template declaration; +* NULL if a sequence constructor +* @withParams: a set of caller-parameters (xsl:with-param) or NULL +* +* Called by: +* - xsltApplyImports() +* - xsltCallTemplate() +* - xsltDefaultProcessOneNode() +* - xsltProcessOneNode() +*/ +static void +xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, + xmlNodePtr list, + xsltTemplatePtr templ, + xsltStackElemPtr withParams) +{ + int oldVarsBase = 0; + long start = 0; + xmlNodePtr cur; + xsltStackElemPtr tmpParam = NULL; + xmlDocPtr oldUserFragmentTop; + +#ifdef XSLT_REFACTORED + xsltStyleItemParamPtr iparam; +#else + xsltStylePreCompPtr iparam; +#endif + +#ifdef WITH_DEBUGGER + int addCallResult = 0; +#endif + + if (ctxt == NULL) + return; + if (templ == NULL) { + xsltTransformError(ctxt, NULL, list, + "xsltApplyXSLTTemplate: Bad arguments; @templ is mandatory.\n"); + return; + } + +#ifdef WITH_DEBUGGER + if (ctxt->debugStatus != XSLT_DEBUG_NONE) { + if (xsltDebuggerStartSequenceConstructor(ctxt, contextNode, + list, templ, &addCallResult) == NULL) + return; + } +#endif + + if (list == NULL) + return; + CHECK_STOPPED; + + if (ctxt->varsNr >= ctxt->maxTemplateVars) + { + xsltTransformError(ctxt, NULL, list, + "xsltApplyXSLTTemplate: A potential infinite template recursion " + "was detected.\n" + "You can adjust maxTemplateVars (--maxvars) in order to " + "raise the maximum number of variables/params (currently set to %d).\n", + ctxt->maxTemplateVars); + xsltDebug(ctxt, contextNode, list, NULL); + ctxt->state = XSLT_STATE_STOPPED; + return; + } + + oldUserFragmentTop = ctxt->tmpRVT; + ctxt->tmpRVT = NULL; + + /* + * Initiate a distinct scope of local params/variables. + */ + oldVarsBase = ctxt->varsBase; + ctxt->varsBase = ctxt->varsNr; + + ctxt->node = contextNode; + if (ctxt->profile) { + templ->nbCalls++; + start = xsltTimestamp(); + profPush(ctxt, 0); + profCallgraphAdd(templ, ctxt->templ); + } + /* + * Push the xsl:template declaration onto the stack. + */ + templPush(ctxt, templ); + +#ifdef WITH_XSLT_DEBUG_PROCESS + if (templ->name != NULL) + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "applying xsl:template '%s'\n", templ->name)); +#endif + /* + * Process xsl:param instructions and skip those elements for + * further processing. + */ + cur = list; + do { + if (cur->type == XML_TEXT_NODE) { + cur = cur->next; + continue; + } + if ((cur->type != XML_ELEMENT_NODE) || + (cur->name[0] != 'p') || + (cur->psvi == NULL) || + (! xmlStrEqual(cur->name, BAD_CAST "param")) || + (! IS_XSLT_ELEM(cur))) + { + break; + } + + list = cur->next; + +#ifdef XSLT_REFACTORED + iparam = (xsltStyleItemParamPtr) cur->psvi; +#else + iparam = (xsltStylePreCompPtr) cur->psvi; +#endif + + /* + * Substitute xsl:param for a given xsl:with-param. + * Since the XPath expression will reference the params/vars + * by index, we need to slot the xsl:with-params in the + * order of encountered xsl:params to keep the sequence of + * params/variables in the stack exactly as it was at + * compile time, + */ + tmpParam = NULL; + if (withParams) { + tmpParam = withParams; + do { + if ((tmpParam->name == (iparam->name)) && + (tmpParam->nameURI == (iparam->ns))) + { + /* + * Push the caller-parameter. + */ + xsltLocalVariablePush(ctxt, tmpParam, -1); + break; + } + tmpParam = tmpParam->next; + } while (tmpParam != NULL); + } + /* + * Push the xsl:param. + */ + if (tmpParam == NULL) { + /* + * Note that we must assume that the added parameter + * has a @depth of 0. + */ + xsltParseStylesheetParam(ctxt, cur); + } + cur = cur->next; + } while (cur != NULL); + /* + * Process the sequence constructor. + */ + xsltApplySequenceConstructor(ctxt, contextNode, list, templ); + + /* + * Remove remaining xsl:param and xsl:with-param items from + * the stack. Don't free xsl:with-param items. + */ + if (ctxt->varsNr > ctxt->varsBase) + xsltTemplateParamsCleanup(ctxt); + ctxt->varsBase = oldVarsBase; + + /* + * Release user-created fragments stored in the scope + * of xsl:template. Note that this mechanism is deprecated: + * user code should now use xsltRegisterLocalRVT() instead + * of the obsolete xsltRegisterTmpRVT(). + */ + if (ctxt->tmpRVT) { + xmlDocPtr curdoc = ctxt->tmpRVT, tmp; + + while (curdoc != NULL) { + tmp = curdoc; + curdoc = (xmlDocPtr) curdoc->next; + xsltReleaseRVT(ctxt, tmp); + } + } + ctxt->tmpRVT = oldUserFragmentTop; + + /* + * Pop the xsl:template declaration from the stack. + */ + templPop(ctxt); + if (ctxt->profile) { + long spent, child, total, end; + + end = xsltTimestamp(); + child = profPop(ctxt); + total = end - start; + spent = total - child; + if (spent <= 0) { + /* + * Not possible unless the original calibration failed + * we can try to correct it on the fly. + */ + xsltCalibrateAdjust(spent); + spent = 0; + } + + templ->time += spent; + if (ctxt->profNr > 0) + ctxt->profTab[ctxt->profNr - 1] += total; + } + +#ifdef WITH_DEBUGGER + if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) { + xslDropCall(); + } +#endif +} + + +/** + * xsltApplyOneTemplate: + * @ctxt: a XSLT process context + * @contextNode: the node in the source tree. + * @list: the nodes of a sequence constructor + * @templ: not used + * @params: a set of parameters (xsl:param) or NULL + * + * Processes a sequence constructor on the current node in the source tree. + * + * @params are the already computed variable stack items; this function + * pushes them on the variable stack, and pops them before exiting; it's + * left to the caller to free or reuse @params afterwards. The initial + * states of the variable stack will always be restored before this + * function exits. + * NOTE that this does *not* initiate a new distinct variable scope; i.e. + * variables already on the stack are visible to the process. The caller's + * side needs to start a new variable scope if needed (e.g. in exsl:function). + * + * @templ is obsolete and not used anymore (e.g. <exslt:function> does not + * provide a @templ); a non-NULL @templ might raise an error in the future. + * + * BIG NOTE: This function is not intended to process the content of an + * xsl:template; it does not expect xsl:param instructions in @list and + * will report errors if found. + * + * Called by: + * - xsltEvalVariable() (variables.c) + * - exsltFuncFunctionFunction() (libexsl/functions.c) + */ +void +xsltApplyOneTemplate(xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, + xmlNodePtr list, + xsltTemplatePtr templ ATTRIBUTE_UNUSED, + xsltStackElemPtr params) +{ + if ((ctxt == NULL) || (list == NULL)) + return; + CHECK_STOPPED; + + if (params) { + /* + * This code should be obsolete - was previously used + * by libexslt/functions.c, but due to bug 381319 the + * logic there was changed. + */ + int oldVarsNr = ctxt->varsNr; + + /* + * Push the given xsl:param(s) onto the variable stack. + */ + while (params != NULL) { + xsltLocalVariablePush(ctxt, params, -1); + params = params->next; + } + xsltApplySequenceConstructor(ctxt, contextNode, list, templ); + /* + * Pop the given xsl:param(s) from the stack but don't free them. + */ + xsltLocalVariablePop(ctxt, oldVarsNr, -2); + } else + xsltApplySequenceConstructor(ctxt, contextNode, list, templ); +} + +/************************************************************************ + * * + * XSLT-1.1 extensions * + * * + ************************************************************************/ + +/** + * xsltDocumentElem: + * @ctxt: an XSLT processing context + * @node: The current node + * @inst: the instruction in the stylesheet + * @castedComp: precomputed information + * + * Process an EXSLT/XSLT-1.1 document element + */ +void +xsltDocumentElem(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemDocumentPtr comp = (xsltStyleItemDocumentPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xsltStylesheetPtr style = NULL; + int ret; + xmlChar *filename = NULL, *prop, *elements; + xmlChar *element, *end; + xmlDocPtr res = NULL; + xmlDocPtr oldOutput; + xmlNodePtr oldInsert, root; + const char *oldOutputFile; + xsltOutputType oldType; + xmlChar *URL = NULL; + const xmlChar *method; + const xmlChar *doctypePublic; + const xmlChar *doctypeSystem; + const xmlChar *version; + const xmlChar *encoding; + int redirect_write_append = 0; + + if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) + return; + + if (comp->filename == NULL) { + + if (xmlStrEqual(inst->name, (const xmlChar *) "output")) { + /* + * The element "output" is in the namespace XSLT_SAXON_NAMESPACE + * (http://icl.com/saxon) + * The @file is in no namespace. + */ +#ifdef WITH_XSLT_DEBUG_EXTRA + xsltGenericDebug(xsltGenericDebugContext, + "Found saxon:output extension\n"); +#endif + URL = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "file", + XSLT_SAXON_NAMESPACE); + + if (URL == NULL) + URL = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "href", + XSLT_SAXON_NAMESPACE); + } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) { +#ifdef WITH_XSLT_DEBUG_EXTRA + xsltGenericDebug(xsltGenericDebugContext, + "Found xalan:write extension\n"); +#endif + URL = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) + "select", + XSLT_XALAN_NAMESPACE); + if (URL != NULL) { + xmlXPathCompExprPtr cmp; + xmlChar *val; + + /* + * Trying to handle bug #59212 + * The value of the "select" attribute is an + * XPath expression. + * (see http://xml.apache.org/xalan-j/extensionslib.html#redirect) + */ + cmp = xmlXPathCompile(URL); + val = xsltEvalXPathString(ctxt, cmp); + xmlXPathFreeCompExpr(cmp); + xmlFree(URL); + URL = val; + } + if (URL == NULL) + URL = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) + "file", + XSLT_XALAN_NAMESPACE); + if (URL == NULL) + URL = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) + "href", + XSLT_XALAN_NAMESPACE); + } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) { + URL = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "href", + NULL); + } + + } else { + URL = xmlStrdup(comp->filename); + } + + if (URL == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsltDocumentElem: href/URI-Reference not found\n"); + return; + } + + /* + * If the computation failed, it's likely that the URL wasn't escaped + */ + filename = xmlBuildURI(URL, (const xmlChar *) ctxt->outputFile); + if (filename == NULL) { + xmlChar *escURL; + + escURL=xmlURIEscapeStr(URL, BAD_CAST ":/.?,"); + if (escURL != NULL) { + filename = xmlBuildURI(escURL, (const xmlChar *) ctxt->outputFile); + xmlFree(escURL); + } + } + + if (filename == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsltDocumentElem: URL computation failed for %s\n", + URL); + xmlFree(URL); + return; + } + + /* + * Security checking: can we write to this resource + */ + if (ctxt->sec != NULL) { + ret = xsltCheckWrite(ctxt->sec, ctxt, filename); + if (ret == 0) { + xsltTransformError(ctxt, NULL, inst, + "xsltDocumentElem: write rights for %s denied\n", + filename); + xmlFree(URL); + xmlFree(filename); + return; + } + } + + oldOutputFile = ctxt->outputFile; + oldOutput = ctxt->output; + oldInsert = ctxt->insert; + oldType = ctxt->type; + ctxt->outputFile = (const char *) filename; + + style = xsltNewStylesheet(); + if (style == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsltDocumentElem: out of memory\n"); + goto error; + } + + /* + * Version described in 1.1 draft allows full parameterization + * of the output. + */ + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "version", + NULL); + if (prop != NULL) { + if (style->version != NULL) + xmlFree(style->version); + style->version = prop; + } + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "encoding", + NULL); + if (prop != NULL) { + if (style->encoding != NULL) + xmlFree(style->encoding); + style->encoding = prop; + } + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "method", + NULL); + if (prop != NULL) { + const xmlChar *URI; + + if (style->method != NULL) + xmlFree(style->method); + style->method = NULL; + if (style->methodURI != NULL) + xmlFree(style->methodURI); + style->methodURI = NULL; + + URI = xsltGetQNameURI(inst, &prop); + if (prop == NULL) { + if (style != NULL) style->errors++; + } else if (URI == NULL) { + if ((xmlStrEqual(prop, (const xmlChar *) "xml")) || + (xmlStrEqual(prop, (const xmlChar *) "html")) || + (xmlStrEqual(prop, (const xmlChar *) "text"))) { + style->method = prop; + } else { + xsltTransformError(ctxt, NULL, inst, + "invalid value for method: %s\n", prop); + if (style != NULL) style->warnings++; + } + } else { + style->method = prop; + style->methodURI = xmlStrdup(URI); + } + } + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) + "doctype-system", NULL); + if (prop != NULL) { + if (style->doctypeSystem != NULL) + xmlFree(style->doctypeSystem); + style->doctypeSystem = prop; + } + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) + "doctype-public", NULL); + if (prop != NULL) { + if (style->doctypePublic != NULL) + xmlFree(style->doctypePublic); + style->doctypePublic = prop; + } + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "standalone", + NULL); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *) "yes")) { + style->standalone = 1; + } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { + style->standalone = 0; + } else { + xsltTransformError(ctxt, NULL, inst, + "invalid value for standalone: %s\n", + prop); + if (style != NULL) style->warnings++; + } + xmlFree(prop); + } + + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "indent", + NULL); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *) "yes")) { + style->indent = 1; + } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { + style->indent = 0; + } else { + xsltTransformError(ctxt, NULL, inst, + "invalid value for indent: %s\n", prop); + if (style != NULL) style->warnings++; + } + xmlFree(prop); + } + + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) + "omit-xml-declaration", + NULL); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *) "yes")) { + style->omitXmlDeclaration = 1; + } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { + style->omitXmlDeclaration = 0; + } else { + xsltTransformError(ctxt, NULL, inst, + "invalid value for omit-xml-declaration: %s\n", + prop); + if (style != NULL) style->warnings++; + } + xmlFree(prop); + } + + elements = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) + "cdata-section-elements", + NULL); + if (elements != NULL) { + if (style->stripSpaces == NULL) + style->stripSpaces = xmlHashCreate(10); + if (style->stripSpaces == NULL) + return; + + element = elements; + while (*element != 0) { + while (IS_BLANK_CH(*element)) + element++; + if (*element == 0) + break; + end = element; + while ((*end != 0) && (!IS_BLANK_CH(*end))) + end++; + element = xmlStrndup(element, end - element); + if (element) { + const xmlChar *URI; + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "add cdata section output element %s\n", + element); +#endif + URI = xsltGetQNameURI(inst, &element); + + xmlHashAddEntry2(style->stripSpaces, element, URI, + (xmlChar *) "cdata"); + xmlFree(element); + } + element = end; + } + xmlFree(elements); + } + + /* + * Create a new document tree and process the element template + */ + XSLT_GET_IMPORT_PTR(method, style, method) + XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) + XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) + XSLT_GET_IMPORT_PTR(version, style, version) + XSLT_GET_IMPORT_PTR(encoding, style, encoding) + + if ((method != NULL) && + (!xmlStrEqual(method, (const xmlChar *) "xml"))) { + if (xmlStrEqual(method, (const xmlChar *) "html")) { + ctxt->type = XSLT_OUTPUT_HTML; + if (((doctypePublic != NULL) || (doctypeSystem != NULL))) + res = htmlNewDoc(doctypeSystem, doctypePublic); + else { + if (version != NULL) { +#ifdef XSLT_GENERATE_HTML_DOCTYPE + xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem); +#endif + } + res = htmlNewDocNoDtD(doctypeSystem, doctypePublic); + } + if (res == NULL) + goto error; + res->dict = ctxt->dict; + xmlDictReference(res->dict); + } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) { + xsltTransformError(ctxt, NULL, inst, + "xsltDocumentElem: unsupported method xhtml\n"); + ctxt->type = XSLT_OUTPUT_HTML; + res = htmlNewDocNoDtD(doctypeSystem, doctypePublic); + if (res == NULL) + goto error; + res->dict = ctxt->dict; + xmlDictReference(res->dict); + } else if (xmlStrEqual(method, (const xmlChar *) "text")) { + ctxt->type = XSLT_OUTPUT_TEXT; + res = xmlNewDoc(style->version); + if (res == NULL) + goto error; + res->dict = ctxt->dict; + xmlDictReference(res->dict); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing transformation dict for output\n"); +#endif + } else { + xsltTransformError(ctxt, NULL, inst, + "xsltDocumentElem: unsupported method (%s)\n", + method); + goto error; + } + } else { + ctxt->type = XSLT_OUTPUT_XML; + res = xmlNewDoc(style->version); + if (res == NULL) + goto error; + res->dict = ctxt->dict; + xmlDictReference(res->dict); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing transformation dict for output\n"); +#endif + } + res->charset = XML_CHAR_ENCODING_UTF8; + if (encoding != NULL) + res->encoding = xmlStrdup(encoding); + ctxt->output = res; + ctxt->insert = (xmlNodePtr) res; + xsltApplySequenceConstructor(ctxt, node, inst->children, NULL); + + /* + * Do some post processing work depending on the generated output + */ + root = xmlDocGetRootElement(res); + if (root != NULL) { + const xmlChar *doctype = NULL; + + if ((root->ns != NULL) && (root->ns->prefix != NULL)) + doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name); + if (doctype == NULL) + doctype = root->name; + + /* + * Apply the default selection of the method + */ + if ((method == NULL) && + (root->ns == NULL) && + (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) { + xmlNodePtr tmp; + + tmp = res->children; + while ((tmp != NULL) && (tmp != root)) { + if (tmp->type == XML_ELEMENT_NODE) + break; + if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp))) + break; + tmp = tmp->next; + } + if (tmp == root) { + ctxt->type = XSLT_OUTPUT_HTML; + res->type = XML_HTML_DOCUMENT_NODE; + if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { + res->intSubset = xmlCreateIntSubset(res, doctype, + doctypePublic, + doctypeSystem); +#ifdef XSLT_GENERATE_HTML_DOCTYPE + } else if (version != NULL) { + xsltGetHTMLIDs(version, &doctypePublic, + &doctypeSystem); + if (((doctypePublic != NULL) || (doctypeSystem != NULL))) + res->intSubset = + xmlCreateIntSubset(res, doctype, + doctypePublic, + doctypeSystem); +#endif + } + } + + } + if (ctxt->type == XSLT_OUTPUT_XML) { + XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) + XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) + if (((doctypePublic != NULL) || (doctypeSystem != NULL))) + res->intSubset = xmlCreateIntSubset(res, doctype, + doctypePublic, + doctypeSystem); + } + } + + /* + * Calls to redirect:write also take an optional attribute append. + * Attribute append="true|yes" which will attempt to simply append + * to an existing file instead of always opening a new file. The + * default behavior of always overwriting the file still happens + * if we do not specify append. + * Note that append use will forbid use of remote URI target. + */ + prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"append", + NULL); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *) "true") || + xmlStrEqual(prop, (const xmlChar *) "yes")) { + style->omitXmlDeclaration = 1; + redirect_write_append = 1; + } else + style->omitXmlDeclaration = 0; + xmlFree(prop); + } + + if (redirect_write_append) { + FILE *f; + + f = fopen((const char *) filename, "ab"); + if (f == NULL) { + ret = -1; + } else { + ret = xsltSaveResultToFile(f, res, style); + fclose(f); + } + } else { + ret = xsltSaveResultToFilename((const char *) filename, res, style, 0); + } + if (ret < 0) { + xsltTransformError(ctxt, NULL, inst, + "xsltDocumentElem: unable to save to %s\n", + filename); +#ifdef WITH_XSLT_DEBUG_EXTRA + } else { + xsltGenericDebug(xsltGenericDebugContext, + "Wrote %d bytes to %s\n", ret, filename); +#endif + } + + error: + ctxt->output = oldOutput; + ctxt->insert = oldInsert; + ctxt->type = oldType; + ctxt->outputFile = oldOutputFile; + if (URL != NULL) + xmlFree(URL); + if (filename != NULL) + xmlFree(filename); + if (style != NULL) + xsltFreeStylesheet(style); + if (res != NULL) + xmlFreeDoc(res); +} + +/************************************************************************ + * * + * Most of the XSLT-1.0 transformations * + * * + ************************************************************************/ + +/** + * xsltSort: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt sort node + * @comp: precomputed information + * + * function attached to xsl:sort nodes, but this should not be + * called directly + */ +void +xsltSort(xsltTransformContextPtr ctxt, + xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst, + xsltStylePreCompPtr comp) { + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:sort : compilation failed\n"); + return; + } + xsltTransformError(ctxt, NULL, inst, + "xsl:sort : improper use this should not be reached\n"); +} + +/** + * xsltCopy: + * @ctxt: an XSLT process context + * @node: the node in the source tree + * @inst: the element node of the XSLT-copy instruction + * @castedComp: computed information of the XSLT-copy instruction + * + * Execute the XSLT-copy instruction on the source node. + */ +void +xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemCopyPtr comp = (xsltStyleItemCopyPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xmlNodePtr copy, oldInsert; + + oldInsert = ctxt->insert; + if (ctxt->insert != NULL) { + switch (node->type) { + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + /* + * This text comes from the stylesheet + * For stylesheets, the set of whitespace-preserving + * element names consists of just xsl:text. + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + if (node->type == XML_CDATA_SECTION_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopy: CDATA text %s\n", node->content)); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopy: text %s\n", node->content)); + } +#endif + xsltCopyText(ctxt, ctxt->insert, node, 0); + break; + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + break; + case XML_ELEMENT_NODE: + /* + * REVISIT NOTE: The "fake" is a doc-node, not an element node. + * REMOVED: + * if (xmlStrEqual(node->name, BAD_CAST " fake node libxslt")) + * return; + */ + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopy: node %s\n", node->name)); +#endif + copy = xsltShallowCopyElem(ctxt, node, ctxt->insert, 0); + ctxt->insert = copy; + if (comp->use != NULL) { + xsltApplyAttributeSet(ctxt, node, inst, comp->use); + } + break; + case XML_ATTRIBUTE_NODE: { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopy: attribute %s\n", node->name)); +#endif + /* + * REVISIT: We could also raise an error if the parent is not + * an element node. + * OPTIMIZE TODO: Can we set the value/children of the + * attribute without an intermediate copy of the string value? + */ + xsltShallowCopyAttr(ctxt, inst, ctxt->insert, (xmlAttrPtr) node); + break; + } + case XML_PI_NODE: +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopy: PI %s\n", node->name)); +#endif + copy = xmlNewDocPI(ctxt->insert->doc, node->name, + node->content); + copy = xsltAddChild(ctxt->insert, copy); + break; + case XML_COMMENT_NODE: +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopy: comment\n")); +#endif + copy = xmlNewComment(node->content); + copy = xsltAddChild(ctxt->insert, copy); + break; + case XML_NAMESPACE_DECL: +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopy: namespace declaration\n")); +#endif + xsltShallowCopyNsNode(ctxt, inst, ctxt->insert, (xmlNsPtr)node); + break; + default: + break; + + } + } + + switch (node->type) { + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_ELEMENT_NODE: + xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children, + NULL); + break; + default: + break; + } + ctxt->insert = oldInsert; +} + +/** + * xsltText: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt text node + * @comp: precomputed information + * + * Process the xslt text node on the source node + */ +void +xsltText(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED, + xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) { + if ((inst->children != NULL) && (comp != NULL)) { + xmlNodePtr text = inst->children; + xmlNodePtr copy; + + while (text != NULL) { + if ((text->type != XML_TEXT_NODE) && + (text->type != XML_CDATA_SECTION_NODE)) { + xsltTransformError(ctxt, NULL, inst, + "xsl:text content problem\n"); + break; + } + copy = xmlNewDocText(ctxt->output, text->content); + if (text->type != XML_CDATA_SECTION_NODE) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "Disable escaping: %s\n", text->content); +#endif + copy->name = xmlStringTextNoenc; + } + copy = xsltAddChild(ctxt->insert, copy); + text = text->next; + } + } +} + +/** + * xsltElement: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt element node + * @castedComp: precomputed information + * + * Process the xslt element node on the source node + */ +void +xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) { +#ifdef XSLT_REFACTORED + xsltStyleItemElementPtr comp = (xsltStyleItemElementPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xmlChar *prop = NULL; + const xmlChar *name, *prefix = NULL, *nsName = NULL; + xmlNodePtr copy; + xmlNodePtr oldInsert; + + if (ctxt->insert == NULL) + return; + + /* + * A comp->has_name == 0 indicates that we need to skip this instruction, + * since it was evaluated to be invalid already during compilation. + */ + if (!comp->has_name) + return; + + /* + * stack and saves + */ + oldInsert = ctxt->insert; + + if (comp->name == NULL) { + /* TODO: fix attr acquisition wrt to the XSLT namespace */ + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "name", XSLT_NAMESPACE); + if (prop == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:element: The attribute 'name' is missing.\n"); + goto error; + } + if (xmlValidateQName(prop, 0)) { + xsltTransformError(ctxt, NULL, inst, + "xsl:element: The effective name '%s' is not a " + "valid QName.\n", prop); + /* we fall through to catch any further errors, if possible */ + } + name = xsltSplitQName(ctxt->dict, prop, &prefix); + xmlFree(prop); + } else { + /* + * The "name" value was static. + */ +#ifdef XSLT_REFACTORED + prefix = comp->nsPrefix; + name = comp->name; +#else + name = xsltSplitQName(ctxt->dict, comp->name, &prefix); +#endif + } + + /* + * Create the new element + */ + if (ctxt->output->dict == ctxt->dict) { + copy = xmlNewDocNodeEatName(ctxt->output, NULL, (xmlChar *)name, NULL); + } else { + copy = xmlNewDocNode(ctxt->output, NULL, (xmlChar *)name, NULL); + } + if (copy == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:element : creation of %s failed\n", name); + return; + } + copy = xsltAddChild(ctxt->insert, copy); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:element : xsltAddChild failed\n"); + return; + } + + /* + * Namespace + * --------- + */ + if (comp->has_ns) { + if (comp->ns != NULL) { + /* + * No AVT; just plain text for the namespace name. + */ + if (comp->ns[0] != 0) + nsName = comp->ns; + } else { + xmlChar *tmpNsName; + /* + * Eval the AVT. + */ + /* TODO: check attr acquisition wrt to the XSLT namespace */ + tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "namespace", XSLT_NAMESPACE); + /* + * SPEC XSLT 1.0: + * "If the string is empty, then the expanded-name of the + * attribute has a null namespace URI." + */ + if ((tmpNsName != NULL) && (tmpNsName[0] != 0)) + nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1); + xmlFree(tmpNsName); + } + + if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) { + xsltTransformError(ctxt, NULL, inst, + "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ " + "forbidden.\n"); + goto error; + } + if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) { + prefix = BAD_CAST "xml"; + } else if (xmlStrEqual(prefix, BAD_CAST "xml")) { + prefix = NULL; + } + } else { + xmlNsPtr ns; + /* + * SPEC XSLT 1.0: + * "If the namespace attribute is not present, then the QName is + * expanded into an expanded-name using the namespace declarations + * in effect for the xsl:element element, including any default + * namespace declaration. + */ + ns = xmlSearchNs(inst->doc, inst, prefix); + if (ns == NULL) { + /* + * TODO: Check this in the compilation layer in case it's a + * static value. + */ + if (prefix != NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:element: The QName '%s:%s' has no " + "namespace binding in scope in the stylesheet; " + "this is an error, since the namespace was not " + "specified by the instruction itself.\n", prefix, name); + } + } else + nsName = ns->href; + } + /* + * Find/create a matching ns-decl in the result tree. + */ + if (nsName != NULL) { + if (xmlStrEqual(prefix, BAD_CAST "xmlns")) { + /* Don't use a prefix of "xmlns" */ + xmlChar *pref = xmlStrdup(BAD_CAST "ns_1"); + + copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, copy); + + xmlFree(pref); + } else { + copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix, + copy); + } + } else if ((copy->parent != NULL) && + (copy->parent->type == XML_ELEMENT_NODE) && + (copy->parent->ns != NULL)) + { + /* + * "Undeclare" the default namespace. + */ + xsltGetSpecialNamespace(ctxt, inst, NULL, NULL, copy); + } + + ctxt->insert = copy; + + if (comp->has_use) { + if (comp->use != NULL) { + xsltApplyAttributeSet(ctxt, node, inst, comp->use); + } else { + xmlChar *attrSets = NULL; + /* + * BUG TODO: use-attribute-sets is not a value template. + * use-attribute-sets = qnames + */ + attrSets = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *)"use-attribute-sets", NULL); + if (attrSets != NULL) { + xsltApplyAttributeSet(ctxt, node, inst, attrSets); + xmlFree(attrSets); + } + } + } + /* + * Instantiate the sequence constructor. + */ + if (inst->children != NULL) + xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children, + NULL); + +error: + ctxt->insert = oldInsert; + return; +} + + +/** + * xsltComment: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt comment node + * @comp: precomputed information + * + * Process the xslt comment node on the source node + */ +void +xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) { + xmlChar *value = NULL; + xmlNodePtr commentNode; + int len; + + value = xsltEvalTemplateString(ctxt, node, inst); + /* TODO: use or generate the compiled form */ + len = xmlStrlen(value); + if (len > 0) { + if ((value[len-1] == '-') || + (xmlStrstr(value, BAD_CAST "--"))) { + xsltTransformError(ctxt, NULL, inst, + "xsl:comment : '--' or ending '-' not allowed in comment\n"); + /* fall through to try to catch further errors */ + } + } +#ifdef WITH_XSLT_DEBUG_PROCESS + if (value == NULL) { + XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext, + "xsltComment: empty\n")); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext, + "xsltComment: content %s\n", value)); + } +#endif + + commentNode = xmlNewComment(value); + commentNode = xsltAddChild(ctxt->insert, commentNode); + + if (value != NULL) + xmlFree(value); +} + +/** + * xsltProcessingInstruction: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt processing-instruction node + * @castedComp: precomputed information + * + * Process the xslt processing-instruction node on the source node + */ +void +xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) { +#ifdef XSLT_REFACTORED + xsltStyleItemPIPtr comp = (xsltStyleItemPIPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + const xmlChar *name; + xmlChar *value = NULL; + xmlNodePtr pi; + + + if (ctxt->insert == NULL) + return; + if (comp->has_name == 0) + return; + if (comp->name == NULL) { + name = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *)"name", NULL); + if (name == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:processing-instruction : name is missing\n"); + goto error; + } + } else { + name = comp->name; + } + /* TODO: check that it's both an an NCName and a PITarget. */ + + + value = xsltEvalTemplateString(ctxt, node, inst); + if (xmlStrstr(value, BAD_CAST "?>") != NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:processing-instruction: '?>' not allowed within PI content\n"); + goto error; + } +#ifdef WITH_XSLT_DEBUG_PROCESS + if (value == NULL) { + XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessingInstruction: %s empty\n", name)); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessingInstruction: %s content %s\n", name, value)); + } +#endif + + pi = xmlNewDocPI(ctxt->insert->doc, name, value); + pi = xsltAddChild(ctxt->insert, pi); + +error: + if ((name != NULL) && (name != comp->name)) + xmlFree((xmlChar *) name); + if (value != NULL) + xmlFree(value); +} + +/** + * xsltCopyOf: + * @ctxt: an XSLT transformation context + * @node: the current node in the source tree + * @inst: the element node of the XSLT copy-of instruction + * @castedComp: precomputed information of the XSLT copy-of instruction + * + * Process the XSLT copy-of instruction. + */ +void +xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) { +#ifdef XSLT_REFACTORED + xsltStyleItemCopyOfPtr comp = (xsltStyleItemCopyOfPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xmlXPathObjectPtr res = NULL; + xmlNodeSetPtr list = NULL; + int i; + + if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) + return; + if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) { + xsltTransformError(ctxt, NULL, inst, + "xsl:copy-of : compilation failed\n"); + return; + } + + /* + * SPEC XSLT 1.0: + * "The xsl:copy-of element can be used to insert a result tree + * fragment into the result tree, without first converting it to + * a string as xsl:value-of does (see [7.6.1 Generating Text with + * xsl:value-of]). The required select attribute contains an + * expression. When the result of evaluating the expression is a + * result tree fragment, the complete fragment is copied into the + * result tree. When the result is a node-set, all the nodes in the + * set are copied in document order into the result tree; copying + * an element node copies the attribute nodes, namespace nodes and + * children of the element node as well as the element node itself; + * a root node is copied by copying its children. When the result + * is neither a node-set nor a result tree fragment, the result is + * converted to a string and then inserted into the result tree, + * as with xsl:value-of. + */ + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopyOf: select %s\n", comp->select)); +#endif + + /* + * Evaluate the "select" expression. + */ + res = xsltPreCompEval(ctxt, node, comp); + + if (res != NULL) { + if (res->type == XPATH_NODESET) { + /* + * Node-set + * -------- + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopyOf: result is a node set\n")); +#endif + list = res->nodesetval; + if (list != NULL) { + xmlNodePtr cur; + /* + * The list is already sorted in document order by XPath. + * Append everything in this order under ctxt->insert. + */ + for (i = 0;i < list->nodeNr;i++) { + cur = list->nodeTab[i]; + if (cur == NULL) + continue; + if ((cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE)) + { + xsltCopyTreeList(ctxt, inst, + cur->children, ctxt->insert, 0, 0); + } else if (cur->type == XML_ATTRIBUTE_NODE) { + xsltShallowCopyAttr(ctxt, inst, + ctxt->insert, (xmlAttrPtr) cur); + } else { + xsltCopyTreeInternal(ctxt, inst, + cur, ctxt->insert, 0, 0); + } + } + } + } else if (res->type == XPATH_XSLT_TREE) { + /* + * Result tree fragment + * -------------------- + * E.g. via <xsl:variable ...><foo/></xsl:variable> + * Note that the root node of such trees is an xmlDocPtr in Libxslt. + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopyOf: result is a result tree fragment\n")); +#endif + list = res->nodesetval; + if ((list != NULL) && (list->nodeTab != NULL) && + (list->nodeTab[0] != NULL) && + (IS_XSLT_REAL_NODE(list->nodeTab[0]))) + { + xsltCopyTreeList(ctxt, inst, + list->nodeTab[0]->children, ctxt->insert, 0, 0); + } + } else { + xmlChar *value = NULL; + /* + * Convert to a string. + */ + value = xmlXPathCastToString(res); + if (value == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltCopyOf(): " + "failed to cast an XPath object to string.\n"); + ctxt->state = XSLT_STATE_STOPPED; + } else { + if (value[0] != 0) { + /* + * Append content as text node. + */ + xsltCopyTextString(ctxt, ctxt->insert, value, 0); + } + xmlFree(value); + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopyOf: result %s\n", res->stringval)); +#endif + } + } + } else { + ctxt->state = XSLT_STATE_STOPPED; + } + + if (res != NULL) + xmlXPathFreeObject(res); +} + +/** + * xsltValueOf: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt value-of node + * @castedComp: precomputed information + * + * Process the xslt value-of node on the source node + */ +void +xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemValueOfPtr comp = (xsltStyleItemValueOfPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xmlXPathObjectPtr res = NULL; + xmlChar *value = NULL; + + if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) + return; + + if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltValueOf(): " + "The XSLT 'value-of' instruction was not compiled.\n"); + return; + } + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext, + "xsltValueOf: select %s\n", comp->select)); +#endif + + res = xsltPreCompEval(ctxt, node, comp); + + /* + * Cast the XPath object to string. + */ + if (res != NULL) { + value = xmlXPathCastToString(res); + if (value == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltValueOf(): " + "failed to cast an XPath object to string.\n"); + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + if (value[0] != 0) { + xsltCopyTextString(ctxt, ctxt->insert, value, comp->noescape); + } + } else { + xsltTransformError(ctxt, NULL, inst, + "XPath evaluation returned no result.\n"); + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + +#ifdef WITH_XSLT_DEBUG_PROCESS + if (value) { + XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext, + "xsltValueOf: result '%s'\n", value)); + } +#endif + +error: + if (value != NULL) + xmlFree(value); + if (res != NULL) + xmlXPathFreeObject(res); +} + +/** + * xsltNumber: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt number node + * @castedComp: precomputed information + * + * Process the xslt number node on the source node + */ +void +xsltNumber(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemNumberPtr comp = (xsltStyleItemNumberPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xmlXPathContextPtr xpctxt; + xmlNsPtr *oldXPNamespaces; + int oldXPNsNr; + + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:number : compilation failed\n"); + return; + } + + if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) + return; + + comp->numdata.doc = inst->doc; + comp->numdata.node = inst; + + xpctxt = ctxt->xpathCtxt; + oldXPNsNr = xpctxt->nsNr; + oldXPNamespaces = xpctxt->namespaces; + +#ifdef XSLT_REFACTORED + if (comp->inScopeNs != NULL) { + xpctxt->namespaces = comp->inScopeNs->list; + xpctxt->nsNr = comp->inScopeNs->xpathNumber; + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } +#else + xpctxt->namespaces = comp->nsList; + xpctxt->nsNr = comp->nsNr; +#endif + + xsltNumberFormat(ctxt, &comp->numdata, node); + + xpctxt->nsNr = oldXPNsNr; + xpctxt->namespaces = oldXPNamespaces; +} + +/** + * xsltApplyImports: + * @ctxt: an XSLT transformation context + * @contextNode: the current node in the source tree. + * @inst: the element node of the XSLT 'apply-imports' instruction + * @comp: the compiled instruction + * + * Process the XSLT apply-imports element. + */ +void +xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, + xmlNodePtr inst, + xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) +{ + xsltTemplatePtr templ; + + if ((ctxt == NULL) || (inst == NULL)) + return; + + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltApplyImports(): " + "The XSLT 'apply-imports' instruction was not compiled.\n"); + return; + } + /* + * NOTE that ctxt->currentTemplateRule and ctxt->templ is not the + * same; the former is the "Current Template Rule" as defined by the + * XSLT spec, the latter is simply the template struct being + * currently processed. + */ + if (ctxt->currentTemplateRule == NULL) { + /* + * SPEC XSLT 2.0: + * "[ERR XTDE0560] It is a non-recoverable dynamic error if + * xsl:apply-imports or xsl:next-match is evaluated when the + * current template rule is null." + */ + xsltTransformError(ctxt, NULL, inst, + "It is an error to call 'apply-imports' " + "when there's no current template rule.\n"); + return; + } + /* + * TODO: Check if this is correct. + */ + templ = xsltGetTemplate(ctxt, contextNode, + ctxt->currentTemplateRule->style); + + if (templ != NULL) { + xsltTemplatePtr oldCurTemplRule = ctxt->currentTemplateRule; + /* + * Set the current template rule. + */ + ctxt->currentTemplateRule = templ; + /* + * URGENT TODO: Need xsl:with-param be handled somehow here? + */ + xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, + templ, NULL); + + ctxt->currentTemplateRule = oldCurTemplRule; + } + else { + /* Use built-in templates. */ + xsltDefaultProcessOneNode(ctxt, contextNode, NULL); + } +} + +/** + * xsltCallTemplate: + * @ctxt: a XSLT transformation context + * @node: the "current node" in the source tree + * @inst: the XSLT 'call-template' instruction + * @castedComp: the compiled information of the instruction + * + * Processes the XSLT call-template instruction on the source node. + */ +void +xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemCallTemplatePtr comp = + (xsltStyleItemCallTemplatePtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xsltStackElemPtr withParams = NULL; + + if (ctxt->insert == NULL) + return; + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "The XSLT 'call-template' instruction was not compiled.\n"); + return; + } + + /* + * The template must have been precomputed + */ + if (comp->templ == NULL) { + comp->templ = xsltFindTemplate(ctxt, comp->name, comp->ns); + if (comp->templ == NULL) { + if (comp->ns != NULL) { + xsltTransformError(ctxt, NULL, inst, + "The called template '{%s}%s' was not found.\n", + comp->ns, comp->name); + } else { + xsltTransformError(ctxt, NULL, inst, + "The called template '%s' was not found.\n", + comp->name); + } + return; + } + } + +#ifdef WITH_XSLT_DEBUG_PROCESS + if ((comp != NULL) && (comp->name != NULL)) + XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "call-template: name %s\n", comp->name)); +#endif + + if (inst->children) { + xmlNodePtr cur; + xsltStackElemPtr param; + + cur = inst->children; + while (cur != NULL) { +#ifdef WITH_DEBUGGER + if (ctxt->debugStatus != XSLT_DEBUG_NONE) + xslHandleDebugger(cur, node, comp->templ, ctxt); +#endif + if (ctxt->state == XSLT_STATE_STOPPED) break; + /* + * TODO: The "with-param"s could be part of the "call-template" + * structure. Avoid to "search" for params dynamically + * in the XML tree every time. + */ + if (IS_XSLT_ELEM(cur)) { + if (IS_XSLT_NAME(cur, "with-param")) { + param = xsltParseStylesheetCallerParam(ctxt, cur); + if (param != NULL) { + param->next = withParams; + withParams = param; + } + } else { + xsltGenericError(xsltGenericErrorContext, + "xsl:call-template: misplaced xsl:%s\n", cur->name); + } + } else { + xsltGenericError(xsltGenericErrorContext, + "xsl:call-template: misplaced %s element\n", cur->name); + } + cur = cur->next; + } + } + /* + * Create a new frame using the params first + */ + xsltApplyXSLTTemplate(ctxt, node, comp->templ->content, comp->templ, + withParams); + if (withParams != NULL) + xsltFreeStackElemList(withParams); + +#ifdef WITH_XSLT_DEBUG_PROCESS + if ((comp != NULL) && (comp->name != NULL)) + XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "call-template returned: name %s\n", comp->name)); +#endif +} + +/** + * xsltApplyTemplates: + * @ctxt: a XSLT transformation context + * @node: the 'current node' in the source tree + * @inst: the element node of an XSLT 'apply-templates' instruction + * @castedComp: the compiled instruction + * + * Processes the XSLT 'apply-templates' instruction on the current node. + */ +void +xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemApplyTemplatesPtr comp = + (xsltStyleItemApplyTemplatesPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + int i; + xmlNodePtr cur, delNode = NULL, oldContextNode; + xmlNodeSetPtr list = NULL, oldList; + xsltStackElemPtr withParams = NULL; + int oldXPProximityPosition, oldXPContextSize; + const xmlChar *oldMode, *oldModeURI; + xmlDocPtr oldXPDoc; + xsltDocumentPtr oldDocInfo; + xmlXPathContextPtr xpctxt; + + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:apply-templates : compilation failed\n"); + return; + } + if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) + return; + +#ifdef WITH_XSLT_DEBUG_PROCESS + if ((node != NULL) && (node->name != NULL)) + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyTemplates: node: '%s'\n", node->name)); +#endif + + xpctxt = ctxt->xpathCtxt; + /* + * Save context states. + */ + oldContextNode = ctxt->node; + oldMode = ctxt->mode; + oldModeURI = ctxt->modeURI; + oldDocInfo = ctxt->document; + oldList = ctxt->nodeList; + + /* + * The xpath context size and proximity position, as + * well as the xpath and context documents, may be changed + * so we save their initial state and will restore on exit + */ + oldXPContextSize = xpctxt->contextSize; + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPDoc = xpctxt->doc; + + /* + * Set up contexts. + */ + ctxt->mode = comp->mode; + ctxt->modeURI = comp->modeURI; + + if (comp->select != NULL) { + xmlXPathObjectPtr res = NULL; + + if (comp->comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:apply-templates : compilation failed\n"); + goto error; + } +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyTemplates: select %s\n", comp->select)); +#endif + + res = xsltPreCompEval(ctxt, node, comp); + + if (res != NULL) { + if (res->type == XPATH_NODESET) { + list = res->nodesetval; /* consume the node set */ + res->nodesetval = NULL; + } else { + xsltTransformError(ctxt, NULL, inst, + "The 'select' expression did not evaluate to a " + "node set.\n"); + ctxt->state = XSLT_STATE_STOPPED; + xmlXPathFreeObject(res); + goto error; + } + xmlXPathFreeObject(res); + /* + * Note: An xsl:apply-templates with a 'select' attribute, + * can change the current source doc. + */ + } else { + xsltTransformError(ctxt, NULL, inst, + "Failed to evaluate the 'select' expression.\n"); + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + if (list == NULL) { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyTemplates: select didn't evaluate to a node list\n")); +#endif + goto exit; + } + /* + * + * NOTE: Previously a document info (xsltDocument) was + * created and attached to the Result Tree Fragment. + * But such a document info is created on demand in + * xsltKeyFunction() (functions.c), so we need to create + * it here beforehand. + * In order to take care of potential keys we need to + * do some extra work for the case when a Result Tree Fragment + * is converted into a nodeset (e.g. exslt:node-set()) : + * We attach a "pseudo-doc" (xsltDocument) to _private. + * This xsltDocument, together with the keyset, will be freed + * when the Result Tree Fragment is freed. + * + */ +#if 0 + if ((ctxt->nbKeys > 0) && + (list->nodeNr != 0) && + (list->nodeTab[0]->doc != NULL) && + XSLT_IS_RES_TREE_FRAG(list->nodeTab[0]->doc)) + { + /* + * NOTE that it's also OK if @effectiveDocInfo will be + * set to NULL. + */ + isRTF = 1; + effectiveDocInfo = list->nodeTab[0]->doc->_private; + } +#endif + } else { + /* + * Build an XPath node set with the children + */ + list = xmlXPathNodeSetCreate(NULL); + if (list == NULL) + goto error; + if (node->type != XML_NAMESPACE_DECL) + cur = node->children; + else + cur = NULL; + while (cur != NULL) { + switch (cur->type) { + case XML_TEXT_NODE: + if ((IS_BLANK_NODE(cur)) && + (cur->parent != NULL) && + (cur->parent->type == XML_ELEMENT_NODE) && + (ctxt->style->stripSpaces != NULL)) { + const xmlChar *val; + + if (cur->parent->ns != NULL) { + val = (const xmlChar *) + xmlHashLookup2(ctxt->style->stripSpaces, + cur->parent->name, + cur->parent->ns->href); + if (val == NULL) { + val = (const xmlChar *) + xmlHashLookup2(ctxt->style->stripSpaces, + BAD_CAST "*", + cur->parent->ns->href); + } + } else { + val = (const xmlChar *) + xmlHashLookup2(ctxt->style->stripSpaces, + cur->parent->name, NULL); + } + if ((val != NULL) && + (xmlStrEqual(val, (xmlChar *) "strip"))) { + delNode = cur; + break; + } + } + /* no break on purpose */ + case XML_ELEMENT_NODE: + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + xmlXPathNodeSetAddUnique(list, cur); + break; + case XML_DTD_NODE: + /* Unlink the DTD, it's still reachable + * using doc->intSubset */ + if (cur->next != NULL) + cur->next->prev = cur->prev; + if (cur->prev != NULL) + cur->prev->next = cur->next; + break; + case XML_NAMESPACE_DECL: + break; + default: +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyTemplates: skipping cur type %d\n", + cur->type)); +#endif + delNode = cur; + } + cur = cur->next; + if (delNode != NULL) { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyTemplates: removing ignorable blank cur\n")); +#endif + xmlUnlinkNode(delNode); + xmlFreeNode(delNode); + delNode = NULL; + } + } + } + +#ifdef WITH_XSLT_DEBUG_PROCESS + if (list != NULL) + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyTemplates: list of %d nodes\n", list->nodeNr)); +#endif + + if ((list == NULL) || (list->nodeNr == 0)) + goto exit; + + /* + * Set the context's node set and size; this is also needed for + * for xsltDoSortFunction(). + */ + ctxt->nodeList = list; + /* + * Process xsl:with-param and xsl:sort instructions. + * (The code became so verbose just to avoid the + * xmlNodePtr sorts[XSLT_MAX_SORT] if there's no xsl:sort) + * BUG TODO: We are not using namespaced potentially defined on the + * xsl:sort or xsl:with-param elements; XPath expression might fail. + */ + if (inst->children) { + xsltStackElemPtr param; + + cur = inst->children; + while (cur) { + +#ifdef WITH_DEBUGGER + if (ctxt->debugStatus != XSLT_DEBUG_NONE) + xslHandleDebugger(cur, node, NULL, ctxt); +#endif + if (ctxt->state == XSLT_STATE_STOPPED) + break; + if (cur->type == XML_TEXT_NODE) { + cur = cur->next; + continue; + } + if (! IS_XSLT_ELEM(cur)) + break; + if (IS_XSLT_NAME(cur, "with-param")) { + param = xsltParseStylesheetCallerParam(ctxt, cur); + if (param != NULL) { + param->next = withParams; + withParams = param; + } + } + if (IS_XSLT_NAME(cur, "sort")) { + xsltTemplatePtr oldCurTempRule = + ctxt->currentTemplateRule; + int nbsorts = 0; + xmlNodePtr sorts[XSLT_MAX_SORT]; + + sorts[nbsorts++] = cur; + + while (cur) { + +#ifdef WITH_DEBUGGER + if (ctxt->debugStatus != XSLT_DEBUG_NONE) + xslHandleDebugger(cur, node, NULL, ctxt); +#endif + if (ctxt->state == XSLT_STATE_STOPPED) + break; + + if (cur->type == XML_TEXT_NODE) { + cur = cur->next; + continue; + } + + if (! IS_XSLT_ELEM(cur)) + break; + if (IS_XSLT_NAME(cur, "with-param")) { + param = xsltParseStylesheetCallerParam(ctxt, cur); + if (param != NULL) { + param->next = withParams; + withParams = param; + } + } + if (IS_XSLT_NAME(cur, "sort")) { + if (nbsorts >= XSLT_MAX_SORT) { + xsltTransformError(ctxt, NULL, cur, + "The number (%d) of xsl:sort instructions exceeds the " + "maximum allowed by this processor's settings.\n", + nbsorts); + ctxt->state = XSLT_STATE_STOPPED; + break; + } else { + sorts[nbsorts++] = cur; + } + } + cur = cur->next; + } + /* + * The "current template rule" is cleared for xsl:sort. + */ + ctxt->currentTemplateRule = NULL; + /* + * Sort. + */ + xsltDoSortFunction(ctxt, sorts, nbsorts); + ctxt->currentTemplateRule = oldCurTempRule; + break; + } + cur = cur->next; + } + } + xpctxt->contextSize = list->nodeNr; + /* + * Apply templates for all selected source nodes. + */ + for (i = 0; i < list->nodeNr; i++) { + cur = list->nodeTab[i]; + /* + * The node becomes the "current node". + */ + ctxt->node = cur; + /* + * An xsl:apply-templates can change the current context doc. + * OPTIMIZE TODO: Get rid of the need to set the context doc. + */ + if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL)) + xpctxt->doc = cur->doc; + + xpctxt->proximityPosition = i + 1; + /* + * Find and apply a template for this node. + */ + xsltProcessOneNode(ctxt, cur, withParams); + } + +exit: +error: + /* + * Free the parameter list. + */ + if (withParams != NULL) + xsltFreeStackElemList(withParams); + if (list != NULL) + xmlXPathFreeNodeSet(list); + /* + * Restore context states. + */ + xpctxt->doc = oldXPDoc; + xpctxt->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; + + ctxt->document = oldDocInfo; + ctxt->nodeList = oldList; + ctxt->node = oldContextNode; + ctxt->mode = oldMode; + ctxt->modeURI = oldModeURI; +} + + +/** + * xsltChoose: + * @ctxt: a XSLT process context + * @contextNode: the current node in the source tree + * @inst: the xsl:choose instruction + * @comp: compiled information of the instruction + * + * Processes the xsl:choose instruction on the source node. + */ +void +xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, + xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) +{ + xmlNodePtr cur; + + if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) + return; + + /* + * TODO: Content model checks should be done only at compilation + * time. + */ + cur = inst->children; + if (cur == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:choose: The instruction has no content.\n"); + return; + } + +#ifdef XSLT_REFACTORED + /* + * We don't check the content model during transformation. + */ +#else + if ((! IS_XSLT_ELEM(cur)) || (! IS_XSLT_NAME(cur, "when"))) { + xsltTransformError(ctxt, NULL, inst, + "xsl:choose: xsl:when expected first\n"); + return; + } +#endif + + { + int testRes = 0, res = 0; + +#ifdef XSLT_REFACTORED + xsltStyleItemWhenPtr wcomp = NULL; +#else + xsltStylePreCompPtr wcomp = NULL; +#endif + + /* + * Process xsl:when --------------------------------------------------- + */ + while (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "when")) { + wcomp = cur->psvi; + + if ((wcomp == NULL) || (wcomp->test == NULL) || + (wcomp->comp == NULL)) + { + xsltTransformError(ctxt, NULL, cur, + "Internal error in xsltChoose(): " + "The XSLT 'when' instruction was not compiled.\n"); + goto error; + } + + +#ifdef WITH_DEBUGGER + if (xslDebugStatus != XSLT_DEBUG_NONE) { + /* + * TODO: Isn't comp->templ always NULL for xsl:choose? + */ + xslHandleDebugger(cur, contextNode, NULL, ctxt); + } +#endif +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, + "xsltChoose: test %s\n", wcomp->test)); +#endif + +#ifdef XSLT_FAST_IF + res = xsltPreCompEvalToBoolean(ctxt, contextNode, wcomp); + + if (res == -1) { + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + testRes = (res == 1) ? 1 : 0; + +#else /* XSLT_FAST_IF */ + + res = xsltPreCompEval(ctxt, cotextNode, wcomp); + + if (res != NULL) { + if (res->type != XPATH_BOOLEAN) + res = xmlXPathConvertBoolean(res); + if (res->type == XPATH_BOOLEAN) + testRes = res->boolval; + else { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, + "xsltChoose: test didn't evaluate to a boolean\n")); +#endif + goto error; + } + xmlXPathFreeObject(res); + res = NULL; + } else { + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + +#endif /* else of XSLT_FAST_IF */ + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, + "xsltChoose: test evaluate to %d\n", testRes)); +#endif + if (testRes) + goto test_is_true; + + cur = cur->next; + } + + /* + * Process xsl:otherwise ---------------------------------------------- + */ + if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "otherwise")) { + +#ifdef WITH_DEBUGGER + if (xslDebugStatus != XSLT_DEBUG_NONE) + xslHandleDebugger(cur, contextNode, NULL, ctxt); +#endif + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, + "evaluating xsl:otherwise\n")); +#endif + goto test_is_true; + } + goto exit; + +test_is_true: + + goto process_sequence; + } + +process_sequence: + + /* + * Instantiate the sequence constructor. + */ + xsltApplySequenceConstructor(ctxt, ctxt->node, cur->children, + NULL); + +exit: +error: + return; +} + +/** + * xsltIf: + * @ctxt: a XSLT process context + * @contextNode: the current node in the source tree + * @inst: the xsl:if instruction + * @castedComp: compiled information of the instruction + * + * Processes the xsl:if instruction on the source node. + */ +void +xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ + int res = 0; + +#ifdef XSLT_REFACTORED + xsltStyleItemIfPtr comp = (xsltStyleItemIfPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + + if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) + return; + if ((comp == NULL) || (comp->test == NULL) || (comp->comp == NULL)) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltIf(): " + "The XSLT 'if' instruction was not compiled.\n"); + return; + } + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, + "xsltIf: test %s\n", comp->test)); +#endif + +#ifdef XSLT_FAST_IF + { + xmlDocPtr oldLocalFragmentTop = ctxt->localRVT; + + res = xsltPreCompEvalToBoolean(ctxt, contextNode, comp); + + /* + * Cleanup fragments created during evaluation of the + * "select" expression. + */ + if (oldLocalFragmentTop != ctxt->localRVT) + xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); + } + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, + "xsltIf: test evaluate to %d\n", res)); +#endif + + if (res == -1) { + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + if (res == 1) { + /* + * Instantiate the sequence constructor of xsl:if. + */ + xsltApplySequenceConstructor(ctxt, + contextNode, inst->children, NULL); + } + +#else /* XSLT_FAST_IF */ + { + /* + * OLD CODE: + */ + xmlXPathObjectPtr xpobj = xsltPreCompEval(ctxt, contextNode, comp); + if (xpobj != NULL) { + if (xpobj->type != XPATH_BOOLEAN) + xpobj = xmlXPathConvertBoolean(xpobj); + if (xpobj->type == XPATH_BOOLEAN) { + res = xpobj->boolval; + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, + "xsltIf: test evaluate to %d\n", res)); +#endif + if (res) { + xsltApplySequenceConstructor(ctxt, + contextNode, inst->children, NULL); + } + } else { + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt, XSLT_TRACE_IF, + xsltGenericDebug(xsltGenericDebugContext, + "xsltIf: test didn't evaluate to a boolean\n")); +#endif + ctxt->state = XSLT_STATE_STOPPED; + } + xmlXPathFreeObject(xpobj); + } else { + ctxt->state = XSLT_STATE_STOPPED; + } + } +#endif /* else of XSLT_FAST_IF */ + +error: + return; +} + +/** + * xsltForEach: + * @ctxt: an XSLT transformation context + * @contextNode: the "current node" in the source tree + * @inst: the element node of the xsl:for-each instruction + * @castedComp: the compiled information of the instruction + * + * Process the xslt for-each node on the source node + */ +void +xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemForEachPtr comp = (xsltStyleItemForEachPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + int i; + xmlXPathObjectPtr res = NULL; + xmlNodePtr cur, curInst; + xmlNodeSetPtr list = NULL; + xmlNodeSetPtr oldList; + int oldXPProximityPosition, oldXPContextSize; + xmlNodePtr oldContextNode; + xsltTemplatePtr oldCurTemplRule; + xmlDocPtr oldXPDoc; + xsltDocumentPtr oldDocInfo; + xmlXPathContextPtr xpctxt; + + if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) { + xsltGenericError(xsltGenericErrorContext, + "xsltForEach(): Bad arguments.\n"); + return; + } + + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltForEach(): " + "The XSLT 'for-each' instruction was not compiled.\n"); + return; + } + if ((comp->select == NULL) || (comp->comp == NULL)) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltForEach(): " + "The selecting expression of the XSLT 'for-each' " + "instruction was not compiled correctly.\n"); + return; + } + xpctxt = ctxt->xpathCtxt; + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, + "xsltForEach: select %s\n", comp->select)); +#endif + + /* + * Save context states. + */ + oldDocInfo = ctxt->document; + oldList = ctxt->nodeList; + oldContextNode = ctxt->node; + /* + * The "current template rule" is cleared for the instantiation of + * xsl:for-each. + */ + oldCurTemplRule = ctxt->currentTemplateRule; + ctxt->currentTemplateRule = NULL; + + oldXPDoc = xpctxt->doc; + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPContextSize = xpctxt->contextSize; + + /* + * Evaluate the 'select' expression. + */ + res = xsltPreCompEval(ctxt, contextNode, comp); + + if (res != NULL) { + if (res->type == XPATH_NODESET) + list = res->nodesetval; + else { + xsltTransformError(ctxt, NULL, inst, + "The 'select' expression does not evaluate to a node set.\n"); + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, + "xsltForEach: select didn't evaluate to a node list\n")); +#endif + goto error; + } + } else { + xsltTransformError(ctxt, NULL, inst, + "Failed to evaluate the 'select' expression.\n"); + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + + if ((list == NULL) || (list->nodeNr <= 0)) + goto exit; + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, + "xsltForEach: select evaluates to %d nodes\n", list->nodeNr)); +#endif + + /* + * Set the list; this has to be done already here for xsltDoSortFunction(). + */ + ctxt->nodeList = list; + /* + * Handle xsl:sort instructions and skip them for further processing. + * BUG TODO: We are not using namespaced potentially defined on the + * xsl:sort element; XPath expression might fail. + */ + curInst = inst->children; + if (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) { + int nbsorts = 0; + xmlNodePtr sorts[XSLT_MAX_SORT]; + + sorts[nbsorts++] = curInst; + +#ifdef WITH_DEBUGGER + if (xslDebugStatus != XSLT_DEBUG_NONE) + xslHandleDebugger(curInst, contextNode, NULL, ctxt); +#endif + + curInst = curInst->next; + while (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) { + if (nbsorts >= XSLT_MAX_SORT) { + xsltTransformError(ctxt, NULL, curInst, + "The number of xsl:sort instructions exceeds the " + "maximum (%d) allowed by this processor.\n", + XSLT_MAX_SORT); + goto error; + } else { + sorts[nbsorts++] = curInst; + } + +#ifdef WITH_DEBUGGER + if (xslDebugStatus != XSLT_DEBUG_NONE) + xslHandleDebugger(curInst, contextNode, NULL, ctxt); +#endif + curInst = curInst->next; + } + xsltDoSortFunction(ctxt, sorts, nbsorts); + } + xpctxt->contextSize = list->nodeNr; + /* + * Instantiate the sequence constructor for each selected node. + */ + for (i = 0; i < list->nodeNr; i++) { + cur = list->nodeTab[i]; + /* + * The selected node becomes the "current node". + */ + ctxt->node = cur; + /* + * An xsl:for-each can change the current context doc. + * OPTIMIZE TODO: Get rid of the need to set the context doc. + */ + if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL)) + xpctxt->doc = cur->doc; + + xpctxt->proximityPosition = i + 1; + + xsltApplySequenceConstructor(ctxt, cur, curInst, NULL); + } + +exit: +error: + if (res != NULL) + xmlXPathFreeObject(res); + /* + * Restore old states. + */ + ctxt->document = oldDocInfo; + ctxt->nodeList = oldList; + ctxt->node = oldContextNode; + ctxt->currentTemplateRule = oldCurTemplRule; + + xpctxt->doc = oldXPDoc; + xpctxt->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; +} + +/************************************************************************ + * * + * Generic interface * + * * + ************************************************************************/ + +#ifdef XSLT_GENERATE_HTML_DOCTYPE +typedef struct xsltHTMLVersion { + const char *version; + const char *public; + const char *system; +} xsltHTMLVersion; + +static xsltHTMLVersion xsltHTMLVersions[] = { + { "4.01frame", "-//W3C//DTD HTML 4.01 Frameset//EN", + "http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd"}, + { "4.01strict", "-//W3C//DTD HTML 4.01//EN", + "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"}, + { "4.01trans", "-//W3C//DTD HTML 4.01 Transitional//EN", + "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"}, + { "4.01", "-//W3C//DTD HTML 4.01 Transitional//EN", + "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"}, + { "4.0strict", "-//W3C//DTD HTML 4.01//EN", + "http://www.w3.org/TR/html4/strict.dtd"}, + { "4.0trans", "-//W3C//DTD HTML 4.01 Transitional//EN", + "http://www.w3.org/TR/html4/loose.dtd"}, + { "4.0frame", "-//W3C//DTD HTML 4.01 Frameset//EN", + "http://www.w3.org/TR/html4/frameset.dtd"}, + { "4.0", "-//W3C//DTD HTML 4.01 Transitional//EN", + "http://www.w3.org/TR/html4/loose.dtd"}, + { "3.2", "-//W3C//DTD HTML 3.2//EN", NULL } +}; + +/** + * xsltGetHTMLIDs: + * @version: the version string + * @publicID: used to return the public ID + * @systemID: used to return the system ID + * + * Returns -1 if not found, 0 otherwise and the system and public + * Identifier for this given verion of HTML + */ +static int +xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID, + const xmlChar **systemID) { + unsigned int i; + if (version == NULL) + return(-1); + for (i = 0;i < (sizeof(xsltHTMLVersions)/sizeof(xsltHTMLVersions[1])); + i++) { + if (!xmlStrcasecmp(version, + (const xmlChar *) xsltHTMLVersions[i].version)) { + if (publicID != NULL) + *publicID = (const xmlChar *) xsltHTMLVersions[i].public; + if (systemID != NULL) + *systemID = (const xmlChar *) xsltHTMLVersions[i].system; + return(0); + } + } + return(-1); +} +#endif + +/** + * xsltApplyStripSpaces: + * @ctxt: a XSLT process context + * @node: the root of the XML tree + * + * Strip the unwanted ignorable spaces from the input tree + */ +void +xsltApplyStripSpaces(xsltTransformContextPtr ctxt, xmlNodePtr node) { + xmlNodePtr current; +#ifdef WITH_XSLT_DEBUG_PROCESS + int nb = 0; +#endif + + + current = node; + while (current != NULL) { + /* + * Cleanup children empty nodes if asked for + */ + if ((IS_XSLT_REAL_NODE(current)) && + (current->children != NULL) && + (xsltFindElemSpaceHandling(ctxt, current))) { + xmlNodePtr delete = NULL, cur = current->children; + + while (cur != NULL) { + if (IS_BLANK_NODE(cur)) + delete = cur; + + cur = cur->next; + if (delete != NULL) { + xmlUnlinkNode(delete); + xmlFreeNode(delete); + delete = NULL; +#ifdef WITH_XSLT_DEBUG_PROCESS + nb++; +#endif + } + } + } + + /* + * Skip to next node in document order. + */ + if (node->type == XML_ENTITY_REF_NODE) { + /* process deep in entities */ + xsltApplyStripSpaces(ctxt, node->children); + } + if ((current->children != NULL) && + (current->type != XML_ENTITY_REF_NODE)) { + current = current->children; + } else if (current->next != NULL) { + current = current->next; + } else { + do { + current = current->parent; + if (current == NULL) + break; + if (current == node) + goto done; + if (current->next != NULL) { + current = current->next; + break; + } + } while (current != NULL); + } + } + +done: +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_STRIP_SPACES,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyStripSpaces: removed %d ignorable blank node\n", nb)); +#endif + return; +} + +static int +xsltCountKeys(xsltTransformContextPtr ctxt) +{ + xsltStylesheetPtr style; + xsltKeyDefPtr keyd; + + if (ctxt == NULL) + return(-1); + + /* + * Do we have those nastly templates with a key() in the match pattern? + */ + ctxt->hasTemplKeyPatterns = 0; + style = ctxt->style; + while (style != NULL) { + if (style->keyMatch != NULL) { + ctxt->hasTemplKeyPatterns = 1; + break; + } + style = xsltNextImport(style); + } + /* + * Count number of key declarations. + */ + ctxt->nbKeys = 0; + style = ctxt->style; + while (style != NULL) { + keyd = style->keys; + while (keyd) { + ctxt->nbKeys++; + keyd = keyd->next; + } + style = xsltNextImport(style); + } + return(ctxt->nbKeys); +} + +/** + * xsltApplyStylesheetInternal: + * @style: a parsed XSLT stylesheet + * @doc: a parsed XML document + * @params: a NULL terminated array of parameters names/values tuples + * @output: the targetted output + * @profile: profile FILE * output or NULL + * @user: user provided parameter + * + * Apply the stylesheet to the document + * NOTE: This may lead to a non-wellformed output XML wise ! + * + * Returns the result document or NULL in case of error + */ +static xmlDocPtr +xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc, + const char **params, const char *output, + FILE * profile, xsltTransformContextPtr userCtxt) +{ + xmlDocPtr res = NULL; + xsltTransformContextPtr ctxt = NULL; + xmlNodePtr root, node; + const xmlChar *method; + const xmlChar *doctypePublic; + const xmlChar *doctypeSystem; + const xmlChar *version; + const xmlChar *encoding; + xsltStackElemPtr variables; + xsltStackElemPtr vptr; + + xsltInitGlobals(); + + if ((style == NULL) || (doc == NULL)) + return (NULL); + + if (style->internalized == 0) { +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "Stylesheet was not fully internalized !\n"); +#endif + } + if (doc->intSubset != NULL) { + /* + * Avoid hitting the DTD when scanning nodes + * but keep it linked as doc->intSubset + */ + xmlNodePtr cur = (xmlNodePtr) doc->intSubset; + if (cur->next != NULL) + cur->next->prev = cur->prev; + if (cur->prev != NULL) + cur->prev->next = cur->next; + if (doc->children == cur) + doc->children = cur->next; + if (doc->last == cur) + doc->last = cur->prev; + cur->prev = cur->next = NULL; + } + + /* + * Check for XPath document order availability + */ + root = xmlDocGetRootElement(doc); + if (root != NULL) { + if (((long) root->content) >= 0 && (xslDebugStatus == XSLT_DEBUG_NONE)) + xmlXPathOrderDocElems(doc); + } + + if (userCtxt != NULL) + ctxt = userCtxt; + else + ctxt = xsltNewTransformContext(style, doc); + + if (ctxt == NULL) + return (NULL); + + ctxt->initialContextDoc = doc; + ctxt->initialContextNode = (xmlNodePtr) doc; + + if (profile != NULL) + ctxt->profile = 1; + + if (output != NULL) + ctxt->outputFile = output; + else + ctxt->outputFile = NULL; + + /* + * internalize the modes if needed + */ + if (ctxt->dict != NULL) { + if (ctxt->mode != NULL) + ctxt->mode = xmlDictLookup(ctxt->dict, ctxt->mode, -1); + if (ctxt->modeURI != NULL) + ctxt->modeURI = xmlDictLookup(ctxt->dict, ctxt->modeURI, -1); + } + + XSLT_GET_IMPORT_PTR(method, style, method) + XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) + XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) + XSLT_GET_IMPORT_PTR(version, style, version) + XSLT_GET_IMPORT_PTR(encoding, style, encoding) + + if ((method != NULL) && + (!xmlStrEqual(method, (const xmlChar *) "xml"))) + { + if (xmlStrEqual(method, (const xmlChar *) "html")) { + ctxt->type = XSLT_OUTPUT_HTML; + if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { + res = htmlNewDoc(doctypeSystem, doctypePublic); + } else { + if (version == NULL) { + xmlDtdPtr dtd; + + res = htmlNewDoc(NULL, NULL); + /* + * Make sure no DTD node is generated in this case + */ + if (res != NULL) { + dtd = xmlGetIntSubset(res); + if (dtd != NULL) { + xmlUnlinkNode((xmlNodePtr) dtd); + xmlFreeDtd(dtd); + } + res->intSubset = NULL; + res->extSubset = NULL; + } + } else { + +#ifdef XSLT_GENERATE_HTML_DOCTYPE + xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem); +#endif + res = htmlNewDoc(doctypeSystem, doctypePublic); + } + } + if (res == NULL) + goto error; + res->dict = ctxt->dict; + xmlDictReference(res->dict); + +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing transformation dict for output\n"); +#endif + } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) { + xsltTransformError(ctxt, NULL, (xmlNodePtr) doc, + "xsltApplyStylesheetInternal: unsupported method xhtml, using html\n"); + ctxt->type = XSLT_OUTPUT_HTML; + res = htmlNewDoc(doctypeSystem, doctypePublic); + if (res == NULL) + goto error; + res->dict = ctxt->dict; + xmlDictReference(res->dict); + +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing transformation dict for output\n"); +#endif + } else if (xmlStrEqual(method, (const xmlChar *) "text")) { + ctxt->type = XSLT_OUTPUT_TEXT; + res = xmlNewDoc(style->version); + if (res == NULL) + goto error; + res->dict = ctxt->dict; + xmlDictReference(res->dict); + +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing transformation dict for output\n"); +#endif + } else { + xsltTransformError(ctxt, NULL, (xmlNodePtr) doc, + "xsltApplyStylesheetInternal: unsupported method (%s)\n", + method); + goto error; + } + } else { + ctxt->type = XSLT_OUTPUT_XML; + res = xmlNewDoc(style->version); + if (res == NULL) + goto error; + res->dict = ctxt->dict; + xmlDictReference(ctxt->dict); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing transformation dict for output\n"); +#endif + } + res->charset = XML_CHAR_ENCODING_UTF8; + if (encoding != NULL) + res->encoding = xmlStrdup(encoding); + variables = style->variables; + + /* + * Start the evaluation, evaluate the params, the stylesheets globals + * and start by processing the top node. + */ + if (xsltNeedElemSpaceHandling(ctxt)) + xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc)); + /* + * Evaluate global params and user-provided params. + */ + ctxt->node = (xmlNodePtr) doc; + if (ctxt->globalVars == NULL) + ctxt->globalVars = xmlHashCreate(20); + if (params != NULL) { + xsltEvalUserParams(ctxt, params); + } + + /* need to be called before evaluating global variables */ + xsltCountKeys(ctxt); + + xsltEvalGlobalVariables(ctxt); + + /* Clean up any unused RVTs. */ + xsltReleaseLocalRVTs(ctxt, NULL); + + ctxt->node = (xmlNodePtr) doc; + ctxt->output = res; + ctxt->insert = (xmlNodePtr) res; + ctxt->varsBase = ctxt->varsNr - 1; + + ctxt->xpathCtxt->contextSize = 1; + ctxt->xpathCtxt->proximityPosition = 1; + ctxt->xpathCtxt->node = NULL; /* TODO: Set the context node here? */ + /* + * Start processing the source tree ----------------------------------- + */ + xsltProcessOneNode(ctxt, ctxt->node, NULL); + /* + * Remove all remaining vars from the stack. + */ + xsltLocalVariablePop(ctxt, 0, -2); + xsltShutdownCtxtExts(ctxt); + + xsltCleanupTemplates(style); /* TODO: <- style should be read only */ + + /* + * Now cleanup our variables so stylesheet can be re-used + * + * TODO: this is not needed anymore global variables are copied + * and not evaluated directly anymore, keep this as a check + */ + if (style->variables != variables) { + vptr = style->variables; + while (vptr->next != variables) + vptr = vptr->next; + vptr->next = NULL; + xsltFreeStackElemList(style->variables); + style->variables = variables; + } + vptr = style->variables; + while (vptr != NULL) { + if (vptr->computed) { + if (vptr->value != NULL) { + xmlXPathFreeObject(vptr->value); + vptr->value = NULL; + vptr->computed = 0; + } + } + vptr = vptr->next; + } +#if 0 + /* + * code disabled by wmb; awaiting kb's review + * problem is that global variable(s) may contain xpath objects + * from doc associated with RVT, so can't be freed at this point. + * xsltFreeTransformContext includes a call to xsltFreeRVTs, so + * I assume this shouldn't be required at this point. + */ + /* + * Free all remaining tree fragments. + */ + xsltFreeRVTs(ctxt); +#endif + /* + * Do some post processing work depending on the generated output + */ + root = xmlDocGetRootElement(res); + if (root != NULL) { + const xmlChar *doctype = NULL; + + if ((root->ns != NULL) && (root->ns->prefix != NULL)) + doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name); + if (doctype == NULL) + doctype = root->name; + + /* + * Apply the default selection of the method + */ + if ((method == NULL) && + (root->ns == NULL) && + (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) { + xmlNodePtr tmp; + + tmp = res->children; + while ((tmp != NULL) && (tmp != root)) { + if (tmp->type == XML_ELEMENT_NODE) + break; + if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp))) + break; + tmp = tmp->next; + } + if (tmp == root) { + ctxt->type = XSLT_OUTPUT_HTML; + /* + * REVISIT TODO: XML_HTML_DOCUMENT_NODE is set after the + * transformation on the doc, but functions like + */ + res->type = XML_HTML_DOCUMENT_NODE; + if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { + res->intSubset = xmlCreateIntSubset(res, doctype, + doctypePublic, + doctypeSystem); +#ifdef XSLT_GENERATE_HTML_DOCTYPE + } else if (version != NULL) { + xsltGetHTMLIDs(version, &doctypePublic, + &doctypeSystem); + if (((doctypePublic != NULL) || (doctypeSystem != NULL))) + res->intSubset = + xmlCreateIntSubset(res, doctype, + doctypePublic, + doctypeSystem); +#endif + } + } + + } + if (ctxt->type == XSLT_OUTPUT_XML) { + XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) + XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) + if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { + xmlNodePtr last; + /* Need a small "hack" here to assure DTD comes before + possible comment nodes */ + node = res->children; + last = res->last; + res->children = NULL; + res->last = NULL; + res->intSubset = xmlCreateIntSubset(res, doctype, + doctypePublic, + doctypeSystem); + if (res->children != NULL) { + res->children->next = node; + node->prev = res->children; + res->last = last; + } else { + res->children = node; + res->last = last; + } + } + } + } + xmlXPathFreeNodeSet(ctxt->nodeList); + if (profile != NULL) { + xsltSaveProfiling(ctxt, profile); + } + + /* + * Be pedantic. + */ + if ((ctxt != NULL) && (ctxt->state != XSLT_STATE_OK)) { + xmlFreeDoc(res); + res = NULL; + } + if ((res != NULL) && (ctxt != NULL) && (output != NULL)) { + int ret; + + ret = xsltCheckWrite(ctxt->sec, ctxt, (const xmlChar *) output); + if (ret == 0) { + xsltTransformError(ctxt, NULL, NULL, + "xsltApplyStylesheet: forbidden to save to %s\n", + output); + } else if (ret < 0) { + xsltTransformError(ctxt, NULL, NULL, + "xsltApplyStylesheet: saving to %s may not be possible\n", + output); + } + } + +#ifdef XSLT_DEBUG_PROFILE_CACHE + printf("# Cache:\n"); + printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs); + printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars); +#endif + + if ((ctxt != NULL) && (userCtxt == NULL)) + xsltFreeTransformContext(ctxt); + + return (res); + +error: + if (res != NULL) + xmlFreeDoc(res); + +#ifdef XSLT_DEBUG_PROFILE_CACHE + printf("# Cache:\n"); + printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs); + printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars); +#endif + + if ((ctxt != NULL) && (userCtxt == NULL)) + xsltFreeTransformContext(ctxt); + return (NULL); +} + +/** + * xsltApplyStylesheet: + * @style: a parsed XSLT stylesheet + * @doc: a parsed XML document + * @params: a NULL terminated arry of parameters names/values tuples + * + * Apply the stylesheet to the document + * NOTE: This may lead to a non-wellformed output XML wise ! + * + * Returns the result document or NULL in case of error + */ +xmlDocPtr +xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, + const char **params) +{ + return (xsltApplyStylesheetInternal(style, doc, params, NULL, NULL, NULL)); +} + +/** + * xsltProfileStylesheet: + * @style: a parsed XSLT stylesheet + * @doc: a parsed XML document + * @params: a NULL terminated arry of parameters names/values tuples + * @output: a FILE * for the profiling output + * + * Apply the stylesheet to the document and dump the profiling to + * the given output. + * + * Returns the result document or NULL in case of error + */ +xmlDocPtr +xsltProfileStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, + const char **params, FILE * output) +{ + xmlDocPtr res; + + res = xsltApplyStylesheetInternal(style, doc, params, NULL, output, NULL); + return (res); +} + +/** + * xsltApplyStylesheetUser: + * @style: a parsed XSLT stylesheet + * @doc: a parsed XML document + * @params: a NULL terminated array of parameters names/values tuples + * @output: the targetted output + * @profile: profile FILE * output or NULL + * @userCtxt: user provided transform context + * + * Apply the stylesheet to the document and allow the user to provide + * its own transformation context. + * + * Returns the result document or NULL in case of error + */ +xmlDocPtr +xsltApplyStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc, + const char **params, const char *output, + FILE * profile, xsltTransformContextPtr userCtxt) +{ + xmlDocPtr res; + + res = xsltApplyStylesheetInternal(style, doc, params, output, + profile, userCtxt); + return (res); +} + +/** + * xsltRunStylesheetUser: + * @style: a parsed XSLT stylesheet + * @doc: a parsed XML document + * @params: a NULL terminated array of parameters names/values tuples + * @output: the URL/filename ot the generated resource if available + * @SAX: a SAX handler for progressive callback output (not implemented yet) + * @IObuf: an output buffer for progressive output (not implemented yet) + * @profile: profile FILE * output or NULL + * @userCtxt: user provided transform context + * + * Apply the stylesheet to the document and generate the output according + * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf. + * + * NOTE: This may lead to a non-wellformed output XML wise ! + * NOTE: This may also result in multiple files being generated + * NOTE: using IObuf, the result encoding used will be the one used for + * creating the output buffer, use the following macro to read it + * from the stylesheet + * XSLT_GET_IMPORT_PTR(encoding, style, encoding) + * NOTE: using SAX, any encoding specified in the stylesheet will be lost + * since the interface uses only UTF8 + * + * Returns the number of by written to the main resource or -1 in case of + * error. + */ +int +xsltRunStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc, + const char **params, const char *output, + xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf, + FILE * profile, xsltTransformContextPtr userCtxt) +{ + xmlDocPtr tmp; + int ret; + + if ((output == NULL) && (SAX == NULL) && (IObuf == NULL)) + return (-1); + if ((SAX != NULL) && (IObuf != NULL)) + return (-1); + + /* unsupported yet */ + if (SAX != NULL) { + XSLT_TODO /* xsltRunStylesheet xmlSAXHandlerPtr SAX */ + return (-1); + } + + tmp = xsltApplyStylesheetInternal(style, doc, params, output, profile, + userCtxt); + if (tmp == NULL) { + xsltTransformError(NULL, NULL, (xmlNodePtr) doc, + "xsltRunStylesheet : run failed\n"); + return (-1); + } + if (IObuf != NULL) { + /* TODO: incomplete, IObuf output not progressive */ + ret = xsltSaveResultTo(IObuf, tmp, style); + } else { + ret = xsltSaveResultToFilename(output, tmp, style, 0); + } + xmlFreeDoc(tmp); + return (ret); +} + +/** + * xsltRunStylesheet: + * @style: a parsed XSLT stylesheet + * @doc: a parsed XML document + * @params: a NULL terminated array of parameters names/values tuples + * @output: the URL/filename ot the generated resource if available + * @SAX: a SAX handler for progressive callback output (not implemented yet) + * @IObuf: an output buffer for progressive output (not implemented yet) + * + * Apply the stylesheet to the document and generate the output according + * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf. + * + * NOTE: This may lead to a non-wellformed output XML wise ! + * NOTE: This may also result in multiple files being generated + * NOTE: using IObuf, the result encoding used will be the one used for + * creating the output buffer, use the following macro to read it + * from the stylesheet + * XSLT_GET_IMPORT_PTR(encoding, style, encoding) + * NOTE: using SAX, any encoding specified in the stylesheet will be lost + * since the interface uses only UTF8 + * + * Returns the number of bytes written to the main resource or -1 in case of + * error. + */ +int +xsltRunStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, + const char **params, const char *output, + xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf) +{ + return(xsltRunStylesheetUser(style, doc, params, output, SAX, IObuf, + NULL, NULL)); +} + +/** + * xsltRegisterAllElement: + * @ctxt: the XPath context + * + * Registers all default XSLT elements in this context + */ +void +xsltRegisterAllElement(xsltTransformContextPtr ctxt) +{ + xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-templates", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltApplyTemplates); + xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-imports", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltApplyImports); + xsltRegisterExtElement(ctxt, (const xmlChar *) "call-template", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltCallTemplate); + xsltRegisterExtElement(ctxt, (const xmlChar *) "element", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltElement); + xsltRegisterExtElement(ctxt, (const xmlChar *) "attribute", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltAttribute); + xsltRegisterExtElement(ctxt, (const xmlChar *) "text", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltText); + xsltRegisterExtElement(ctxt, (const xmlChar *) "processing-instruction", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltProcessingInstruction); + xsltRegisterExtElement(ctxt, (const xmlChar *) "comment", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltComment); + xsltRegisterExtElement(ctxt, (const xmlChar *) "copy", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltCopy); + xsltRegisterExtElement(ctxt, (const xmlChar *) "value-of", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltValueOf); + xsltRegisterExtElement(ctxt, (const xmlChar *) "number", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltNumber); + xsltRegisterExtElement(ctxt, (const xmlChar *) "for-each", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltForEach); + xsltRegisterExtElement(ctxt, (const xmlChar *) "if", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltIf); + xsltRegisterExtElement(ctxt, (const xmlChar *) "choose", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltChoose); + xsltRegisterExtElement(ctxt, (const xmlChar *) "sort", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltSort); + xsltRegisterExtElement(ctxt, (const xmlChar *) "copy-of", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltCopyOf); + xsltRegisterExtElement(ctxt, (const xmlChar *) "message", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltMessage); + + /* + * Those don't have callable entry points but are registered anyway + */ + xsltRegisterExtElement(ctxt, (const xmlChar *) "variable", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltDebug); + xsltRegisterExtElement(ctxt, (const xmlChar *) "param", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltDebug); + xsltRegisterExtElement(ctxt, (const xmlChar *) "with-param", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltDebug); + xsltRegisterExtElement(ctxt, (const xmlChar *) "decimal-format", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltDebug); + xsltRegisterExtElement(ctxt, (const xmlChar *) "when", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltDebug); + xsltRegisterExtElement(ctxt, (const xmlChar *) "otherwise", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltDebug); + xsltRegisterExtElement(ctxt, (const xmlChar *) "fallback", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltDebug); + +} |