diff --git a/ChangeLog b/ChangeLog index 5c46b202..625f5938 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,64 @@ +Fri Jul 14 17:55:42 CEST 2006 Kasimier Buchcik + + * libxslt/attributes.c libxslt/documents.c + libxslt/functions.c libxslt/keys.c libxslt/namespaces.c + libxslt/pattern.c libxslt/preproc.c libxslt/templates.c + libxslt/templates.h libxslt/transform.c libxslt/variables.c + libxslt/xslt.c libxslt/xsltInternals.h libxslt/xsltutils.c + libxslt/xsltutils.h libexslt/common.c libexslt/dynamic.c + libexslt/functions.c libexslt/strings.c: + Refactored xsltValueOf(). Changed to use xmlXPathCastToString() + directly, rather than creating an intermediate object with + xmlXPathConvertString(). This now does not add a text-node to + the result if the string is empty (this has impact on + serialization, since an empty text-node is serialized as + , and now it will be serialized as ). + Refactored other functions in transform.c: + Mostly code cleanup/restructuring. Minimized number of + function variables for instruction which eat up function stack + memory when recursing templates (xsltIf(), xsltChoose(), + xsltApplyTemplates(), xsltCallTemplate()). + Changed XSLT tests to use xmlXPathCompiledEvalToBoolean(). + Implemented redefinition checks at compilation-time and + eliminating them at transformation time in the refactored code + paths. + Introduced the field @currentTemplateRule on xsltTransformContext to + reflect the "Current Template Rule" as defined by the spec. + 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 by Libxslt. + Added XML_COMMENT_NODE and XML_CDATA_SECTION_NODE to the macro + IS_XSLT_REAL_NODE. + Misc code cleanup/restructuring and everything else I already forgot. + Refactored lifetime of temporary result tree fragments. + Substituted all calls to the now deprecated xsltRegisterTmpRVT() + for the new xsltRegisterLocalRVT(). + Fragments of xsl:variable and xsl:param are freed when the + variable/pram is freed. + Fragments created when evaluating a "select" of xsl:varible and + xsl:param are also bound to the lifetime of the var/param. + EXSLT's func:function now uses the following functions to let take + care the transformation's garbage collector of returned tree + fragments: + xsltExtensionInstructionResultRegister(), + xsltExtensionInstructionResultFinalize() + Fixes: + #339222 - xsl:param at invalid position inside an xsl:template is + not catched + #346015 - Non-declared caller-parameters are accepted + #160400 - Compiles invalid XSLT; unbound variable accepted + #308441 - namespaced parameters become unregistered + #307103 - problem with proximity position in predicates of match + patterns + #328218 - problem with exsl:node-set() when converting strings + to node sets + #318088 - infinite recursion detection + #321505 - Multiple contiguous CDATA in output + #334493 - "--param" option does not have root context + #114377 - weird func:result/xsl:variable/exsl:node-set interaction + #150309 - Regression caused by fix for 142768 + Wed Jun 21 15:13:27 CEST 2006 Kasimier Buchcik * tests/docs/bug-54.xml tests/general/bug-54.out diff --git a/libexslt/common.c b/libexslt/common.c index ffed71b6..7b07ea28 100644 --- a/libexslt/common.c +++ b/libexslt/common.c @@ -1,116 +1,131 @@ -#define IN_LIBEXSLT -#include "libexslt/libexslt.h" - -#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__) -#include -#else -#include "config.h" -#endif - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "exslt.h" - -static void -exsltNodeSetFunction (xmlXPathParserContextPtr ctxt, int nargs) { - xmlChar *strval; - xmlNodePtr retNode; - xmlXPathObjectPtr ret; - - if (nargs != 1) { - xmlXPathSetArityError(ctxt); - return; - } - - if (xmlXPathStackIsNodeSet (ctxt)) { - xsltFunctionNodeSet (ctxt, nargs); - return; - } - - strval = xmlXPathPopString (ctxt); - retNode = xmlNewDocText (NULL, strval); - ret = xmlXPathNewValueTree (retNode); - if (ret == NULL) { - xsltGenericError(xsltGenericErrorContext, - "exsltNodeSetFunction: ret == NULL\n"); - } else { - ret->type = XPATH_NODESET; - } - - if (strval != NULL) - xmlFree (strval); - - valuePush (ctxt, ret); -} - -static void -exsltObjectTypeFunction (xmlXPathParserContextPtr ctxt, int nargs) { - xmlXPathObjectPtr obj, ret; - - if (nargs != 1) { - xmlXPathSetArityError(ctxt); - return; - } - - obj = valuePop(ctxt); - - switch (obj->type) { - case XPATH_STRING: - ret = xmlXPathNewCString("string"); - break; - case XPATH_NUMBER: - ret = xmlXPathNewCString("number"); - break; - case XPATH_BOOLEAN: - ret = xmlXPathNewCString("boolean"); - break; - case XPATH_NODESET: - ret = xmlXPathNewCString("node-set"); - break; - case XPATH_XSLT_TREE: - ret = xmlXPathNewCString("RTF"); - break; - case XPATH_USERS: - ret = xmlXPathNewCString("external"); - break; - default: - xsltGenericError(xsltGenericErrorContext, - "object-type() invalid arg\n"); - ctxt->error = XPATH_INVALID_TYPE; - xmlXPathFreeObject(obj); - return; - } - xmlXPathFreeObject(obj); - valuePush(ctxt, ret); -} - - -/** - * exsltCommonRegister: - * - * Registers the EXSLT - Common module - */ - -void -exsltCommonRegister (void) { - xsltRegisterExtModuleFunction((const xmlChar *) "node-set", - EXSLT_COMMON_NAMESPACE, - exsltNodeSetFunction); - xsltRegisterExtModuleFunction((const xmlChar *) "object-type", - EXSLT_COMMON_NAMESPACE, - exsltObjectTypeFunction); - xsltRegisterExtModuleElement((const xmlChar *) "document", - EXSLT_COMMON_NAMESPACE, - (xsltPreComputeFunction) xsltDocumentComp, - (xsltTransformFunction) xsltDocumentElem); -} +#define IN_LIBEXSLT +#include "libexslt/libexslt.h" + +#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__) +#include +#else +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "exslt.h" + +static void +exsltNodeSetFunction (xmlXPathParserContextPtr ctxt, int nargs) { + if (nargs != 1) { + xmlXPathSetArityError(ctxt); + return; + } + if (xmlXPathStackIsNodeSet (ctxt)) { + xsltFunctionNodeSet (ctxt, nargs); + return; + } else { + xmlDocPtr fragment; + xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt); + xmlNodePtr txt; + xmlChar *strval; + xmlXPathObjectPtr obj; + /* + * SPEC EXSLT: + * "You can also use this function to turn a string into a text + * node, which is helpful if you want to pass a string to a + * function that only accepts a node-set." + */ + fragment = xsltCreateRVT(tctxt); + if (fragment == NULL) { + xsltTransformError(tctxt, NULL, tctxt->inst, + "exsltNodeSetFunction: Failed to create a tree fragment.\n"); + tctxt->state = XSLT_STATE_STOPPED; + return; + } + xsltRegisterLocalRVT(tctxt, fragment); + + strval = xmlXPathPopString (ctxt); + + txt = xmlNewDocText (fragment, strval); + xmlAddChild((xmlNodePtr) fragment, txt); + obj = xmlXPathNewNodeSet(txt); + if (obj == NULL) { + xsltTransformError(tctxt, NULL, tctxt->inst, + "exsltNodeSetFunction: Failed to create a node set object.\n"); + tctxt->state = XSLT_STATE_STOPPED; + } + if (strval != NULL) + xmlFree (strval); + + valuePush (ctxt, obj); + } +} + +static void +exsltObjectTypeFunction (xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr obj, ret; + + if (nargs != 1) { + xmlXPathSetArityError(ctxt); + return; + } + + obj = valuePop(ctxt); + + switch (obj->type) { + case XPATH_STRING: + ret = xmlXPathNewCString("string"); + break; + case XPATH_NUMBER: + ret = xmlXPathNewCString("number"); + break; + case XPATH_BOOLEAN: + ret = xmlXPathNewCString("boolean"); + break; + case XPATH_NODESET: + ret = xmlXPathNewCString("node-set"); + break; + case XPATH_XSLT_TREE: + ret = xmlXPathNewCString("RTF"); + break; + case XPATH_USERS: + ret = xmlXPathNewCString("external"); + break; + default: + xsltGenericError(xsltGenericErrorContext, + "object-type() invalid arg\n"); + ctxt->error = XPATH_INVALID_TYPE; + xmlXPathFreeObject(obj); + return; + } + xmlXPathFreeObject(obj); + valuePush(ctxt, ret); +} + + +/** + * exsltCommonRegister: + * + * Registers the EXSLT - Common module + */ + +void +exsltCommonRegister (void) { + xsltRegisterExtModuleFunction((const xmlChar *) "node-set", + EXSLT_COMMON_NAMESPACE, + exsltNodeSetFunction); + xsltRegisterExtModuleFunction((const xmlChar *) "object-type", + EXSLT_COMMON_NAMESPACE, + exsltObjectTypeFunction); + xsltRegisterExtModuleElement((const xmlChar *) "document", + EXSLT_COMMON_NAMESPACE, + (xsltPreComputeFunction) xsltDocumentComp, + (xsltTransformFunction) xsltDocumentElem); +} diff --git a/libexslt/dynamic.c b/libexslt/dynamic.c index 86db6634..87ef8da3 100644 --- a/libexslt/dynamic.c +++ b/libexslt/dynamic.c @@ -1,274 +1,274 @@ -/* - * dynamic.c: Implementation of the EXSLT -- Dynamic module - * - * References: - * http://www.exslt.org/dyn/dyn.html - * - * See Copyright for the status of this software. - * - * Authors: - * Mark Vakoc - * Thomas Broyer - * - * TODO: - * elements: - * functions: - * min - * max - * sum - * map - * closure - */ - -#define IN_LIBEXSLT -#include "libexslt/libexslt.h" - -#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__) -#include -#else -#include "config.h" -#endif - -#include -#include -#include - -#include -#include -#include -#include - -#include "exslt.h" - -/** - * exsltDynEvaluateFunction: - * @ctxt: an XPath parser context - * @nargs: the number of arguments - * - * Evaluates the string as an XPath expression and returns the result - * value, which may be a boolean, number, string, node set, result tree - * fragment or external object. - */ - -static void -exsltDynEvaluateFunction(xmlXPathParserContextPtr ctxt, int nargs) { - xmlChar *str = NULL; - xmlXPathObjectPtr ret = NULL; - - if (ctxt == NULL) - return; - if (nargs != 1) { - xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL); - xsltGenericError(xsltGenericErrorContext, - "dyn:evalute() : invalid number of args %d\n", nargs); - ctxt->error = XPATH_INVALID_ARITY; - return; - } - str = xmlXPathPopString(ctxt); - /* return an empty node-set if an empty string is passed in */ - if (!str||!xmlStrlen(str)) { - if (str) xmlFree(str); - valuePush(ctxt,xmlXPathNewNodeSet(NULL)); - return; - } - ret = xmlXPathEval(str,ctxt->context); - if (ret) - valuePush(ctxt,ret); - else { - xsltGenericError(xsltGenericErrorContext, - "dyn:evaluate() : unable to evaluate expression '%s'\n",str); - valuePush(ctxt,xmlXPathNewNodeSet(NULL)); - } - xmlFree(str); - return; -} - -/** - * exsltDynMapFunction: - * @ctxt: an XPath parser context - * @nargs: the number of arguments - * - * Evaluates the string as an XPath expression and returns the result - * value, which may be a boolean, number, string, node set, result tree - * fragment or external object. - */ - -static void -exsltDynMapFunction(xmlXPathParserContextPtr ctxt, int nargs) -{ - xmlChar *str = NULL; - xmlNodeSetPtr nodeset = NULL; - xmlXPathCompExprPtr comp = NULL; - xmlXPathObjectPtr ret = NULL; - xmlDocPtr oldDoc, container; - xmlNodePtr oldNode; - int oldContextSize; - int oldProximityPosition; - int i, j; - - - if (nargs != 2) { - xmlXPathSetArityError(ctxt); - return; - } - str = xmlXPathPopString(ctxt); - if (xmlXPathCheckError(ctxt)) { - xmlXPathSetTypeError(ctxt); - return; - } - - nodeset = xmlXPathPopNodeSet(ctxt); - if (xmlXPathCheckError(ctxt)) { - xmlXPathSetTypeError(ctxt); - return; - } - if (str == NULL || !xmlStrlen(str) || !(comp = xmlXPathCompile(str))) { - if (nodeset != NULL) - xmlXPathFreeNodeSet(nodeset); - if (str != NULL) - xmlFree(str); - valuePush(ctxt, xmlXPathNewNodeSet(NULL)); - return; - } - - ret = xmlXPathNewNodeSet(NULL); - if (ret == NULL) { - xsltGenericError(xsltGenericErrorContext, - "exsltDynMapFunctoin: ret == NULL\n"); - goto cleanup; - } - - oldDoc = ctxt->context->doc; - oldNode = ctxt->context->node; - oldContextSize = ctxt->context->contextSize; - oldProximityPosition = ctxt->context->proximityPosition; - - /** - * since we really don't know we're going to be adding node(s) - * down the road we create the RVT regardless - */ - container = xsltCreateRVT(xsltXPathGetTransformContext(ctxt)); - if (container != NULL) - xsltRegisterTmpRVT(xsltXPathGetTransformContext(ctxt), container); - - if (nodeset && nodeset->nodeNr > 0) { - xmlXPathNodeSetSort(nodeset); - ctxt->context->contextSize = nodeset->nodeNr; - ctxt->context->proximityPosition = 0; - for (i = 0; i < nodeset->nodeNr; i++) { - xmlXPathObjectPtr subResult = NULL; - - ctxt->context->proximityPosition++; - ctxt->context->node = nodeset->nodeTab[i]; - ctxt->context->doc = nodeset->nodeTab[i]->doc; - - subResult = xmlXPathCompiledEval(comp, ctxt->context); - if (subResult != NULL) { - switch (subResult->type) { - case XPATH_NODESET: - if (subResult->nodesetval != NULL) - for (j = 0; j < subResult->nodesetval->nodeNr; - j++) - xmlXPathNodeSetAdd(ret->nodesetval, - subResult->nodesetval-> - nodeTab[j]); - break; - case XPATH_BOOLEAN: - if (container != NULL) { - xmlNodePtr cur = - xmlNewChild((xmlNodePtr) container, NULL, - BAD_CAST "boolean", - BAD_CAST (subResult-> - boolval ? "true" : "")); - if (cur != NULL) { - cur->ns = - xmlNewNs(cur, - BAD_CAST - "http://exslt.org/common", - BAD_CAST "exsl"); - xmlXPathNodeSetAddUnique(ret->nodesetval, - cur); - } - } - break; - case XPATH_NUMBER: - if (container != NULL) { - xmlChar *val = - xmlXPathCastNumberToString(subResult-> - floatval); - xmlNodePtr cur = - xmlNewChild((xmlNodePtr) container, NULL, - BAD_CAST "number", val); - if (val != NULL) - xmlFree(val); - - if (cur != NULL) { - cur->ns = - xmlNewNs(cur, - BAD_CAST - "http://exslt.org/common", - BAD_CAST "exsl"); - xmlXPathNodeSetAddUnique(ret->nodesetval, - cur); - } - } - break; - case XPATH_STRING: - if (container != NULL) { - xmlNodePtr cur = - xmlNewChild((xmlNodePtr) container, NULL, - BAD_CAST "string", - subResult->stringval); - if (cur != NULL) { - cur->ns = - xmlNewNs(cur, - BAD_CAST - "http://exslt.org/common", - BAD_CAST "exsl"); - xmlXPathNodeSetAddUnique(ret->nodesetval, - cur); - } - } - break; - default: - break; - } - xmlXPathFreeObject(subResult); - } - } - } - ctxt->context->doc = oldDoc; - ctxt->context->node = oldNode; - ctxt->context->contextSize = oldContextSize; - ctxt->context->proximityPosition = oldProximityPosition; - - - cleanup: - /* restore the xpath context */ - if (comp != NULL) - xmlXPathFreeCompExpr(comp); - if (nodeset != NULL) - xmlXPathFreeNodeSet(nodeset); - if (str != NULL) - xmlFree(str); - valuePush(ctxt, ret); - return; -} - - -/** - * exsltDynRegister: - * - * Registers the EXSLT - Dynamic module - */ - -void -exsltDynRegister (void) { - xsltRegisterExtModuleFunction ((const xmlChar *) "evaluate", - EXSLT_DYNAMIC_NAMESPACE, - exsltDynEvaluateFunction); - xsltRegisterExtModuleFunction ((const xmlChar *) "map", - EXSLT_DYNAMIC_NAMESPACE, - exsltDynMapFunction); - -} +/* + * dynamic.c: Implementation of the EXSLT -- Dynamic module + * + * References: + * http://www.exslt.org/dyn/dyn.html + * + * See Copyright for the status of this software. + * + * Authors: + * Mark Vakoc + * Thomas Broyer + * + * TODO: + * elements: + * functions: + * min + * max + * sum + * map + * closure + */ + +#define IN_LIBEXSLT +#include "libexslt/libexslt.h" + +#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__) +#include +#else +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include "exslt.h" + +/** + * exsltDynEvaluateFunction: + * @ctxt: an XPath parser context + * @nargs: the number of arguments + * + * Evaluates the string as an XPath expression and returns the result + * value, which may be a boolean, number, string, node set, result tree + * fragment or external object. + */ + +static void +exsltDynEvaluateFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xmlChar *str = NULL; + xmlXPathObjectPtr ret = NULL; + + if (ctxt == NULL) + return; + if (nargs != 1) { + xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL); + xsltGenericError(xsltGenericErrorContext, + "dyn:evalute() : invalid number of args %d\n", nargs); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + str = xmlXPathPopString(ctxt); + /* return an empty node-set if an empty string is passed in */ + if (!str||!xmlStrlen(str)) { + if (str) xmlFree(str); + valuePush(ctxt,xmlXPathNewNodeSet(NULL)); + return; + } + ret = xmlXPathEval(str,ctxt->context); + if (ret) + valuePush(ctxt,ret); + else { + xsltGenericError(xsltGenericErrorContext, + "dyn:evaluate() : unable to evaluate expression '%s'\n",str); + valuePush(ctxt,xmlXPathNewNodeSet(NULL)); + } + xmlFree(str); + return; +} + +/** + * exsltDynMapFunction: + * @ctxt: an XPath parser context + * @nargs: the number of arguments + * + * Evaluates the string as an XPath expression and returns the result + * value, which may be a boolean, number, string, node set, result tree + * fragment or external object. + */ + +static void +exsltDynMapFunction(xmlXPathParserContextPtr ctxt, int nargs) +{ + xmlChar *str = NULL; + xmlNodeSetPtr nodeset = NULL; + xmlXPathCompExprPtr comp = NULL; + xmlXPathObjectPtr ret = NULL; + xmlDocPtr oldDoc, container; + xmlNodePtr oldNode; + int oldContextSize; + int oldProximityPosition; + int i, j; + + + if (nargs != 2) { + xmlXPathSetArityError(ctxt); + return; + } + str = xmlXPathPopString(ctxt); + if (xmlXPathCheckError(ctxt)) { + xmlXPathSetTypeError(ctxt); + return; + } + + nodeset = xmlXPathPopNodeSet(ctxt); + if (xmlXPathCheckError(ctxt)) { + xmlXPathSetTypeError(ctxt); + return; + } + if (str == NULL || !xmlStrlen(str) || !(comp = xmlXPathCompile(str))) { + if (nodeset != NULL) + xmlXPathFreeNodeSet(nodeset); + if (str != NULL) + xmlFree(str); + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); + return; + } + + ret = xmlXPathNewNodeSet(NULL); + if (ret == NULL) { + xsltGenericError(xsltGenericErrorContext, + "exsltDynMapFunctoin: ret == NULL\n"); + goto cleanup; + } + + oldDoc = ctxt->context->doc; + oldNode = ctxt->context->node; + oldContextSize = ctxt->context->contextSize; + oldProximityPosition = ctxt->context->proximityPosition; + + /** + * since we really don't know we're going to be adding node(s) + * down the road we create the RVT regardless + */ + container = xsltCreateRVT(xsltXPathGetTransformContext(ctxt)); + if (container != NULL) + xsltRegisterLocalRVT(xsltXPathGetTransformContext(ctxt), container); + + if (nodeset && nodeset->nodeNr > 0) { + xmlXPathNodeSetSort(nodeset); + ctxt->context->contextSize = nodeset->nodeNr; + ctxt->context->proximityPosition = 0; + for (i = 0; i < nodeset->nodeNr; i++) { + xmlXPathObjectPtr subResult = NULL; + + ctxt->context->proximityPosition++; + ctxt->context->node = nodeset->nodeTab[i]; + ctxt->context->doc = nodeset->nodeTab[i]->doc; + + subResult = xmlXPathCompiledEval(comp, ctxt->context); + if (subResult != NULL) { + switch (subResult->type) { + case XPATH_NODESET: + if (subResult->nodesetval != NULL) + for (j = 0; j < subResult->nodesetval->nodeNr; + j++) + xmlXPathNodeSetAdd(ret->nodesetval, + subResult->nodesetval-> + nodeTab[j]); + break; + case XPATH_BOOLEAN: + if (container != NULL) { + xmlNodePtr cur = + xmlNewChild((xmlNodePtr) container, NULL, + BAD_CAST "boolean", + BAD_CAST (subResult-> + boolval ? "true" : "")); + if (cur != NULL) { + cur->ns = + xmlNewNs(cur, + BAD_CAST + "http://exslt.org/common", + BAD_CAST "exsl"); + xmlXPathNodeSetAddUnique(ret->nodesetval, + cur); + } + } + break; + case XPATH_NUMBER: + if (container != NULL) { + xmlChar *val = + xmlXPathCastNumberToString(subResult-> + floatval); + xmlNodePtr cur = + xmlNewChild((xmlNodePtr) container, NULL, + BAD_CAST "number", val); + if (val != NULL) + xmlFree(val); + + if (cur != NULL) { + cur->ns = + xmlNewNs(cur, + BAD_CAST + "http://exslt.org/common", + BAD_CAST "exsl"); + xmlXPathNodeSetAddUnique(ret->nodesetval, + cur); + } + } + break; + case XPATH_STRING: + if (container != NULL) { + xmlNodePtr cur = + xmlNewChild((xmlNodePtr) container, NULL, + BAD_CAST "string", + subResult->stringval); + if (cur != NULL) { + cur->ns = + xmlNewNs(cur, + BAD_CAST + "http://exslt.org/common", + BAD_CAST "exsl"); + xmlXPathNodeSetAddUnique(ret->nodesetval, + cur); + } + } + break; + default: + break; + } + xmlXPathFreeObject(subResult); + } + } + } + ctxt->context->doc = oldDoc; + ctxt->context->node = oldNode; + ctxt->context->contextSize = oldContextSize; + ctxt->context->proximityPosition = oldProximityPosition; + + + cleanup: + /* restore the xpath context */ + if (comp != NULL) + xmlXPathFreeCompExpr(comp); + if (nodeset != NULL) + xmlXPathFreeNodeSet(nodeset); + if (str != NULL) + xmlFree(str); + valuePush(ctxt, ret); + return; +} + + +/** + * exsltDynRegister: + * + * Registers the EXSLT - Dynamic module + */ + +void +exsltDynRegister (void) { + xsltRegisterExtModuleFunction ((const xmlChar *) "evaluate", + EXSLT_DYNAMIC_NAMESPACE, + exsltDynEvaluateFunction); + xsltRegisterExtModuleFunction ((const xmlChar *) "map", + EXSLT_DYNAMIC_NAMESPACE, + exsltDynMapFunction); + +} diff --git a/libexslt/functions.c b/libexslt/functions.c index 51f7d448..4d904c9e 100644 --- a/libexslt/functions.c +++ b/libexslt/functions.c @@ -1,680 +1,729 @@ -#define IN_LIBEXSLT -#include "libexslt/libexslt.h" - -#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__) -#include -#else -#include "config.h" -#endif - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "exslt.h" - -typedef struct _exsltFuncFunctionData exsltFuncFunctionData; -struct _exsltFuncFunctionData { - int nargs; /* number of arguments to the function */ - xmlNodePtr content; /* the func:fuction template content */ -}; - -typedef struct _exsltFuncData exsltFuncData; -struct _exsltFuncData { - xmlHashTablePtr funcs; /* pointer to the stylesheet module data */ - xmlXPathObjectPtr result; /* returned by func:result */ - int error; /* did an error occur? */ -}; - -typedef struct _exsltFuncResultPreComp exsltFuncResultPreComp; -struct _exsltFuncResultPreComp { - xsltElemPreComp comp; - xmlXPathCompExprPtr select; - xmlNsPtr *nsList; - int nsNr; -}; - -/* Used for callback function in exsltInitFunc */ -typedef struct _exsltFuncImportRegData exsltFuncImportRegData; -struct _exsltFuncImportRegData { - xsltTransformContextPtr ctxt; - xmlHashTablePtr hash; -}; - -static void exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, - int nargs); -static exsltFuncFunctionData *exsltFuncNewFunctionData(void); - -/** - * exsltFuncRegisterFunc: - * @func: the #exsltFuncFunctionData for the function - * @ctxt: an XSLT transformation context - * @URI: the function namespace URI - * @name: the function name - * - * Registers a function declared by a func:function element - */ -static void -exsltFuncRegisterFunc (exsltFuncFunctionData *data, - xsltTransformContextPtr ctxt, - const xmlChar *URI, const xmlChar *name, - ATTRIBUTE_UNUSED const xmlChar *ignored) { - if ((data == NULL) || (ctxt == NULL) || (URI == NULL) || (name == NULL)) - return; - - xsltGenericDebug(xsltGenericDebugContext, - "exsltFuncRegisterFunc: register {%s}%s\n", - URI, name); - xsltRegisterExtFunction(ctxt, name, URI, - exsltFuncFunctionFunction); -} - -/* - * exsltFuncRegisterImportFunc - * @data: the exsltFuncFunctionData for the function - * @ch: structure containing context and hash table - * @URI: the function namespace URI - * @name: the function name - * - * Checks if imported function is already registered in top-level - * stylesheet. If not, copies function data and registers function - */ -static void -exsltFuncRegisterImportFunc (exsltFuncFunctionData *data, - exsltFuncImportRegData *ch, - const xmlChar *URI, const xmlChar *name, - ATTRIBUTE_UNUSED const xmlChar *ignored) { - exsltFuncFunctionData *func=NULL; - - if ((data == NULL) || (ch == NULL) || (URI == NULL) || (name == NULL)) - return; - - if (ch->ctxt == NULL || ch->hash == NULL) - return; - - /* Check if already present */ - func = (exsltFuncFunctionData*)xmlHashLookup2(ch->hash, URI, name); - if (func == NULL) { /* Not yet present - copy it in */ - func = exsltFuncNewFunctionData(); - memcpy(func, data, sizeof(exsltFuncFunctionData)); - if (xmlHashAddEntry2(ch->hash, URI, name, func) < 0) { - xsltGenericError(xsltGenericErrorContext, - "Failed to register function {%s}%s\n", - URI, name); - } else { /* Do the registration */ - xsltGenericDebug(xsltGenericDebugContext, - "exsltFuncRegisterImportFunc: register {%s}%s\n", - URI, name); - xsltRegisterExtFunction(ch->ctxt, name, URI, - exsltFuncFunctionFunction); - } - } -} - -/** - * exsltFuncInit: - * @ctxt: an XSLT transformation context - * @URI: the namespace URI for the extension - * - * Initializes the EXSLT - Functions module. - * Called at transformation-time; merges all - * functions declared in the import tree taking - * import precedence into account, i.e. overriding - * functions with lower import precedence. - * - * Returns the data for this transformation - */ -static exsltFuncData * -exsltFuncInit (xsltTransformContextPtr ctxt, const xmlChar *URI) { - exsltFuncData *ret; - xsltStylesheetPtr tmp; - exsltFuncImportRegData ch; - xmlHashTablePtr hash; - - ret = (exsltFuncData *) xmlMalloc (sizeof(exsltFuncData)); - if (ret == NULL) { - xsltGenericError(xsltGenericErrorContext, - "exsltFuncInit: not enough memory\n"); - return(NULL); - } - memset(ret, 0, sizeof(exsltFuncData)); - - ret->result = NULL; - ret->error = 0; - - ch.hash = (xmlHashTablePtr) xsltStyleGetExtData(ctxt->style, URI); - ret->funcs = ch.hash; - xmlHashScanFull(ch.hash, (xmlHashScannerFull) exsltFuncRegisterFunc, ctxt); - tmp = ctxt->style; - ch.ctxt = ctxt; - while ((tmp=xsltNextImport(tmp))!=NULL) { - hash = xsltGetExtInfo(tmp, URI); - if (hash != NULL) { - xmlHashScanFull(hash, - (xmlHashScannerFull) exsltFuncRegisterImportFunc, &ch); - } - } - - return(ret); -} - -/** - * exsltFuncShutdown: - * @ctxt: an XSLT transformation context - * @URI: the namespace URI for the extension - * @data: the module data to free up - * - * Shutdown the EXSLT - Functions module - * Called at transformation-time. - */ -static void -exsltFuncShutdown (xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, - const xmlChar *URI ATTRIBUTE_UNUSED, - exsltFuncData *data) { - if (data->result != NULL) - xmlXPathFreeObject(data->result); - xmlFree(data); -} - -/** - * exsltFuncStyleInit: - * @style: an XSLT stylesheet - * @URI: the namespace URI for the extension - * - * Allocates the stylesheet data for EXSLT - Function - * Called at compile-time. - * - * Returns the allocated data - */ -static xmlHashTablePtr -exsltFuncStyleInit (xsltStylesheetPtr style ATTRIBUTE_UNUSED, - const xmlChar *URI ATTRIBUTE_UNUSED) { - return xmlHashCreate(1); -} - -/** - * exsltFuncStyleShutdown: - * @style: an XSLT stylesheet - * @URI: the namespace URI for the extension - * @data: the stylesheet data to free up - * - * Shutdown the EXSLT - Function module - * Called at compile-time. - */ -static void -exsltFuncStyleShutdown (xsltStylesheetPtr style ATTRIBUTE_UNUSED, - const xmlChar *URI ATTRIBUTE_UNUSED, - xmlHashTablePtr data) { - xmlHashFree(data, (xmlHashDeallocator) xmlFree); -} - -/** - * exsltFuncNewFunctionData: - * - * Allocates an #exslFuncFunctionData object - * - * Returns the new structure - */ -static exsltFuncFunctionData * -exsltFuncNewFunctionData (void) { - exsltFuncFunctionData *ret; - - ret = (exsltFuncFunctionData *) xmlMalloc (sizeof(exsltFuncFunctionData)); - if (ret == NULL) { - xsltGenericError(xsltGenericErrorContext, - "exsltFuncNewFunctionData: not enough memory\n"); - return (NULL); - } - memset(ret, 0, sizeof(exsltFuncFunctionData)); - - ret->nargs = 0; - ret->content = NULL; - - return(ret); -} - -/** - * exsltFreeFuncResultPreComp: - * @comp: the #exsltFuncResultPreComp to free up - * - * Deallocates an #exsltFuncResultPreComp - */ -static void -exsltFreeFuncResultPreComp (exsltFuncResultPreComp *comp) { - if (comp == NULL) - return; - - if (comp->select != NULL) - xmlXPathFreeCompExpr (comp->select); - if (comp->nsList != NULL) - xmlFree(comp->nsList); - xmlFree(comp); -} - -/** - * exsltFuncFunctionFunction: - * @ctxt: an XPath parser context - * @nargs: the number of arguments - * - * Evaluates the func:function element that defines the called function. - */ -static void -exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, int nargs) { - xmlXPathObjectPtr obj, oldResult, ret; - exsltFuncData *data; - exsltFuncFunctionData *func; - xmlNodePtr paramNode, oldInsert, fake, content = NULL; - int oldBase; - xsltStackElemPtr params = NULL, param; - xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt); - int i; - - /* - * retrieve func:function template - */ - data = (exsltFuncData *) xsltGetExtData (tctxt, - EXSLT_FUNCTIONS_NAMESPACE); - oldResult = data->result; - data->result = NULL; - - func = (exsltFuncFunctionData*) xmlHashLookup2 (data->funcs, - ctxt->context->functionURI, - ctxt->context->function); - - /* - * params handling - */ - if (nargs > func->nargs) { - xsltGenericError(xsltGenericErrorContext, - "{%s}%s: called with too many arguments\n", - ctxt->context->functionURI, ctxt->context->function); - ctxt->error = XPATH_INVALID_ARITY; - return; - } - if (func->content != NULL) { - paramNode = func->content->prev; - content = func->content; - } - else - paramNode = NULL; - if ((paramNode == NULL) && (func->nargs != 0)) { - xsltGenericError(xsltGenericErrorContext, - "exsltFuncFunctionFunction: nargs != 0 and " - "param == NULL\n"); - return; - } - - /* set params */ - for (i = func->nargs; (i > nargs) && (paramNode != NULL); i--) { - paramNode = paramNode->prev; - if (content != NULL) - content = content->prev; - } - while ((i-- > 0) && (paramNode != NULL)) { - obj = valuePop(ctxt); - /* FIXME: this is a bit hackish */ - param = xsltParseStylesheetCallerParam (tctxt, paramNode); - param->computed = 1; - if (param->value != NULL) - xmlXPathFreeObject(param->value); - param->value = obj; - param->next = params; - params = param; - paramNode = paramNode->prev; - } - - /* - * actual processing - */ - fake = xmlNewDocNode(tctxt->output, NULL, - (const xmlChar *)"fake", NULL); - oldInsert = tctxt->insert; - tctxt->insert = fake; - /* - * In order to give the function variables a new 'scope' we - * change varsBase in the context. - */ - oldBase = tctxt->varsBase; - tctxt->varsBase = tctxt->varsNr; - xsltApplyOneTemplate (tctxt, xmlXPathGetContextNode(ctxt), - content, NULL, params); - tctxt->insert = oldInsert; - tctxt->varsBase = oldBase; /* restore original scope */ - if (params != NULL) - xsltFreeStackElemList(params); - - if (data->error != 0) - return; - - if (data->result != NULL) - ret = data->result; - else - ret = xmlXPathNewCString(""); - - data->result = oldResult; - - /* - * It is an error if the instantiation of the template results in - * the generation of result nodes. - */ - if (fake->children != NULL) { -#ifdef LIBXML_DEBUG_ENABLED - xmlDebugDumpNode (stderr, fake, 1); -#endif - xsltGenericError(xsltGenericErrorContext, - "{%s}%s: cannot write to result tree while " - "executing a function\n", - ctxt->context->functionURI, ctxt->context->function); - xmlFreeNode(fake); - return; - } - xmlFreeNode(fake); - valuePush(ctxt, ret); -} - - -static void -exsltFuncFunctionComp (xsltStylesheetPtr style, xmlNodePtr inst) { - xmlChar *name, *prefix; - xmlNsPtr ns; - xmlHashTablePtr data; - exsltFuncFunctionData *func; - - if ((style == NULL) || (inst == NULL)) - return; - - - { - xmlChar *qname; - - qname = xmlGetProp(inst, (const xmlChar *) "name"); - name = xmlSplitQName2 (qname, &prefix); - xmlFree(qname); - } - if ((name == NULL) || (prefix == NULL)) { - xsltGenericError(xsltGenericErrorContext, - "func:function: not a QName\n"); - if (name != NULL) - xmlFree(name); - return; - } - /* namespace lookup */ - ns = xmlSearchNs (inst->doc, inst, prefix); - if (ns == NULL) { - xsltGenericError(xsltGenericErrorContext, - "func:function: undeclared prefix %s\n", - prefix); - xmlFree(name); - xmlFree(prefix); - return; - } - xmlFree(prefix); - - /* - * Create function data - */ - func = exsltFuncNewFunctionData(); - func->content = inst->children; - while (IS_XSLT_ELEM(func->content) && - IS_XSLT_NAME(func->content, "param")) { - func->content = func->content->next; - func->nargs++; - } - - xsltParseTemplateContent(style, inst); - - /* - * Register the function data such that it can be retrieved - * by exslFuncFunctionFunction - */ -#ifdef XSLT_REFACTORED - /* - * Ensure that the hash table will be stored in the *current* - * stylesheet level in order to correctly evaluate the - * import precedence. - */ - data = (xmlHashTablePtr) - xsltStyleStylesheetLevelGetExtData(style, - EXSLT_FUNCTIONS_NAMESPACE); -#else - data = (xmlHashTablePtr) - xsltStyleGetExtData (style, EXSLT_FUNCTIONS_NAMESPACE); -#endif - if (data == NULL) { - xsltGenericError(xsltGenericErrorContext, - "exsltFuncFunctionComp: no stylesheet data\n"); - xmlFree(name); - return; - } - - if (xmlHashAddEntry2 (data, ns->href, name, func) < 0) { - xsltTransformError(NULL, style, inst, - "Failed to register function {%s}%s\n", - ns->href, name); - style->errors++; - } else { - xsltGenericDebug(xsltGenericDebugContext, - "exsltFuncFunctionComp: register {%s}%s\n", - ns->href, name); - } - xmlFree(name); -} - -static xsltElemPreCompPtr -exsltFuncResultComp (xsltStylesheetPtr style, xmlNodePtr inst, - xsltTransformFunction function) { - xmlNodePtr test; - xmlChar *sel; - exsltFuncResultPreComp *ret; - - /* - * "Validity" checking - */ - /* it is an error to have any following sibling elements aside - * from the xsl:fallback element. - */ - for (test = inst->next; test != NULL; test = test->next) { - if (test->type != XML_ELEMENT_NODE) - continue; - if (IS_XSLT_ELEM(test) && IS_XSLT_NAME(test, "fallback")) - continue; - xsltGenericError(xsltGenericErrorContext, - "exsltFuncResultElem: only xsl:fallback is " - "allowed to follow func:result\n"); - return (NULL); - } - /* it is an error for a func:result element to not be a descendant - * of func:function. - * it is an error if a func:result occurs within a func:result - * element. - * it is an error if instanciating the content of a variable - * binding element (i.e. xsl:variable, xsl:param) results in the - * instanciation of a func:result element. - */ - for (test = inst->parent; test != NULL; test = test->parent) { - if ((test->ns != NULL) && - (xmlStrEqual(test->ns->href, EXSLT_FUNCTIONS_NAMESPACE))) { - if (xmlStrEqual(test->name, (const xmlChar *) "function")) { - break; - } - if (xmlStrEqual(test->name, (const xmlChar *) "result")) { - xsltGenericError(xsltGenericErrorContext, - "func:result element not allowed within" - " another func:result element\n"); - return (NULL); - } - } - if (IS_XSLT_ELEM(test) && - (IS_XSLT_NAME(test, "variable") || - IS_XSLT_NAME(test, "param"))) { - xsltGenericError(xsltGenericErrorContext, - "func:result element not allowed within" - " a variable binding element\n"); - return (NULL); - } - } - - /* - * Precomputation - */ - ret = (exsltFuncResultPreComp *) - xmlMalloc (sizeof(exsltFuncResultPreComp)); - if (ret == NULL) { - xsltPrintErrorContext(NULL, NULL, NULL); - xsltGenericError(xsltGenericErrorContext, - "exsltFuncResultComp : malloc failed\n"); - return (NULL); - } - memset(ret, 0, sizeof(exsltFuncResultPreComp)); - - xsltInitElemPreComp ((xsltElemPreCompPtr) ret, style, inst, function, - (xsltElemPreCompDeallocator) exsltFreeFuncResultPreComp); - ret->select = NULL; - - /* - * Precompute the select attribute - */ - sel = xmlGetNsProp(inst, (const xmlChar *) "select", NULL); - if (sel != NULL) { - ret->select = xmlXPathCompile (sel); - xmlFree(sel); - } - /* - * Precompute the namespace list - */ - ret->nsList = xmlGetNsList(inst->doc, inst); - if (ret->nsList != NULL) { - int i = 0; - while (ret->nsList[i] != NULL) - i++; - ret->nsNr = i; - } - return ((xsltElemPreCompPtr) ret); -} - -static void -exsltFuncResultElem (xsltTransformContextPtr ctxt, - xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst, - exsltFuncResultPreComp *comp) { - exsltFuncData *data; - xmlXPathObjectPtr ret; - xmlNsPtr *oldNsList; - int oldNsNr; - - /* It is an error if instantiating the content of the - * func:function element results in the instantiation of more than - * one func:result elements. - */ - data = (exsltFuncData *) xsltGetExtData (ctxt, EXSLT_FUNCTIONS_NAMESPACE); - if (data == NULL) { - xsltGenericError(xsltGenericErrorContext, - "exsltFuncReturnElem: data == NULL\n"); - return; - } - if (data->result != NULL) { - xsltGenericError(xsltGenericErrorContext, - "func:result already instanciated\n"); - data->error = 1; - return; - } - /* - * Processing - */ - if (comp->select != NULL) { - /* If the func:result element has a select attribute, then the - * value of the attribute must be an expression and the - * returned value is the object that results from evaluating - * the expression. In this case, the content must be empty. - */ - if (inst->children != NULL) { - xsltGenericError(xsltGenericErrorContext, - "func:result content must be empty if it" - " has a select attribute\n"); - data->error = 1; - return; - } - oldNsList = ctxt->xpathCtxt->namespaces; - oldNsNr = ctxt->xpathCtxt->nsNr; - ctxt->xpathCtxt->namespaces = comp->nsList; - ctxt->xpathCtxt->nsNr = comp->nsNr; - ret = xmlXPathCompiledEval(comp->select, ctxt->xpathCtxt); - ctxt->xpathCtxt->nsNr = oldNsNr; - ctxt->xpathCtxt->namespaces = oldNsList; - if (ret == NULL) { - xsltGenericError(xsltGenericErrorContext, - "exsltFuncResultElem: ret == NULL\n"); - return; - } - } else if (inst->children != NULL) { - /* If the func:result element does not have a select attribute - * and has non-empty content (i.e. the func:result element has - * one or more child nodes), then the content of the - * func:result element specifies the value. - */ - xmlNodePtr oldInsert; - xmlDocPtr container; - - container = xsltCreateRVT(ctxt); - if (container == NULL) { - xsltGenericError(xsltGenericErrorContext, - "exsltFuncResultElem: out of memory\n"); - data->error = 1; - return; - } - xsltRegisterTmpRVT(ctxt, container); - oldInsert = ctxt->insert; - ctxt->insert = (xmlNodePtr) container; - xsltApplyOneTemplate (ctxt, ctxt->xpathCtxt->node, - inst->children, NULL, NULL); - ctxt->insert = oldInsert; - - ret = xmlXPathNewValueTree((xmlNodePtr) container); - if (ret == NULL) { - xsltGenericError(xsltGenericErrorContext, - "exsltFuncResultElem: ret == NULL\n"); - data->error = 1; - } else { - ret->boolval = 0; /* Freeing is not handled there anymore */ - } - } else { - /* If the func:result element has empty content and does not - * have a select attribute, then the returned value is an - * empty string. - */ - ret = xmlXPathNewCString(""); - } - data->result = ret; -} - -/** - * exsltFuncRegister: - * - * Registers the EXSLT - Functions module - */ -void -exsltFuncRegister (void) { - xsltRegisterExtModuleFull (EXSLT_FUNCTIONS_NAMESPACE, - (xsltExtInitFunction) exsltFuncInit, - (xsltExtShutdownFunction) exsltFuncShutdown, - (xsltStyleExtInitFunction) exsltFuncStyleInit, - (xsltStyleExtShutdownFunction) exsltFuncStyleShutdown); - - xsltRegisterExtModuleTopLevel ((const xmlChar *) "function", - EXSLT_FUNCTIONS_NAMESPACE, - exsltFuncFunctionComp); - xsltRegisterExtModuleElement ((const xmlChar *) "result", - EXSLT_FUNCTIONS_NAMESPACE, - (xsltPreComputeFunction)exsltFuncResultComp, - (xsltTransformFunction) exsltFuncResultElem); -} +#define IN_LIBEXSLT +#include "libexslt/libexslt.h" + +#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__) +#include +#else +#include "config.h" +#endif + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "exslt.h" + +typedef struct _exsltFuncFunctionData exsltFuncFunctionData; +struct _exsltFuncFunctionData { + int nargs; /* number of arguments to the function */ + xmlNodePtr content; /* the func:fuction template content */ +}; + +typedef struct _exsltFuncData exsltFuncData; +struct _exsltFuncData { + xmlHashTablePtr funcs; /* pointer to the stylesheet module data */ + xmlXPathObjectPtr result; /* returned by func:result */ + int error; /* did an error occur? */ + xmlDocPtr RVT; /* result tree fragment */ +}; + +typedef struct _exsltFuncResultPreComp exsltFuncResultPreComp; +struct _exsltFuncResultPreComp { + xsltElemPreComp comp; + xmlXPathCompExprPtr select; + xmlNsPtr *nsList; + int nsNr; +}; + +/* Used for callback function in exsltInitFunc */ +typedef struct _exsltFuncImportRegData exsltFuncImportRegData; +struct _exsltFuncImportRegData { + xsltTransformContextPtr ctxt; + xmlHashTablePtr hash; +}; + +static void exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, + int nargs); +static exsltFuncFunctionData *exsltFuncNewFunctionData(void); + +static const xmlChar *exsltResultDataID = (const xmlChar *) "EXSLT Result"; + +/** + * exsltFuncRegisterFunc: + * @func: the #exsltFuncFunctionData for the function + * @ctxt: an XSLT transformation context + * @URI: the function namespace URI + * @name: the function name + * + * Registers a function declared by a func:function element + */ +static void +exsltFuncRegisterFunc (exsltFuncFunctionData *data, + xsltTransformContextPtr ctxt, + const xmlChar *URI, const xmlChar *name, + ATTRIBUTE_UNUSED const xmlChar *ignored) { + if ((data == NULL) || (ctxt == NULL) || (URI == NULL) || (name == NULL)) + return; + + xsltGenericDebug(xsltGenericDebugContext, + "exsltFuncRegisterFunc: register {%s}%s\n", + URI, name); + xsltRegisterExtFunction(ctxt, name, URI, + exsltFuncFunctionFunction); +} + +/* + * exsltFuncRegisterImportFunc + * @data: the exsltFuncFunctionData for the function + * @ch: structure containing context and hash table + * @URI: the function namespace URI + * @name: the function name + * + * Checks if imported function is already registered in top-level + * stylesheet. If not, copies function data and registers function + */ +static void +exsltFuncRegisterImportFunc (exsltFuncFunctionData *data, + exsltFuncImportRegData *ch, + const xmlChar *URI, const xmlChar *name, + ATTRIBUTE_UNUSED const xmlChar *ignored) { + exsltFuncFunctionData *func=NULL; + + if ((data == NULL) || (ch == NULL) || (URI == NULL) || (name == NULL)) + return; + + if (ch->ctxt == NULL || ch->hash == NULL) + return; + + /* Check if already present */ + func = (exsltFuncFunctionData*)xmlHashLookup2(ch->hash, URI, name); + if (func == NULL) { /* Not yet present - copy it in */ + func = exsltFuncNewFunctionData(); + memcpy(func, data, sizeof(exsltFuncFunctionData)); + if (xmlHashAddEntry2(ch->hash, URI, name, func) < 0) { + xsltGenericError(xsltGenericErrorContext, + "Failed to register function {%s}%s\n", + URI, name); + } else { /* Do the registration */ + xsltGenericDebug(xsltGenericDebugContext, + "exsltFuncRegisterImportFunc: register {%s}%s\n", + URI, name); + xsltRegisterExtFunction(ch->ctxt, name, URI, + exsltFuncFunctionFunction); + } + } +} + +/** + * exsltFuncInit: + * @ctxt: an XSLT transformation context + * @URI: the namespace URI for the extension + * + * Initializes the EXSLT - Functions module. + * Called at transformation-time; merges all + * functions declared in the import tree taking + * import precedence into account, i.e. overriding + * functions with lower import precedence. + * + * Returns the data for this transformation + */ +static exsltFuncData * +exsltFuncInit (xsltTransformContextPtr ctxt, const xmlChar *URI) { + exsltFuncData *ret; + xsltStylesheetPtr tmp; + exsltFuncImportRegData ch; + xmlHashTablePtr hash; + + ret = (exsltFuncData *) xmlMalloc (sizeof(exsltFuncData)); + if (ret == NULL) { + xsltGenericError(xsltGenericErrorContext, + "exsltFuncInit: not enough memory\n"); + return(NULL); + } + memset(ret, 0, sizeof(exsltFuncData)); + + ret->result = NULL; + ret->error = 0; + + ch.hash = (xmlHashTablePtr) xsltStyleGetExtData(ctxt->style, URI); + ret->funcs = ch.hash; + xmlHashScanFull(ch.hash, (xmlHashScannerFull) exsltFuncRegisterFunc, ctxt); + tmp = ctxt->style; + ch.ctxt = ctxt; + while ((tmp=xsltNextImport(tmp))!=NULL) { + hash = xsltGetExtInfo(tmp, URI); + if (hash != NULL) { + xmlHashScanFull(hash, + (xmlHashScannerFull) exsltFuncRegisterImportFunc, &ch); + } + } + + return(ret); +} + +/** + * exsltFuncShutdown: + * @ctxt: an XSLT transformation context + * @URI: the namespace URI for the extension + * @data: the module data to free up + * + * Shutdown the EXSLT - Functions module + * Called at transformation-time. + */ +static void +exsltFuncShutdown (xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, + const xmlChar *URI ATTRIBUTE_UNUSED, + exsltFuncData *data) { + if (data->result != NULL) + xmlXPathFreeObject(data->result); + xmlFree(data); +} + +/** + * exsltFuncStyleInit: + * @style: an XSLT stylesheet + * @URI: the namespace URI for the extension + * + * Allocates the stylesheet data for EXSLT - Function + * Called at compile-time. + * + * Returns the allocated data + */ +static xmlHashTablePtr +exsltFuncStyleInit (xsltStylesheetPtr style ATTRIBUTE_UNUSED, + const xmlChar *URI ATTRIBUTE_UNUSED) { + return xmlHashCreate(1); +} + +/** + * exsltFuncStyleShutdown: + * @style: an XSLT stylesheet + * @URI: the namespace URI for the extension + * @data: the stylesheet data to free up + * + * Shutdown the EXSLT - Function module + * Called at compile-time. + */ +static void +exsltFuncStyleShutdown (xsltStylesheetPtr style ATTRIBUTE_UNUSED, + const xmlChar *URI ATTRIBUTE_UNUSED, + xmlHashTablePtr data) { + xmlHashFree(data, (xmlHashDeallocator) xmlFree); +} + +/** + * exsltFuncNewFunctionData: + * + * Allocates an #exslFuncFunctionData object + * + * Returns the new structure + */ +static exsltFuncFunctionData * +exsltFuncNewFunctionData (void) { + exsltFuncFunctionData *ret; + + ret = (exsltFuncFunctionData *) xmlMalloc (sizeof(exsltFuncFunctionData)); + if (ret == NULL) { + xsltGenericError(xsltGenericErrorContext, + "exsltFuncNewFunctionData: not enough memory\n"); + return (NULL); + } + memset(ret, 0, sizeof(exsltFuncFunctionData)); + + ret->nargs = 0; + ret->content = NULL; + + return(ret); +} + +/** + * exsltFreeFuncResultPreComp: + * @comp: the #exsltFuncResultPreComp to free up + * + * Deallocates an #exsltFuncResultPreComp + */ +static void +exsltFreeFuncResultPreComp (exsltFuncResultPreComp *comp) { + if (comp == NULL) + return; + + if (comp->select != NULL) + xmlXPathFreeCompExpr (comp->select); + if (comp->nsList != NULL) + xmlFree(comp->nsList); + xmlFree(comp); +} + +/** + * exsltFuncFunctionFunction: + * @ctxt: an XPath parser context + * @nargs: the number of arguments + * + * Evaluates the func:function element that defines the called function. + */ +static void +exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr obj, oldResult, ret; + exsltFuncData *data; + exsltFuncFunctionData *func; + xmlNodePtr paramNode, oldInsert, fake; + int oldBase; + xsltStackElemPtr params = NULL, param; + xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt); + int i; + + /* + * retrieve func:function template + */ + data = (exsltFuncData *) xsltGetExtData (tctxt, + EXSLT_FUNCTIONS_NAMESPACE); + oldResult = data->result; + data->result = NULL; + + func = (exsltFuncFunctionData*) xmlHashLookup2 (data->funcs, + ctxt->context->functionURI, + ctxt->context->function); + + /* + * params handling + */ + if (nargs > func->nargs) { + xsltGenericError(xsltGenericErrorContext, + "{%s}%s: called with too many arguments\n", + ctxt->context->functionURI, ctxt->context->function); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + if (func->content != NULL) { + paramNode = func->content->prev; + } + else + paramNode = NULL; + if ((paramNode == NULL) && (func->nargs != 0)) { + xsltGenericError(xsltGenericErrorContext, + "exsltFuncFunctionFunction: nargs != 0 and " + "param == NULL\n"); + return; + } + /* + * Process xsl:param instructions which were not set by the + * invoking function call. + */ + for (i = func->nargs; (i > nargs) && (paramNode != NULL); i--) { + /* + * Those are the xsl:param instructions, which were not + * set by the calling function. + */ + param = xsltParseStylesheetCallerParam (tctxt, paramNode); + param->next = params; + params = param; + paramNode = paramNode->prev; + } + /* + * Process xsl:param instructions which are set by the + * invoking function call. + */ + while ((i-- > 0) && (paramNode != NULL)) { + obj = valuePop(ctxt); + /* + * TODO: Using xsltParseStylesheetCallerParam() is actually + * not correct, since we are processing an xsl:param; but + * using xsltParseStylesheetParam() won't work, as it puts + * the param on the varible stack and does not give access to + * the created xsltStackElemPtr. + * It's also not correct, as xsltParseStylesheetCallerParam() + * will report error messages indicating an "xsl:with-param" and + * not the actual "xsl:param". + */ + param = xsltParseStylesheetCallerParam (tctxt, paramNode); + param->computed = 1; + if (param->value != NULL) + xmlXPathFreeObject(param->value); + param->value = obj; + param->next = params; + params = param; + paramNode = paramNode->prev; + } + + /* + * actual processing + */ + fake = xmlNewDocNode(tctxt->output, NULL, + (const xmlChar *)"fake", NULL); + oldInsert = tctxt->insert; + tctxt->insert = fake; + /* + * In order to give the function variables a new 'scope' we + * change varsBase in the context. + */ + oldBase = tctxt->varsBase; + tctxt->varsBase = tctxt->varsNr; + xsltApplyOneTemplate (tctxt, xmlXPathGetContextNode(ctxt), + func->content, NULL, params); + tctxt->insert = oldInsert; + tctxt->varsBase = oldBase; /* restore original scope */ + if (params != NULL) + xsltFreeStackElemList(params); + + if (data->error != 0) + goto error; + + if (data->result != NULL) { + ret = data->result; + } else + ret = xmlXPathNewCString(""); + + data->result = oldResult; + + /* + * It is an error if the instantiation of the template results in + * the generation of result nodes. + */ + if (fake->children != NULL) { +#ifdef LIBXML_DEBUG_ENABLED + xmlDebugDumpNode (stderr, fake, 1); +#endif + xsltGenericError(xsltGenericErrorContext, + "{%s}%s: cannot write to result tree while " + "executing a function\n", + ctxt->context->functionURI, ctxt->context->function); + xmlFreeNode(fake); + goto error; + } + xmlFreeNode(fake); + valuePush(ctxt, ret); + +error: + /* + * IMPORTANT: This enables previously tree fragments marked as + * being results of a function, to be garbage-collected after + * the calling process exits. + */ + xsltExtensionInstructionResultFinalize(tctxt); +} + + +static void +exsltFuncFunctionComp (xsltStylesheetPtr style, xmlNodePtr inst) { + xmlChar *name, *prefix; + xmlNsPtr ns; + xmlHashTablePtr data; + exsltFuncFunctionData *func; + + if ((style == NULL) || (inst == NULL)) + return; + + + { + xmlChar *qname; + + qname = xmlGetProp(inst, (const xmlChar *) "name"); + name = xmlSplitQName2 (qname, &prefix); + xmlFree(qname); + } + if ((name == NULL) || (prefix == NULL)) { + xsltGenericError(xsltGenericErrorContext, + "func:function: not a QName\n"); + if (name != NULL) + xmlFree(name); + return; + } + /* namespace lookup */ + ns = xmlSearchNs (inst->doc, inst, prefix); + if (ns == NULL) { + xsltGenericError(xsltGenericErrorContext, + "func:function: undeclared prefix %s\n", + prefix); + xmlFree(name); + xmlFree(prefix); + return; + } + xmlFree(prefix); + + /* + * Create function data + */ + func = exsltFuncNewFunctionData(); + func->content = inst->children; + while (IS_XSLT_ELEM(func->content) && + IS_XSLT_NAME(func->content, "param")) { + func->content = func->content->next; + func->nargs++; + } + + xsltParseTemplateContent(style, inst); + + /* + * Register the function data such that it can be retrieved + * by exslFuncFunctionFunction + */ +#ifdef XSLT_REFACTORED + /* + * Ensure that the hash table will be stored in the *current* + * stylesheet level in order to correctly evaluate the + * import precedence. + */ + data = (xmlHashTablePtr) + xsltStyleStylesheetLevelGetExtData(style, + EXSLT_FUNCTIONS_NAMESPACE); +#else + data = (xmlHashTablePtr) + xsltStyleGetExtData (style, EXSLT_FUNCTIONS_NAMESPACE); +#endif + if (data == NULL) { + xsltGenericError(xsltGenericErrorContext, + "exsltFuncFunctionComp: no stylesheet data\n"); + xmlFree(name); + return; + } + + if (xmlHashAddEntry2 (data, ns->href, name, func) < 0) { + xsltTransformError(NULL, style, inst, + "Failed to register function {%s}%s\n", + ns->href, name); + style->errors++; + } else { + xsltGenericDebug(xsltGenericDebugContext, + "exsltFuncFunctionComp: register {%s}%s\n", + ns->href, name); + } + xmlFree(name); +} + +static xsltElemPreCompPtr +exsltFuncResultComp (xsltStylesheetPtr style, xmlNodePtr inst, + xsltTransformFunction function) { + xmlNodePtr test; + xmlChar *sel; + exsltFuncResultPreComp *ret; + + /* + * "Validity" checking + */ + /* it is an error to have any following sibling elements aside + * from the xsl:fallback element. + */ + for (test = inst->next; test != NULL; test = test->next) { + if (test->type != XML_ELEMENT_NODE) + continue; + if (IS_XSLT_ELEM(test) && IS_XSLT_NAME(test, "fallback")) + continue; + xsltGenericError(xsltGenericErrorContext, + "exsltFuncResultElem: only xsl:fallback is " + "allowed to follow func:result\n"); + return (NULL); + } + /* it is an error for a func:result element to not be a descendant + * of func:function. + * it is an error if a func:result occurs within a func:result + * element. + * it is an error if instanciating the content of a variable + * binding element (i.e. xsl:variable, xsl:param) results in the + * instanciation of a func:result element. + */ + for (test = inst->parent; test != NULL; test = test->parent) { + if ((test->ns != NULL) && + (xmlStrEqual(test->ns->href, EXSLT_FUNCTIONS_NAMESPACE))) { + if (xmlStrEqual(test->name, (const xmlChar *) "function")) { + break; + } + if (xmlStrEqual(test->name, (const xmlChar *) "result")) { + xsltGenericError(xsltGenericErrorContext, + "func:result element not allowed within" + " another func:result element\n"); + return (NULL); + } + } + if (IS_XSLT_ELEM(test) && + (IS_XSLT_NAME(test, "variable") || + IS_XSLT_NAME(test, "param"))) { + xsltGenericError(xsltGenericErrorContext, + "func:result element not allowed within" + " a variable binding element\n"); + return (NULL); + } + } + + /* + * Precomputation + */ + ret = (exsltFuncResultPreComp *) + xmlMalloc (sizeof(exsltFuncResultPreComp)); + if (ret == NULL) { + xsltPrintErrorContext(NULL, NULL, NULL); + xsltGenericError(xsltGenericErrorContext, + "exsltFuncResultComp : malloc failed\n"); + return (NULL); + } + memset(ret, 0, sizeof(exsltFuncResultPreComp)); + + xsltInitElemPreComp ((xsltElemPreCompPtr) ret, style, inst, function, + (xsltElemPreCompDeallocator) exsltFreeFuncResultPreComp); + ret->select = NULL; + + /* + * Precompute the select attribute + */ + sel = xmlGetNsProp(inst, (const xmlChar *) "select", NULL); + if (sel != NULL) { + ret->select = xmlXPathCompile (sel); + xmlFree(sel); + } + /* + * Precompute the namespace list + */ + ret->nsList = xmlGetNsList(inst->doc, inst); + if (ret->nsList != NULL) { + int i = 0; + while (ret->nsList[i] != NULL) + i++; + ret->nsNr = i; + } + return ((xsltElemPreCompPtr) ret); +} + +static void +exsltFuncResultElem (xsltTransformContextPtr ctxt, + xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst, + exsltFuncResultPreComp *comp) { + exsltFuncData *data; + xmlXPathObjectPtr ret; + + + /* It is an error if instantiating the content of the + * func:function element results in the instantiation of more than + * one func:result elements. + */ + data = (exsltFuncData *) xsltGetExtData (ctxt, EXSLT_FUNCTIONS_NAMESPACE); + if (data == NULL) { + xsltGenericError(xsltGenericErrorContext, + "exsltFuncReturnElem: data == NULL\n"); + return; + } + if (data->result != NULL) { + xsltGenericError(xsltGenericErrorContext, + "func:result already instanciated\n"); + data->error = 1; + return; + } + /* + * Processing + */ + if (comp->select != NULL) { + xmlNsPtr *oldXPNsList; + int oldXPNsNr; + xmlNodePtr oldXPContextNode; + /* If the func:result element has a select attribute, then the + * value of the attribute must be an expression and the + * returned value is the object that results from evaluating + * the expression. In this case, the content must be empty. + */ + if (inst->children != NULL) { + xsltGenericError(xsltGenericErrorContext, + "func:result content must be empty if it" + " has a select attribute\n"); + data->error = 1; + return; + } + oldXPNsList = ctxt->xpathCtxt->namespaces; + oldXPNsNr = ctxt->xpathCtxt->nsNr; + oldXPContextNode = ctxt->xpathCtxt->node; + + ctxt->xpathCtxt->namespaces = comp->nsList; + ctxt->xpathCtxt->nsNr = comp->nsNr; + + ret = xmlXPathCompiledEval(comp->select, ctxt->xpathCtxt); + + ctxt->xpathCtxt->node = oldXPContextNode; + ctxt->xpathCtxt->nsNr = oldXPNsNr; + ctxt->xpathCtxt->namespaces = oldXPNsList; + + if (ret == NULL) { + xsltGenericError(xsltGenericErrorContext, + "exsltFuncResultElem: ret == NULL\n"); + return; + } + /* + * Mark it as a function result in order to avoid garbage + * collecting of tree fragments before the function exits. + */ + xsltExtensionInstructionResultRegister(ctxt, ret); + } else if (inst->children != NULL) { + /* If the func:result element does not have a select attribute + * and has non-empty content (i.e. the func:result element has + * one or more child nodes), then the content of the + * func:result element specifies the value. + */ + xmlNodePtr oldInsert; + xmlDocPtr container; + + container = xsltCreateRVT(ctxt); + if (container == NULL) { + xsltGenericError(xsltGenericErrorContext, + "exsltFuncResultElem: out of memory\n"); + data->error = 1; + return; + } + xsltRegisterLocalRVT(ctxt, container); + + oldInsert = ctxt->insert; + ctxt->insert = (xmlNodePtr) container; + xsltApplyOneTemplate (ctxt, ctxt->xpathCtxt->node, + inst->children, NULL, NULL); + ctxt->insert = oldInsert; + + ret = xmlXPathNewValueTree((xmlNodePtr) container); + if (ret == NULL) { + xsltGenericError(xsltGenericErrorContext, + "exsltFuncResultElem: ret == NULL\n"); + data->error = 1; + } else { + ret->boolval = 0; /* Freeing is not handled there anymore */ + /* + * Mark it as a function result in order to avoid garbage + * collecting of tree fragments before the function exits. + */ + xsltExtensionInstructionResultRegister(ctxt, ret); + } + } else { + /* If the func:result element has empty content and does not + * have a select attribute, then the returned value is an + * empty string. + */ + ret = xmlXPathNewCString(""); + } + data->result = ret; +} + +/** + * exsltFuncRegister: + * + * Registers the EXSLT - Functions module + */ +void +exsltFuncRegister (void) { + xsltRegisterExtModuleFull (EXSLT_FUNCTIONS_NAMESPACE, + (xsltExtInitFunction) exsltFuncInit, + (xsltExtShutdownFunction) exsltFuncShutdown, + (xsltStyleExtInitFunction) exsltFuncStyleInit, + (xsltStyleExtShutdownFunction) exsltFuncStyleShutdown); + + xsltRegisterExtModuleTopLevel ((const xmlChar *) "function", + EXSLT_FUNCTIONS_NAMESPACE, + exsltFuncFunctionComp); + xsltRegisterExtModuleElement ((const xmlChar *) "result", + EXSLT_FUNCTIONS_NAMESPACE, + (xsltPreComputeFunction)exsltFuncResultComp, + (xsltTransformFunction) exsltFuncResultElem); +} diff --git a/libexslt/strings.c b/libexslt/strings.c index 6ad6d6ce..484310a8 100644 --- a/libexslt/strings.c +++ b/libexslt/strings.c @@ -1,525 +1,526 @@ -#define IN_LIBEXSLT -#include "libexslt/libexslt.h" - -#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__) -#include -#else -#include "config.h" -#endif - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "exslt.h" - -/** - * exsltStrTokenizeFunction: - * @ctxt: an XPath parser context - * @nargs: the number of arguments - * - * Splits up a string on the characters of the delimiter string and returns a - * node set of token elements, each containing one token from the string. - */ -static void -exsltStrTokenizeFunction(xmlXPathParserContextPtr ctxt, int nargs) -{ - xsltTransformContextPtr tctxt; - xmlChar *str, *delimiters, *cur; - const xmlChar *token, *delimiter; - xmlNodePtr node; - xmlDocPtr container; - xmlXPathObjectPtr ret = NULL; - int clen; - - if ((nargs < 1) || (nargs > 2)) { - xmlXPathSetArityError(ctxt); - return; - } - - if (nargs == 2) { - delimiters = xmlXPathPopString(ctxt); - if (xmlXPathCheckError(ctxt)) - return; - } else { - delimiters = xmlStrdup((const xmlChar *) "\t\r\n "); - } - if (delimiters == NULL) - return; - - str = xmlXPathPopString(ctxt); - if (xmlXPathCheckError(ctxt) || (str == NULL)) { - xmlFree(delimiters); - return; - } - - /* Return a result tree fragment */ - tctxt = xsltXPathGetTransformContext(ctxt); - if (tctxt == NULL) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "exslt:tokenize : internal error tctxt == NULL\n"); - goto fail; - } - - container = xsltCreateRVT(tctxt); - if (container != NULL) { - xsltRegisterTmpRVT(tctxt, container); - ret = xmlXPathNewNodeSet(NULL); - if (ret != NULL) { - ret->boolval = 0; /* Freeing is not handled there anymore */ - for (cur = str, token = str; *cur != 0; cur += clen) { - clen = xmlUTF8Size(cur); - if (*delimiters == 0) { /* empty string case */ - xmlChar ctmp; - ctmp = *(cur+clen); - *(cur+clen) = 0; - node = xmlNewDocRawNode(container, NULL, - (const xmlChar *) "token", cur); - xmlAddChild((xmlNodePtr) container, node); - xmlXPathNodeSetAddUnique(ret->nodesetval, node); - *(cur+clen) = ctmp; /* restore the changed byte */ - token = cur + clen; - } else for (delimiter = delimiters; *delimiter != 0; - delimiter += xmlUTF8Size(delimiter)) { - if (!xmlUTF8Charcmp(cur, delimiter)) { - if (cur == token) { - /* discard empty tokens */ - token = cur + clen; - break; - } - *cur = 0; /* terminate the token */ - node = xmlNewDocRawNode(container, NULL, - (const xmlChar *) "token", token); - xmlAddChild((xmlNodePtr) container, node); - xmlXPathNodeSetAddUnique(ret->nodesetval, node); - *cur = *delimiter; /* restore the changed byte */ - token = cur + clen; - break; - } - } - } - if (token != cur) { - node = xmlNewDocRawNode(container, NULL, - (const xmlChar *) "token", token); - xmlAddChild((xmlNodePtr) container, node); - xmlXPathNodeSetAddUnique(ret->nodesetval, node); - } - } - } - -fail: - if (str != NULL) - xmlFree(str); - if (delimiters != NULL) - xmlFree(delimiters); - if (ret != NULL) - valuePush(ctxt, ret); - else - valuePush(ctxt, xmlXPathNewNodeSet(NULL)); -} - -/** - * exsltStrSplitFunction: - * @ctxt: an XPath parser context - * @nargs: the number of arguments - * - * Splits up a string on a delimiting string and returns a node set of token - * elements, each containing one token from the string. - */ -static void -exsltStrSplitFunction(xmlXPathParserContextPtr ctxt, int nargs) { - xsltTransformContextPtr tctxt; - xmlChar *str, *delimiter, *cur; - const xmlChar *token; - xmlNodePtr node; - xmlDocPtr container; - xmlXPathObjectPtr ret = NULL; - int delimiterLength; - - if ((nargs < 1) || (nargs > 2)) { - xmlXPathSetArityError(ctxt); - return; - } - - if (nargs == 2) { - delimiter = xmlXPathPopString(ctxt); - if (xmlXPathCheckError(ctxt)) - return; - } else { - delimiter = xmlStrdup((const xmlChar *) " "); - } - if (delimiter == NULL) - return; - delimiterLength = xmlStrlen (delimiter); - - str = xmlXPathPopString(ctxt); - if (xmlXPathCheckError(ctxt) || (str == NULL)) { - xmlFree(delimiter); - return; - } - - /* Return a result tree fragment */ - tctxt = xsltXPathGetTransformContext(ctxt); - if (tctxt == NULL) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "exslt:tokenize : internal error tctxt == NULL\n"); - goto fail; - } - - container = xsltCreateRVT(tctxt); - if (container != NULL) { - xsltRegisterTmpRVT(tctxt, container); - ret = xmlXPathNewNodeSet(NULL); - if (ret != NULL) { - ret->boolval = 0; /* Freeing is not handled there anymore */ - for (cur = str, token = str; *cur != 0; cur++) { - if (delimiterLength == 0) { - if (cur != token) { - xmlChar tmp = *cur; - *cur = 0; - node = xmlNewDocRawNode(container, NULL, - (const xmlChar *) "token", token); - xmlAddChild((xmlNodePtr) container, node); - xmlXPathNodeSetAddUnique(ret->nodesetval, node); - *cur = tmp; - token++; - } - } - else if (!xmlStrncasecmp(cur, delimiter, delimiterLength)) { - if (cur == token) { - /* discard empty tokens */ - cur = cur + delimiterLength - 1; - token = cur + 1; - continue; - } - *cur = 0; - node = xmlNewDocRawNode(container, NULL, - (const xmlChar *) "token", token); - xmlAddChild((xmlNodePtr) container, node); - xmlXPathNodeSetAddUnique(ret->nodesetval, node); - *cur = *delimiter; - cur = cur + delimiterLength - 1; - token = cur + 1; - } - } - if (token != cur) { - node = xmlNewDocRawNode(container, NULL, - (const xmlChar *) "token", token); - xmlAddChild((xmlNodePtr) container, node); - xmlXPathNodeSetAddUnique(ret->nodesetval, node); - } - } - } - -fail: - if (str != NULL) - xmlFree(str); - if (delimiter != NULL) - xmlFree(delimiter); - if (ret != NULL) - valuePush(ctxt, ret); - else - valuePush(ctxt, xmlXPathNewNodeSet(NULL)); -} - -/** - * exsltStrEncodeUriFunction: - * @ctxt: an XPath parser context - * @nargs: the number of arguments - * - * URI-Escapes a string - */ -static void -exsltStrEncodeUriFunction (xmlXPathParserContextPtr ctxt, int nargs) { - int escape_all = 1, str_len = 0; - xmlChar *str = NULL, *ret = NULL, *tmp; - - if ((nargs < 2) || (nargs > 3)) { - xmlXPathSetArityError(ctxt); - return; - } - - if (nargs >= 3) { - /* check for UTF-8 if encoding was explicitly given; - we don't support anything else yet */ - tmp = xmlXPathPopString(ctxt); - if (xmlUTF8Strlen(tmp) != 5 || xmlStrcmp((const xmlChar *)"UTF-8",tmp)) { - xmlXPathReturnEmptyString(ctxt); - xmlFree(tmp); - return; - } - xmlFree(tmp); - } - - escape_all = xmlXPathPopBoolean(ctxt); - - str = xmlXPathPopString(ctxt); - str_len = xmlUTF8Strlen(str); - - if (str_len == 0) { - xmlXPathReturnEmptyString(ctxt); - xmlFree(str); - return; - } - - ret = xmlURIEscapeStr(str,(const xmlChar *)(escape_all?"-_.!~*'()":"-_.!~*'();/?:@&=+$,[]")); - xmlXPathReturnString(ctxt, ret); - - if (str != NULL) - xmlFree(str); -} - -/** - * exsltStrDecodeUriFunction: - * @ctxt: an XPath parser context - * @nargs: the number of arguments - * - * reverses URI-Escaping of a string - */ -static void -exsltStrDecodeUriFunction (xmlXPathParserContextPtr ctxt, int nargs) { - int str_len = 0; - xmlChar *str = NULL, *ret = NULL, *tmp; - - if ((nargs < 1) || (nargs > 2)) { - xmlXPathSetArityError(ctxt); - return; - } - - if (nargs >= 2) { - /* check for UTF-8 if encoding was explicitly given; - we don't support anything else yet */ - tmp = xmlXPathPopString(ctxt); - if (xmlUTF8Strlen(tmp) != 5 || xmlStrcmp((const xmlChar *)"UTF-8",tmp)) { - xmlXPathReturnEmptyString(ctxt); - xmlFree(tmp); - return; - } - xmlFree(tmp); - } - - str = xmlXPathPopString(ctxt); - str_len = xmlUTF8Strlen(str); - - if (str_len == 0) { - xmlXPathReturnEmptyString(ctxt); - xmlFree(str); - return; - } - - ret = (xmlChar *) xmlURIUnescapeString((const char *)str,0,NULL); - if (!xmlCheckUTF8(ret)) { - /* FIXME: instead of throwing away the whole URI, we should - only discard the invalid sequence(s). How to do that? */ - xmlXPathReturnEmptyString(ctxt); - xmlFree(str); - xmlFree(ret); - return; - } - - xmlXPathReturnString(ctxt, ret); - - if (str != NULL) - xmlFree(str); -} - -/** - * exsltStrPaddingFunction: - * @ctxt: an XPath parser context - * @nargs: the number of arguments - * - * Creates a padding string of a certain length. - */ -static void -exsltStrPaddingFunction (xmlXPathParserContextPtr ctxt, int nargs) { - int number, str_len = 0; - xmlChar *str = NULL, *ret = NULL, *tmp; - - if ((nargs < 1) || (nargs > 2)) { - xmlXPathSetArityError(ctxt); - return; - } - - if (nargs == 2) { - str = xmlXPathPopString(ctxt); - str_len = xmlUTF8Strlen(str); - } - if (str_len == 0) { - if (str != NULL) xmlFree(str); - str = xmlStrdup((const xmlChar *) " "); - str_len = 1; - } - - number = (int) xmlXPathPopNumber(ctxt); - - if (number <= 0) { - xmlXPathReturnEmptyString(ctxt); - xmlFree(str); - return; - } - - while (number >= str_len) { - ret = xmlStrncat(ret, str, str_len); - number -= str_len; - } - tmp = xmlUTF8Strndup (str, number); - ret = xmlStrcat(ret, tmp); - if (tmp != NULL) - xmlFree (tmp); - - xmlXPathReturnString(ctxt, ret); - - if (str != NULL) - xmlFree(str); -} - -/** - * exsltStrAlignFunction: - * @ctxt: an XPath parser context - * @nargs: the number of arguments - * - * Aligns a string within another string. - */ -static void -exsltStrAlignFunction (xmlXPathParserContextPtr ctxt, int nargs) { - xmlChar *str, *padding, *alignment, *ret; - int str_l, padding_l; - - if ((nargs < 2) || (nargs > 3)) { - xmlXPathSetArityError(ctxt); - return; - } - - if (nargs == 3) - alignment = xmlXPathPopString(ctxt); - else - alignment = NULL; - - padding = xmlXPathPopString(ctxt); - str = xmlXPathPopString(ctxt); - - str_l = xmlUTF8Strlen (str); - padding_l = xmlUTF8Strlen (padding); - - if (str_l == padding_l) { - xmlXPathReturnString (ctxt, str); - xmlFree(padding); - xmlFree(alignment); - return; - } - - if (str_l > padding_l) { - ret = xmlUTF8Strndup (str, padding_l); - } else { - if (xmlStrEqual(alignment, (const xmlChar *) "right")) { - ret = xmlUTF8Strndup (padding, padding_l - str_l); - ret = xmlStrcat (ret, str); - } else if (xmlStrEqual(alignment, (const xmlChar *) "center")) { - int left = (padding_l - str_l) / 2; - int right_start; - - ret = xmlUTF8Strndup (padding, left); - ret = xmlStrcat (ret, str); - - right_start = xmlUTF8Strsize (padding, left + str_l); - ret = xmlStrcat (ret, padding + right_start); - } else { - int str_s; - - str_s = xmlStrlen (str); - ret = xmlStrdup (str); - ret = xmlStrcat (ret, padding + str_s); - } - } - - xmlXPathReturnString (ctxt, ret); - - xmlFree(str); - xmlFree(padding); - xmlFree(alignment); -} - -/** - * exsltStrConcatFunction: - * @ctxt: an XPath parser context - * @nargs: the number of arguments - * - * Takes a node set and returns the concatenation of the string values - * of the nodes in that node set. If the node set is empty, it - * returns an empty string. - */ -static void -exsltStrConcatFunction (xmlXPathParserContextPtr ctxt, int nargs) { - xmlXPathObjectPtr obj; - xmlChar *ret = NULL; - int i; - - if (nargs != 1) { - xmlXPathSetArityError(ctxt); - return; - } - - if (!xmlXPathStackIsNodeSet(ctxt)) { - xmlXPathSetTypeError(ctxt); - return; - } - - obj = valuePop (ctxt); - - if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) { - xmlXPathReturnEmptyString(ctxt); - return; - } - - for (i = 0; i < obj->nodesetval->nodeNr; i++) { - xmlChar *tmp; - tmp = xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]); - - ret = xmlStrcat (ret, tmp); - - xmlFree(tmp); - } - - xmlXPathFreeObject (obj); - - xmlXPathReturnString(ctxt, ret); -} - -/** - * exsltStrRegister: - * - * Registers the EXSLT - Strings module - */ - -void -exsltStrRegister (void) { - xsltRegisterExtModuleFunction ((const xmlChar *) "tokenize", - EXSLT_STRINGS_NAMESPACE, - exsltStrTokenizeFunction); - xsltRegisterExtModuleFunction ((const xmlChar *) "split", - EXSLT_STRINGS_NAMESPACE, - exsltStrSplitFunction); - xsltRegisterExtModuleFunction ((const xmlChar *) "encode-uri", - EXSLT_STRINGS_NAMESPACE, - exsltStrEncodeUriFunction); - xsltRegisterExtModuleFunction ((const xmlChar *) "decode-uri", - EXSLT_STRINGS_NAMESPACE, - exsltStrDecodeUriFunction); - xsltRegisterExtModuleFunction ((const xmlChar *) "padding", - EXSLT_STRINGS_NAMESPACE, - exsltStrPaddingFunction); - xsltRegisterExtModuleFunction ((const xmlChar *) "align", - EXSLT_STRINGS_NAMESPACE, - exsltStrAlignFunction); - xsltRegisterExtModuleFunction ((const xmlChar *) "concat", - EXSLT_STRINGS_NAMESPACE, - exsltStrConcatFunction); -} +#define IN_LIBEXSLT +#include "libexslt/libexslt.h" + +#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__) +#include +#else +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "exslt.h" + +/** + * exsltStrTokenizeFunction: + * @ctxt: an XPath parser context + * @nargs: the number of arguments + * + * Splits up a string on the characters of the delimiter string and returns a + * node set of token elements, each containing one token from the string. + */ +static void +exsltStrTokenizeFunction(xmlXPathParserContextPtr ctxt, int nargs) +{ + xsltTransformContextPtr tctxt; + xmlChar *str, *delimiters, *cur; + const xmlChar *token, *delimiter; + xmlNodePtr node; + xmlDocPtr container; + xmlXPathObjectPtr ret = NULL; + int clen; + + if ((nargs < 1) || (nargs > 2)) { + xmlXPathSetArityError(ctxt); + return; + } + + if (nargs == 2) { + delimiters = xmlXPathPopString(ctxt); + if (xmlXPathCheckError(ctxt)) + return; + } else { + delimiters = xmlStrdup((const xmlChar *) "\t\r\n "); + } + if (delimiters == NULL) + return; + + str = xmlXPathPopString(ctxt); + if (xmlXPathCheckError(ctxt) || (str == NULL)) { + xmlFree(delimiters); + return; + } + + /* Return a result tree fragment */ + tctxt = xsltXPathGetTransformContext(ctxt); + if (tctxt == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "exslt:tokenize : internal error tctxt == NULL\n"); + goto fail; + } + + container = xsltCreateRVT(tctxt); + if (container != NULL) { + xsltRegisterLocalRVT(tctxt, container); + ret = xmlXPathNewNodeSet(NULL); + if (ret != NULL) { + for (cur = str, token = str; *cur != 0; cur += clen) { + clen = xmlUTF8Size(cur); + if (*delimiters == 0) { /* empty string case */ + xmlChar ctmp; + ctmp = *(cur+clen); + *(cur+clen) = 0; + node = xmlNewDocRawNode(container, NULL, + (const xmlChar *) "token", cur); + xmlAddChild((xmlNodePtr) container, node); + xmlXPathNodeSetAddUnique(ret->nodesetval, node); + *(cur+clen) = ctmp; /* restore the changed byte */ + token = cur + clen; + } else for (delimiter = delimiters; *delimiter != 0; + delimiter += xmlUTF8Size(delimiter)) { + if (!xmlUTF8Charcmp(cur, delimiter)) { + if (cur == token) { + /* discard empty tokens */ + token = cur + clen; + break; + } + *cur = 0; /* terminate the token */ + node = xmlNewDocRawNode(container, NULL, + (const xmlChar *) "token", token); + xmlAddChild((xmlNodePtr) container, node); + xmlXPathNodeSetAddUnique(ret->nodesetval, node); + *cur = *delimiter; /* restore the changed byte */ + token = cur + clen; + break; + } + } + } + if (token != cur) { + node = xmlNewDocRawNode(container, NULL, + (const xmlChar *) "token", token); + xmlAddChild((xmlNodePtr) container, node); + xmlXPathNodeSetAddUnique(ret->nodesetval, node); + } + } + } + +fail: + if (str != NULL) + xmlFree(str); + if (delimiters != NULL) + xmlFree(delimiters); + if (ret != NULL) + valuePush(ctxt, ret); + else + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); +} + +/** + * exsltStrSplitFunction: + * @ctxt: an XPath parser context + * @nargs: the number of arguments + * + * Splits up a string on a delimiting string and returns a node set of token + * elements, each containing one token from the string. + */ +static void +exsltStrSplitFunction(xmlXPathParserContextPtr ctxt, int nargs) { + xsltTransformContextPtr tctxt; + xmlChar *str, *delimiter, *cur; + const xmlChar *token; + xmlNodePtr node; + xmlDocPtr container; + xmlXPathObjectPtr ret = NULL; + int delimiterLength; + + if ((nargs < 1) || (nargs > 2)) { + xmlXPathSetArityError(ctxt); + return; + } + + if (nargs == 2) { + delimiter = xmlXPathPopString(ctxt); + if (xmlXPathCheckError(ctxt)) + return; + } else { + delimiter = xmlStrdup((const xmlChar *) " "); + } + if (delimiter == NULL) + return; + delimiterLength = xmlStrlen (delimiter); + + str = xmlXPathPopString(ctxt); + if (xmlXPathCheckError(ctxt) || (str == NULL)) { + xmlFree(delimiter); + return; + } + + /* Return a result tree fragment */ + tctxt = xsltXPathGetTransformContext(ctxt); + if (tctxt == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "exslt:tokenize : internal error tctxt == NULL\n"); + goto fail; + } + + /* + * OPTIMIZE TODO: We are creating an xmlDoc for every split! + */ + container = xsltCreateRVT(tctxt); + if (container != NULL) { + xsltRegisterLocalRVT(tctxt, container); + ret = xmlXPathNewNodeSet(NULL); + if (ret != NULL) { + for (cur = str, token = str; *cur != 0; cur++) { + if (delimiterLength == 0) { + if (cur != token) { + xmlChar tmp = *cur; + *cur = 0; + node = xmlNewDocRawNode(container, NULL, + (const xmlChar *) "token", token); + xmlAddChild((xmlNodePtr) container, node); + xmlXPathNodeSetAddUnique(ret->nodesetval, node); + *cur = tmp; + token++; + } + } + else if (!xmlStrncasecmp(cur, delimiter, delimiterLength)) { + if (cur == token) { + /* discard empty tokens */ + cur = cur + delimiterLength - 1; + token = cur + 1; + continue; + } + *cur = 0; + node = xmlNewDocRawNode(container, NULL, + (const xmlChar *) "token", token); + xmlAddChild((xmlNodePtr) container, node); + xmlXPathNodeSetAddUnique(ret->nodesetval, node); + *cur = *delimiter; + cur = cur + delimiterLength - 1; + token = cur + 1; + } + } + if (token != cur) { + node = xmlNewDocRawNode(container, NULL, + (const xmlChar *) "token", token); + xmlAddChild((xmlNodePtr) container, node); + xmlXPathNodeSetAddUnique(ret->nodesetval, node); + } + } + } + +fail: + if (str != NULL) + xmlFree(str); + if (delimiter != NULL) + xmlFree(delimiter); + if (ret != NULL) + valuePush(ctxt, ret); + else + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); +} + +/** + * exsltStrEncodeUriFunction: + * @ctxt: an XPath parser context + * @nargs: the number of arguments + * + * URI-Escapes a string + */ +static void +exsltStrEncodeUriFunction (xmlXPathParserContextPtr ctxt, int nargs) { + int escape_all = 1, str_len = 0; + xmlChar *str = NULL, *ret = NULL, *tmp; + + if ((nargs < 2) || (nargs > 3)) { + xmlXPathSetArityError(ctxt); + return; + } + + if (nargs >= 3) { + /* check for UTF-8 if encoding was explicitly given; + we don't support anything else yet */ + tmp = xmlXPathPopString(ctxt); + if (xmlUTF8Strlen(tmp) != 5 || xmlStrcmp((const xmlChar *)"UTF-8",tmp)) { + xmlXPathReturnEmptyString(ctxt); + xmlFree(tmp); + return; + } + xmlFree(tmp); + } + + escape_all = xmlXPathPopBoolean(ctxt); + + str = xmlXPathPopString(ctxt); + str_len = xmlUTF8Strlen(str); + + if (str_len == 0) { + xmlXPathReturnEmptyString(ctxt); + xmlFree(str); + return; + } + + ret = xmlURIEscapeStr(str,(const xmlChar *)(escape_all?"-_.!~*'()":"-_.!~*'();/?:@&=+$,[]")); + xmlXPathReturnString(ctxt, ret); + + if (str != NULL) + xmlFree(str); +} + +/** + * exsltStrDecodeUriFunction: + * @ctxt: an XPath parser context + * @nargs: the number of arguments + * + * reverses URI-Escaping of a string + */ +static void +exsltStrDecodeUriFunction (xmlXPathParserContextPtr ctxt, int nargs) { + int str_len = 0; + xmlChar *str = NULL, *ret = NULL, *tmp; + + if ((nargs < 1) || (nargs > 2)) { + xmlXPathSetArityError(ctxt); + return; + } + + if (nargs >= 2) { + /* check for UTF-8 if encoding was explicitly given; + we don't support anything else yet */ + tmp = xmlXPathPopString(ctxt); + if (xmlUTF8Strlen(tmp) != 5 || xmlStrcmp((const xmlChar *)"UTF-8",tmp)) { + xmlXPathReturnEmptyString(ctxt); + xmlFree(tmp); + return; + } + xmlFree(tmp); + } + + str = xmlXPathPopString(ctxt); + str_len = xmlUTF8Strlen(str); + + if (str_len == 0) { + xmlXPathReturnEmptyString(ctxt); + xmlFree(str); + return; + } + + ret = (xmlChar *) xmlURIUnescapeString((const char *)str,0,NULL); + if (!xmlCheckUTF8(ret)) { + /* FIXME: instead of throwing away the whole URI, we should + only discard the invalid sequence(s). How to do that? */ + xmlXPathReturnEmptyString(ctxt); + xmlFree(str); + xmlFree(ret); + return; + } + + xmlXPathReturnString(ctxt, ret); + + if (str != NULL) + xmlFree(str); +} + +/** + * exsltStrPaddingFunction: + * @ctxt: an XPath parser context + * @nargs: the number of arguments + * + * Creates a padding string of a certain length. + */ +static void +exsltStrPaddingFunction (xmlXPathParserContextPtr ctxt, int nargs) { + int number, str_len = 0; + xmlChar *str = NULL, *ret = NULL, *tmp; + + if ((nargs < 1) || (nargs > 2)) { + xmlXPathSetArityError(ctxt); + return; + } + + if (nargs == 2) { + str = xmlXPathPopString(ctxt); + str_len = xmlUTF8Strlen(str); + } + if (str_len == 0) { + if (str != NULL) xmlFree(str); + str = xmlStrdup((const xmlChar *) " "); + str_len = 1; + } + + number = (int) xmlXPathPopNumber(ctxt); + + if (number <= 0) { + xmlXPathReturnEmptyString(ctxt); + xmlFree(str); + return; + } + + while (number >= str_len) { + ret = xmlStrncat(ret, str, str_len); + number -= str_len; + } + tmp = xmlUTF8Strndup (str, number); + ret = xmlStrcat(ret, tmp); + if (tmp != NULL) + xmlFree (tmp); + + xmlXPathReturnString(ctxt, ret); + + if (str != NULL) + xmlFree(str); +} + +/** + * exsltStrAlignFunction: + * @ctxt: an XPath parser context + * @nargs: the number of arguments + * + * Aligns a string within another string. + */ +static void +exsltStrAlignFunction (xmlXPathParserContextPtr ctxt, int nargs) { + xmlChar *str, *padding, *alignment, *ret; + int str_l, padding_l; + + if ((nargs < 2) || (nargs > 3)) { + xmlXPathSetArityError(ctxt); + return; + } + + if (nargs == 3) + alignment = xmlXPathPopString(ctxt); + else + alignment = NULL; + + padding = xmlXPathPopString(ctxt); + str = xmlXPathPopString(ctxt); + + str_l = xmlUTF8Strlen (str); + padding_l = xmlUTF8Strlen (padding); + + if (str_l == padding_l) { + xmlXPathReturnString (ctxt, str); + xmlFree(padding); + xmlFree(alignment); + return; + } + + if (str_l > padding_l) { + ret = xmlUTF8Strndup (str, padding_l); + } else { + if (xmlStrEqual(alignment, (const xmlChar *) "right")) { + ret = xmlUTF8Strndup (padding, padding_l - str_l); + ret = xmlStrcat (ret, str); + } else if (xmlStrEqual(alignment, (const xmlChar *) "center")) { + int left = (padding_l - str_l) / 2; + int right_start; + + ret = xmlUTF8Strndup (padding, left); + ret = xmlStrcat (ret, str); + + right_start = xmlUTF8Strsize (padding, left + str_l); + ret = xmlStrcat (ret, padding + right_start); + } else { + int str_s; + + str_s = xmlStrlen (str); + ret = xmlStrdup (str); + ret = xmlStrcat (ret, padding + str_s); + } + } + + xmlXPathReturnString (ctxt, ret); + + xmlFree(str); + xmlFree(padding); + xmlFree(alignment); +} + +/** + * exsltStrConcatFunction: + * @ctxt: an XPath parser context + * @nargs: the number of arguments + * + * Takes a node set and returns the concatenation of the string values + * of the nodes in that node set. If the node set is empty, it + * returns an empty string. + */ +static void +exsltStrConcatFunction (xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr obj; + xmlChar *ret = NULL; + int i; + + if (nargs != 1) { + xmlXPathSetArityError(ctxt); + return; + } + + if (!xmlXPathStackIsNodeSet(ctxt)) { + xmlXPathSetTypeError(ctxt); + return; + } + + obj = valuePop (ctxt); + + if (xmlXPathNodeSetIsEmpty(obj->nodesetval)) { + xmlXPathReturnEmptyString(ctxt); + return; + } + + for (i = 0; i < obj->nodesetval->nodeNr; i++) { + xmlChar *tmp; + tmp = xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]); + + ret = xmlStrcat (ret, tmp); + + xmlFree(tmp); + } + + xmlXPathFreeObject (obj); + + xmlXPathReturnString(ctxt, ret); +} + +/** + * exsltStrRegister: + * + * Registers the EXSLT - Strings module + */ + +void +exsltStrRegister (void) { + xsltRegisterExtModuleFunction ((const xmlChar *) "tokenize", + EXSLT_STRINGS_NAMESPACE, + exsltStrTokenizeFunction); + xsltRegisterExtModuleFunction ((const xmlChar *) "split", + EXSLT_STRINGS_NAMESPACE, + exsltStrSplitFunction); + xsltRegisterExtModuleFunction ((const xmlChar *) "encode-uri", + EXSLT_STRINGS_NAMESPACE, + exsltStrEncodeUriFunction); + xsltRegisterExtModuleFunction ((const xmlChar *) "decode-uri", + EXSLT_STRINGS_NAMESPACE, + exsltStrDecodeUriFunction); + xsltRegisterExtModuleFunction ((const xmlChar *) "padding", + EXSLT_STRINGS_NAMESPACE, + exsltStrPaddingFunction); + xsltRegisterExtModuleFunction ((const xmlChar *) "align", + EXSLT_STRINGS_NAMESPACE, + exsltStrAlignFunction); + xsltRegisterExtModuleFunction ((const xmlChar *) "concat", + EXSLT_STRINGS_NAMESPACE, + exsltStrConcatFunction); +} diff --git a/libxslt/attributes.c b/libxslt/attributes.c index 46e365c3..795e7978 100644 --- a/libxslt/attributes.c +++ b/libxslt/attributes.c @@ -1,1130 +1,1132 @@ -/* - * attributes.c: Implementation of the XSLT attributes handling - * - * Reference: - * http://www.w3.org/TR/1999/REC-xslt-19991116 - * - * See Copyright for the status of this software. - * - * daniel@veillard.com - */ - -#define IN_LIBXSLT -#include "libxslt.h" - -#include - -#ifdef HAVE_SYS_TYPES_H -#include -#endif -#ifdef HAVE_MATH_H -#include -#endif -#ifdef HAVE_FLOAT_H -#include -#endif -#ifdef HAVE_IEEEFP_H -#include -#endif -#ifdef HAVE_NAN_H -#include -#endif -#ifdef HAVE_CTYPE_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include "xslt.h" -#include "xsltInternals.h" -#include "xsltutils.h" -#include "attributes.h" -#include "namespaces.h" -#include "templates.h" -#include "imports.h" -#include "transform.h" -#include "preproc.h" - -#define WITH_XSLT_DEBUG_ATTRIBUTES -#ifdef WITH_XSLT_DEBUG -#define WITH_XSLT_DEBUG_ATTRIBUTES -#endif - -/* - * TODO: merge attribute sets from different import precedence. - * all this should be precomputed just before the transformation - * starts or at first hit with a cache in the context. - * The simple way for now would be to not allow redefinition of - * attributes once generated in the output tree, possibly costlier. - */ - -/* - * Useful macros - */ -#ifdef IS_BLANK -#undef IS_BLANK -#endif - -#define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \ - ((c) == 0x0D)) - -#define IS_BLANK_NODE(n) \ - (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content))) - - -/* - * The in-memory structure corresponding to an XSLT Attribute in - * an attribute set - */ - - -typedef struct _xsltAttrElem xsltAttrElem; -typedef xsltAttrElem *xsltAttrElemPtr; -struct _xsltAttrElem { - struct _xsltAttrElem *next;/* chained list */ - xmlNodePtr attr; /* the xsl:attribute definition */ - const xmlChar *set; /* or the attribute set */ - const xmlChar *ns; /* and its namespace */ -}; - -/************************************************************************ - * * - * XSLT Attribute handling * - * * - ************************************************************************/ - -/** - * xsltNewAttrElem: - * @attr: the new xsl:attribute node - * - * Create a new XSLT AttrElem - * - * Returns the newly allocated xsltAttrElemPtr or NULL in case of error - */ -static xsltAttrElemPtr -xsltNewAttrElem(xmlNodePtr attr) { - xsltAttrElemPtr cur; - - cur = (xsltAttrElemPtr) xmlMalloc(sizeof(xsltAttrElem)); - if (cur == NULL) { - xsltGenericError(xsltGenericErrorContext, - "xsltNewAttrElem : malloc failed\n"); - return(NULL); - } - memset(cur, 0, sizeof(xsltAttrElem)); - cur->attr = attr; - return(cur); -} - -/** - * xsltFreeAttrElem: - * @attr: an XSLT AttrElem - * - * Free up the memory allocated by @attr - */ -static void -xsltFreeAttrElem(xsltAttrElemPtr attr) { - xmlFree(attr); -} - -/** - * xsltFreeAttrElemList: - * @list: an XSLT AttrElem list - * - * Free up the memory allocated by @list - */ -static void -xsltFreeAttrElemList(xsltAttrElemPtr list) { - xsltAttrElemPtr next; - - while (list != NULL) { - next = list->next; - xsltFreeAttrElem(list); - list = next; - } -} - -#ifdef XSLT_REFACTORED - /* - * This was moved to xsltParseStylesheetAttributeSet(). - */ -#else -/** - * xsltAddAttrElemList: - * @list: an XSLT AttrElem list - * @attr: the new xsl:attribute node - * - * Add the new attribute to the list. - * - * Returns the new list pointer - */ -static xsltAttrElemPtr -xsltAddAttrElemList(xsltAttrElemPtr list, xmlNodePtr attr) { - xsltAttrElemPtr next, cur; - - if (attr == NULL) - return(list); - if (list == NULL) - return(xsltNewAttrElem(attr)); - cur = list; - while (cur != NULL) { - next = cur->next; - if (cur->attr == attr) - return(cur); - if (cur->next == NULL) { - cur->next = xsltNewAttrElem(attr); - return(list); - } - cur = next; - } - return(list); -} -#endif /* XSLT_REFACTORED */ - -/** - * xsltMergeAttrElemList: - * @list: an XSLT AttrElem list - * @old: another XSLT AttrElem list - * - * Add all the attributes from list @old to list @list, - * but drop redefinition of existing values. - * - * Returns the new list pointer - */ -static xsltAttrElemPtr -xsltMergeAttrElemList(xsltStylesheetPtr style, - xsltAttrElemPtr list, xsltAttrElemPtr old) { - xsltAttrElemPtr cur; - int add; - - while (old != NULL) { - if ((old->attr == NULL) && (old->set == NULL)) { - old = old->next; - continue; - } - /* - * Check that the attribute is not yet in the list - */ - cur = list; - add = 1; - while (cur != NULL) { - if ((cur->attr == NULL) && (cur->set == NULL)) { - if (cur->next == NULL) - break; - cur = cur->next; - continue; - } - if ((cur->set != NULL) && (cur->set == old->set)) { - add = 0; - break; - } - if (cur->set != NULL) { - if (cur->next == NULL) - break; - cur = cur->next; - continue; - } - if (old->set != NULL) { - if (cur->next == NULL) - break; - cur = cur->next; - continue; - } - if (cur->attr == old->attr) { - xsltGenericError(xsltGenericErrorContext, - "xsl:attribute-set : use-attribute-sets recursion detected\n"); - return(list); - } - if (cur->next == NULL) - break; - cur = cur->next; - } - - if (add == 1) { - /* - * Changed to use the string-dict, rather than duplicating - * @set and @ns; this fixes bug #340400. - */ - if (cur == NULL) { - list = xsltNewAttrElem(old->attr); - if (old->set != NULL) { - list->set = xmlDictLookup(style->dict, old->set, -1); - if (old->ns != NULL) - list->ns = xmlDictLookup(style->dict, old->ns, -1); - } - } else if (add) { - cur->next = xsltNewAttrElem(old->attr); - if (old->set != NULL) { - cur->next->set = xmlDictLookup(style->dict, old->set, -1); - if (old->ns != NULL) - cur->next->ns = xmlDictLookup(style->dict, old->ns, -1); - } - } - } - - old = old->next; - } - return(list); -} - -/************************************************************************ - * * - * Module interfaces * - * * - ************************************************************************/ - -/** - * xsltParseStylesheetAttributeSet: - * @style: the XSLT stylesheet - * @cur: the "attribute-set" element - * - * parse an XSLT stylesheet attribute-set element - */ - -void -xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) { - const xmlChar *ncname; - const xmlChar *prefix; - xmlChar *value; - xmlNodePtr child; - xsltAttrElemPtr attrItems; - - if ((cur == NULL) || (style == NULL)) - return; - - value = xmlGetNsProp(cur, (const xmlChar *)"name", NULL); - if (value == NULL) { - xsltGenericError(xsltGenericErrorContext, - "xsl:attribute-set : name is missing\n"); - return; - } - - ncname = xsltSplitQName(style->dict, value, &prefix); - xmlFree(value); - value = NULL; - - if (style->attributeSets == NULL) { -#ifdef WITH_XSLT_DEBUG_ATTRIBUTES - xsltGenericDebug(xsltGenericDebugContext, - "creating attribute set table\n"); -#endif - style->attributeSets = xmlHashCreate(10); - } - if (style->attributeSets == NULL) - return; - - attrItems = xmlHashLookup2(style->attributeSets, ncname, prefix); - - /* - * Parse the content. Only xsl:attribute elements are allowed. - */ - child = cur->children; - while (child != NULL) { - /* - * Report invalid nodes. - */ - if ((child->type != XML_ELEMENT_NODE) || - (child->ns == NULL) || - (! IS_XSLT_ELEM(child))) - { - if (child->type == XML_ELEMENT_NODE) - xsltTransformError(NULL, style, child, - "xsl:attribute-set : unexpected child %s\n", - child->name); - else - xsltTransformError(NULL, style, child, - "xsl:attribute-set : child of unexpected type\n"); - } else if (!IS_XSLT_NAME(child, "attribute")) { - xsltTransformError(NULL, style, child, - "xsl:attribute-set : unexpected child xsl:%s\n", - child->name); - } else { -#ifdef XSLT_REFACTORED - xsltAttrElemPtr nextAttr, curAttr; - - /* - * Process xsl:attribute - * --------------------- - */ - -#ifdef WITH_XSLT_DEBUG_ATTRIBUTES - xsltGenericDebug(xsltGenericDebugContext, - "add attribute to list %s\n", ncname); -#endif - /* - * The following was taken over from - * xsltAddAttrElemList(). - */ - if (attrItems == NULL) { - attrItems = xsltNewAttrElem(child); - } else { - curAttr = attrItems; - while (curAttr != NULL) { - nextAttr = curAttr->next; - if (curAttr->attr == child) { - /* - * URGENT TODO: Can somebody explain - * why attrItems is set to curAttr - * here? Is this somehow related to - * avoidance of recursions? - */ - attrItems = curAttr; - goto next_child; - } - if (curAttr->next == NULL) - curAttr->next = xsltNewAttrElem(child); - curAttr = nextAttr; - } - } - /* - * Parse the xsl:attribute and its content. - */ - xsltParseAnyXSLTElem(XSLT_CCTXT(style), child); -#else -#ifdef WITH_XSLT_DEBUG_ATTRIBUTES - xsltGenericDebug(xsltGenericDebugContext, - "add attribute to list %s\n", ncname); -#endif - /* - * OLD behaviour: - */ - attrItems = xsltAddAttrElemList(attrItems, child); -#endif - } - -#ifdef XSLT_REFACTORED -next_child: -#endif - child = child->next; - } - - /* - * Process attribue "use-attribute-sets". - */ - /* TODO check recursion */ - value = xmlGetNsProp(cur, (const xmlChar *)"use-attribute-sets", - NULL); - if (value != NULL) { - const xmlChar *curval, *endval; - curval = value; - while (*curval != 0) { - while (IS_BLANK(*curval)) curval++; - if (*curval == 0) - break; - endval = curval; - while ((*endval != 0) && (!IS_BLANK(*endval))) endval++; - curval = xmlDictLookup(style->dict, curval, endval - curval); - if (curval) { - const xmlChar *ncname2 = NULL; - const xmlChar *prefix2 = NULL; - xsltAttrElemPtr refAttrItems; - -#ifdef WITH_XSLT_DEBUG_ATTRIBUTES - xsltGenericDebug(xsltGenericDebugContext, - "xsl:attribute-set : %s adds use %s\n", ncname, curval); -#endif - ncname2 = xsltSplitQName(style->dict, curval, &prefix2); - refAttrItems = xsltNewAttrElem(NULL); - if (refAttrItems != NULL) { - refAttrItems->set = ncname2; - refAttrItems->ns = prefix2; - attrItems = xsltMergeAttrElemList(style, - attrItems, refAttrItems); - xsltFreeAttrElem(refAttrItems); - } - } - curval = endval; - } - xmlFree(value); - value = NULL; - } - - /* - * Update the value - */ - /* - * TODO: Why is this dummy entry needed.? - */ - if (attrItems == NULL) - attrItems = xsltNewAttrElem(NULL); - xmlHashUpdateEntry2(style->attributeSets, ncname, prefix, attrItems, NULL); -#ifdef WITH_XSLT_DEBUG_ATTRIBUTES - xsltGenericDebug(xsltGenericDebugContext, - "updated attribute list %s\n", ncname); -#endif -} - -/** - * xsltGetSAS: - * @style: the XSLT stylesheet - * @name: the attribute list name - * @ns: the attribute list namespace - * - * lookup an attribute set based on the style cascade - * - * Returns the attribute set or NULL - */ -static xsltAttrElemPtr -xsltGetSAS(xsltStylesheetPtr style, const xmlChar *name, const xmlChar *ns) { - xsltAttrElemPtr values; - - while (style != NULL) { - values = xmlHashLookup2(style->attributeSets, name, ns); - if (values != NULL) - return(values); - style = xsltNextImport(style); - } - return(NULL); -} - -/** - * xsltResolveSASCallback,: - * @style: the XSLT stylesheet - * - * resolve the references in an attribute set. - */ -static void -xsltResolveSASCallback(xsltAttrElemPtr values, xsltStylesheetPtr style, - const xmlChar *name, const xmlChar *ns, - ATTRIBUTE_UNUSED const xmlChar *ignored) { - xsltAttrElemPtr tmp; - xsltAttrElemPtr refs; - - tmp = values; - while (tmp != NULL) { - if (tmp->set != NULL) { - /* - * Check against cycles ! - */ - if ((xmlStrEqual(name, tmp->set)) && (xmlStrEqual(ns, tmp->ns))) { - xsltGenericError(xsltGenericErrorContext, - "xsl:attribute-set : use-attribute-sets recursion detected on %s\n", - name); - } else { -#ifdef WITH_XSLT_DEBUG_ATTRIBUTES - xsltGenericDebug(xsltGenericDebugContext, - "Importing attribute list %s\n", tmp->set); -#endif - - refs = xsltGetSAS(style, tmp->set, tmp->ns); - if (refs == NULL) { - xsltGenericError(xsltGenericErrorContext, - "xsl:attribute-set : use-attribute-sets %s reference missing %s\n", - name, tmp->set); - } else { - /* - * recurse first for cleanup - */ - xsltResolveSASCallback(refs, style, name, ns, NULL); - /* - * Then merge - */ - xsltMergeAttrElemList(style, values, refs); - /* - * Then suppress the reference - */ - tmp->set = NULL; - tmp->ns = NULL; - } - } - } - tmp = tmp->next; - } -} - -/** - * xsltMergeSASCallback,: - * @style: the XSLT stylesheet - * - * Merge an attribute set from an imported stylesheet. - */ -static void -xsltMergeSASCallback(xsltAttrElemPtr values, xsltStylesheetPtr style, - const xmlChar *name, const xmlChar *ns, - ATTRIBUTE_UNUSED const xmlChar *ignored) { - int ret; - xsltAttrElemPtr topSet; - - ret = xmlHashAddEntry2(style->attributeSets, name, ns, values); - if (ret < 0) { - /* - * Add failed, this attribute set can be removed. - */ -#ifdef WITH_XSLT_DEBUG_ATTRIBUTES - xsltGenericDebug(xsltGenericDebugContext, - "attribute set %s present already in top stylesheet" - " - merging\n", name); -#endif - topSet = xmlHashLookup2(style->attributeSets, name, ns); - if (topSet==NULL) { - xsltGenericError(xsltGenericErrorContext, - "xsl:attribute-set : logic error merging from imports for" - " attribute-set %s\n", name); - } else { - topSet = xsltMergeAttrElemList(style, topSet, values); - xmlHashUpdateEntry2(style->attributeSets, name, ns, topSet, NULL); - } - xsltFreeAttrElemList(values); -#ifdef WITH_XSLT_DEBUG_ATTRIBUTES - } else { - xsltGenericDebug(xsltGenericDebugContext, - "attribute set %s moved to top stylesheet\n", - name); -#endif - } -} - -/** - * xsltResolveStylesheetAttributeSet: - * @style: the XSLT stylesheet - * - * resolve the references between attribute sets. - */ -void -xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) { - xsltStylesheetPtr cur; - -#ifdef WITH_XSLT_DEBUG_ATTRIBUTES - xsltGenericDebug(xsltGenericDebugContext, - "Resolving attribute sets references\n"); -#endif - /* - * First aggregate all the attribute sets definitions from the imports - */ - cur = xsltNextImport(style); - while (cur != NULL) { - if (cur->attributeSets != NULL) { - if (style->attributeSets == NULL) { -#ifdef WITH_XSLT_DEBUG_ATTRIBUTES - xsltGenericDebug(xsltGenericDebugContext, - "creating attribute set table\n"); -#endif - style->attributeSets = xmlHashCreate(10); - } - xmlHashScanFull(cur->attributeSets, - (xmlHashScannerFull) xsltMergeSASCallback, style); - /* - * the attribute lists have either been migrated to style - * or freed directly in xsltMergeSASCallback() - */ - xmlHashFree(cur->attributeSets, NULL); - cur->attributeSets = NULL; - } - cur = xsltNextImport(cur); - } - - /* - * Then resolve all the references and computes the resulting sets - */ - if (style->attributeSets != NULL) { - xmlHashScanFull(style->attributeSets, - (xmlHashScannerFull) xsltResolveSASCallback, style); - } -} - -/** - * xsltAttributeInternal: - * @ctxt: a XSLT process context - * @node: the current node in the source tree - * @inst: the xsl:attribute element - * @comp: precomputed information - * @fromAttributeSet: the attribute comes from an attribute-set - * - * Process the xslt attribute node on the source node - */ -static void -xsltAttributeInternal(xsltTransformContextPtr ctxt, - xmlNodePtr currentNode, - xmlNodePtr inst, - xsltStylePreCompPtr castedComp, - int fromAttributeSet) -{ -#ifdef XSLT_REFACTORED - xsltStyleItemAttributePtr comp = - (xsltStyleItemAttributePtr) castedComp; -#else - xsltStylePreCompPtr comp = castedComp; -#endif - xmlNodePtr targetElem; - xmlChar *prop = NULL; - const xmlChar *name = NULL, *prefix = NULL, *nsName = NULL; - xmlChar *value = NULL; - xmlNsPtr ns = NULL; - xmlAttrPtr attr; - - if ((ctxt == NULL) || (currentNode == NULL) || (inst == 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; - /* - * BIG NOTE: This previously used xsltGetSpecialNamespace() and - * xsltGetNamespace(), but since both are not appropriate, we - * will process namespace lookup here to avoid adding yet another - * ns-lookup function to namespaces.c. - */ - /* - * SPEC XSLT 1.0: Error cases: - * - Creating nodes other than text nodes during the instantiation of - * the content of the xsl:attribute element; implementations may - * either signal the error or ignore the offending nodes." - */ - - if (comp == NULL) { - xsltTransformError(ctxt, NULL, inst, - "Internal error in xsltAttributeInternal(): " - "The instruction was no compiled.\n"); - return; - } - /* - * TODO: Shouldn't ctxt->insert == NULL be treated as an internal error? - * So report an internal error? - */ - if (ctxt->insert == NULL) - return; - /* - * SPEC XSLT 1.0: - * "Adding an attribute to a node that is not an element; - * implementations may either signal the error or ignore the attribute." - * - * TODO: I think we should signal such errors in the future, and maybe - * provide an option to ignore such errors. - */ - targetElem = ctxt->insert; - if (targetElem->type != XML_ELEMENT_NODE) - return; - - /* - * SPEC XSLT 1.0: - * "Adding an attribute to an element after children have been added - * to it; implementations may either signal the error or ignore the - * attribute." - * - * TODO: We should decide whether not to report such errors or - * to ignore them; note that we *ignore* if the parent is not an - * element, but here we report an error. - */ - if (targetElem->children != NULL) { - /* - * NOTE: Ah! This seems to be intended to support streamed - * result generation!. - */ - xsltTransformError(ctxt, NULL, inst, - "xsl:attribute: Cannot add attributes to an " - "element if children have been already added " - "to the element.\n"); - return; - } - - /* - * Process the name - * ---------------- - */ - -#ifdef WITH_DEBUGGER - if (ctxt->debugStatus != XSLT_DEBUG_NONE) - xslHandleDebugger(inst, currentNode, NULL, ctxt); -#endif - - 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:attribute: The attribute 'name' is missing.\n"); - goto error; - } - if (xmlValidateQName(prop, 0)) { - xsltTransformError(ctxt, NULL, inst, - "xsl:attribute: 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); - - /* - * Reject a prefix of "xmlns". - */ - if ((prefix != NULL) && - (!xmlStrncasecmp(prefix, (xmlChar *) "xmlns", 5))) - { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltAttribute: xmlns prefix forbidden\n"); -#endif - /* - * SPEC XSLT 1.0: - * "It is an error if the string that results from instantiating - * the attribute value template is not a QName or is the string - * xmlns. An XSLT processor may signal the error; if it does not - * signal the error, it must recover by not adding the attribute - * to the result tree." - * TODO: Decide which way to go here. - */ - goto error; - } - - } else { - /* - * The "name" value was static. - */ -#ifdef XSLT_REFACTORED - prefix = comp->nsPrefix; - name = comp->name; -#else - name = xsltSplitQName(ctxt->dict, comp->name, &prefix); -#endif - } - - /* - * Process namespace semantics - * --------------------------- - * - * Evaluate the namespace name. - */ - if (comp->has_ns) { - /* - * The "namespace" attribute was existent. - */ - 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); - /* - * This fixes bug #302020: The AVT might also evaluate to the - * empty string; this means that the empty string also indicates - * "no 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); - }; - } else if (prefix != NULL) { - /* - * 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:attribute element, *not* including any - * default namespace declaration." - */ - ns = xmlSearchNs(inst->doc, inst, prefix); - if (ns == NULL) { - /* - * Note that this is treated as an error now (checked with - * Saxon, Xalan-J and MSXML). - */ - xsltTransformError(ctxt, NULL, inst, - "xsl:attribute: 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; - } - - if (fromAttributeSet) { - /* - * This tries to ensure that xsl:attribute(s) coming - * from an xsl:attribute-set won't override attribute of - * literal result elements or of explicit xsl:attribute(s). - * URGENT TODO: This might be buggy, since it will miss to - * overwrite two equal attributes both from attribute sets. - */ - attr = xmlHasNsProp(targetElem, name, nsName); - if (attr != NULL) - return; - } - - /* - * Find/create a matching ns-decl in the result tree. - */ - ns = NULL; - -#if 0 - if (0) { - /* - * OPTIMIZE TODO: How do we know if we are adding to a - * fragment or to the result tree? - * - * If we are adding to a result tree fragment (i.e., not to the - * actual result tree), we'll don't bother searching for the - * ns-decl, but just store it in the dummy-doc of the result - * tree fragment. - */ - if (nsName != NULL) { - ns = xsltTreeAcquireStoredNs(ctxt->document->doc, nsName, - prefix); - } - } -#endif - - if (nsName != NULL) { - /* - * Something about ns-prefixes: - * SPEC XSLT 1.0: - * "XSLT processors may make use of the prefix of the QName specified - * in the name attribute when selecting the prefix used for outputting - * the created attribute as XML; however, they are not required to do - * so and, if the prefix is xmlns, they must not do so" - */ - /* - * xsl:attribute can produce a scenario where the prefix is NULL, - * so generate a prefix. - */ - if (prefix == NULL) { - xmlChar *pref = xmlStrdup(BAD_CAST "ns_1"); - - ns = xsltGetSpecialNamespace(ctxt, inst, nsName, BAD_CAST pref, - targetElem); - - xmlFree(pref); - } else { - ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix, - targetElem); - } - if (ns == NULL) { - xsltTransformError(ctxt, NULL, inst, - "Namespace fixup error: Failed to acquire an in-scope " - "namespace binding for the generated attribute '{%s}%s'.\n", - nsName, name); - goto error; - } - } - /* - * Construction of the value - * ------------------------- - */ - if (inst->children == NULL) { - /* - * No content. - * TODO: Do we need to put the empty string in ? - */ - attr = xmlSetNsProp(ctxt->insert, ns, name, (const xmlChar *) ""); - } else if ((inst->children->next == NULL) && - ((inst->children->type == XML_TEXT_NODE) || - (inst->children->type == XML_CDATA_SECTION_NODE))) - { - xmlNodePtr copyTxt; - - /* - * xmlSetNsProp() will take care of duplicates. - */ - attr = xmlSetNsProp(ctxt->insert, ns, name, NULL); - if (attr == NULL) /* TODO: report error ? */ - goto error; - /* - * This was taken over from xsltCopyText() (transform.c). - */ - if (ctxt->internalized && - (ctxt->insert->doc != NULL) && - (ctxt->insert->doc->dict == ctxt->dict)) - { - copyTxt = xmlNewText(NULL); - if (copyTxt == NULL) /* TODO: report error */ - goto error; - /* - * This is a safe scenario where we don't need to lookup - * the dict. - */ - copyTxt->content = inst->children->content; - /* - * Copy "disable-output-escaping" information. - * TODO: Does this have any effect for attribute values - * anyway? - */ - if (inst->children->name == xmlStringTextNoenc) - copyTxt->name = xmlStringTextNoenc; - } else { - /* - * Copy the value. - */ - copyTxt = xmlNewText(inst->children->content); - if (copyTxt == NULL) /* TODO: report error */ - goto error; - } - attr->children = attr->last = copyTxt; - copyTxt->parent = (xmlNodePtr) attr; - copyTxt->doc = attr->doc; - /* - * Copy "disable-output-escaping" information. - * TODO: Does this have any effect for attribute values - * anyway? - */ - if (inst->children->name == xmlStringTextNoenc) - copyTxt->name = xmlStringTextNoenc; - - } else { - /* - * The sequence constructor might be complex, so instantiate it. - */ - value = xsltEvalTemplateString(ctxt, currentNode, inst); - if (value != NULL) { - attr = xmlSetNsProp(ctxt->insert, ns, name, value); - xmlFree(value); - } else { - /* - * TODO: Do we have to add the empty string to the attr? - * TODO: Does a value of NULL indicate an - * error in xsltEvalTemplateString() ? - */ - attr = xmlSetNsProp(ctxt->insert, ns, name, - (const xmlChar *) ""); - } - } - -error: - return; -} - -/** - * xsltAttribute: - * @ctxt: a XSLT process context - * @node: the node in the source tree. - * @inst: the xslt attribute node - * @comp: precomputed information - * - * Process the xslt attribute node on the source node - */ -void -xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNodePtr inst, xsltStylePreCompPtr comp) { - xsltAttributeInternal(ctxt, node, inst, comp, 0); -} - -/** - * xsltApplyAttributeSet: - * @ctxt: the XSLT stylesheet - * @node: the node in the source tree. - * @inst: the attribute node "xsl:use-attribute-sets" - * @attrSets: the list of QNames of the attribute-sets to be applied - * - * Apply the xsl:use-attribute-sets. - * If @attrSets is NULL, then @inst will be used to exctract this - * value. - * If both, @attrSets and @inst, are NULL, then this will do nothing. - */ -void -xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNodePtr inst, - const xmlChar *attrSets) -{ - const xmlChar *ncname = NULL; - const xmlChar *prefix = NULL; - const xmlChar *curstr, *endstr; - xsltAttrElemPtr attrs; - xsltStylesheetPtr style; - - if (attrSets == NULL) { - if (inst == NULL) - return; - else { - /* - * Extract the value from @inst. - */ - if (inst->type == XML_ATTRIBUTE_NODE) { - if ( ((xmlAttrPtr) inst)->children != NULL) - attrSets = ((xmlAttrPtr) inst)->children->content; - - } - if (attrSets == NULL) { - /* - * TODO: Return an error? - */ - return; - } - } - } - /* - * Parse/apply the list of QNames. - */ - curstr = attrSets; - while (*curstr != 0) { - while (IS_BLANK(*curstr)) - curstr++; - if (*curstr == 0) - break; - endstr = curstr; - while ((*endstr != 0) && (!IS_BLANK(*endstr))) - endstr++; - curstr = xmlDictLookup(ctxt->dict, curstr, endstr - curstr); - if (curstr) { - /* - * TODO: Validate the QName. - */ - -#ifdef WITH_XSLT_DEBUG_curstrUTES - xsltGenericDebug(xsltGenericDebugContext, - "apply curstrute set %s\n", curstr); -#endif - ncname = xsltSplitQName(ctxt->dict, curstr, &prefix); - - style = ctxt->style; - -#ifdef WITH_DEBUGGER - if ((style != NULL) && - (style->attributeSets != NULL) && - (ctxt->debugStatus != XSLT_DEBUG_NONE)) - { - attrs = - xmlHashLookup2(style->attributeSets, ncname, prefix); - if ((attrs != NULL) && (attrs->attr != NULL)) - xslHandleDebugger(attrs->attr->parent, node, NULL, - ctxt); - } -#endif - /* - * Lookup the referenced curstrute-set. - */ - while (style != NULL) { - attrs = - xmlHashLookup2(style->attributeSets, ncname, prefix); - while (attrs != NULL) { - if (attrs->attr != NULL) { - xsltAttributeInternal(ctxt, node, attrs->attr, - attrs->attr->psvi, 1); - } - attrs = attrs->next; - } - style = xsltNextImport(style); - } - } - curstr = endstr; - } -} - -/** - * xsltFreeAttributeSetsHashes: - * @style: an XSLT stylesheet - * - * Free up the memory used by attribute sets - */ -void -xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) { - if (style->attributeSets != NULL) - xmlHashFree((xmlHashTablePtr) style->attributeSets, - (xmlHashDeallocator) xsltFreeAttrElemList); - style->attributeSets = NULL; -} +/* + * attributes.c: Implementation of the XSLT attributes handling + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_MATH_H +#include +#endif +#ifdef HAVE_FLOAT_H +#include +#endif +#ifdef HAVE_IEEEFP_H +#include +#endif +#ifdef HAVE_NAN_H +#include +#endif +#ifdef HAVE_CTYPE_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "attributes.h" +#include "namespaces.h" +#include "templates.h" +#include "imports.h" +#include "transform.h" +#include "preproc.h" + +#define WITH_XSLT_DEBUG_ATTRIBUTES +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_ATTRIBUTES +#endif + +/* + * TODO: merge attribute sets from different import precedence. + * all this should be precomputed just before the transformation + * starts or at first hit with a cache in the context. + * The simple way for now would be to not allow redefinition of + * attributes once generated in the output tree, possibly costlier. + */ + +/* + * Useful macros + */ +#ifdef IS_BLANK +#undef IS_BLANK +#endif + +#define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \ + ((c) == 0x0D)) + +#define IS_BLANK_NODE(n) \ + (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content))) + + +/* + * The in-memory structure corresponding to an XSLT Attribute in + * an attribute set + */ + + +typedef struct _xsltAttrElem xsltAttrElem; +typedef xsltAttrElem *xsltAttrElemPtr; +struct _xsltAttrElem { + struct _xsltAttrElem *next;/* chained list */ + xmlNodePtr attr; /* the xsl:attribute definition */ + const xmlChar *set; /* or the attribute set */ + const xmlChar *ns; /* and its namespace */ +}; + +/************************************************************************ + * * + * XSLT Attribute handling * + * * + ************************************************************************/ + +/** + * xsltNewAttrElem: + * @attr: the new xsl:attribute node + * + * Create a new XSLT AttrElem + * + * Returns the newly allocated xsltAttrElemPtr or NULL in case of error + */ +static xsltAttrElemPtr +xsltNewAttrElem(xmlNodePtr attr) { + xsltAttrElemPtr cur; + + cur = (xsltAttrElemPtr) xmlMalloc(sizeof(xsltAttrElem)); + if (cur == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltNewAttrElem : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltAttrElem)); + cur->attr = attr; + return(cur); +} + +/** + * xsltFreeAttrElem: + * @attr: an XSLT AttrElem + * + * Free up the memory allocated by @attr + */ +static void +xsltFreeAttrElem(xsltAttrElemPtr attr) { + xmlFree(attr); +} + +/** + * xsltFreeAttrElemList: + * @list: an XSLT AttrElem list + * + * Free up the memory allocated by @list + */ +static void +xsltFreeAttrElemList(xsltAttrElemPtr list) { + xsltAttrElemPtr next; + + while (list != NULL) { + next = list->next; + xsltFreeAttrElem(list); + list = next; + } +} + +#ifdef XSLT_REFACTORED + /* + * This was moved to xsltParseStylesheetAttributeSet(). + */ +#else +/** + * xsltAddAttrElemList: + * @list: an XSLT AttrElem list + * @attr: the new xsl:attribute node + * + * Add the new attribute to the list. + * + * Returns the new list pointer + */ +static xsltAttrElemPtr +xsltAddAttrElemList(xsltAttrElemPtr list, xmlNodePtr attr) { + xsltAttrElemPtr next, cur; + + if (attr == NULL) + return(list); + if (list == NULL) + return(xsltNewAttrElem(attr)); + cur = list; + while (cur != NULL) { + next = cur->next; + if (cur->attr == attr) + return(cur); + if (cur->next == NULL) { + cur->next = xsltNewAttrElem(attr); + return(list); + } + cur = next; + } + return(list); +} +#endif /* XSLT_REFACTORED */ + +/** + * xsltMergeAttrElemList: + * @list: an XSLT AttrElem list + * @old: another XSLT AttrElem list + * + * Add all the attributes from list @old to list @list, + * but drop redefinition of existing values. + * + * Returns the new list pointer + */ +static xsltAttrElemPtr +xsltMergeAttrElemList(xsltStylesheetPtr style, + xsltAttrElemPtr list, xsltAttrElemPtr old) { + xsltAttrElemPtr cur; + int add; + + while (old != NULL) { + if ((old->attr == NULL) && (old->set == NULL)) { + old = old->next; + continue; + } + /* + * Check that the attribute is not yet in the list + */ + cur = list; + add = 1; + while (cur != NULL) { + if ((cur->attr == NULL) && (cur->set == NULL)) { + if (cur->next == NULL) + break; + cur = cur->next; + continue; + } + if ((cur->set != NULL) && (cur->set == old->set)) { + add = 0; + break; + } + if (cur->set != NULL) { + if (cur->next == NULL) + break; + cur = cur->next; + continue; + } + if (old->set != NULL) { + if (cur->next == NULL) + break; + cur = cur->next; + continue; + } + if (cur->attr == old->attr) { + xsltGenericError(xsltGenericErrorContext, + "xsl:attribute-set : use-attribute-sets recursion detected\n"); + return(list); + } + if (cur->next == NULL) + break; + cur = cur->next; + } + + if (add == 1) { + /* + * Changed to use the string-dict, rather than duplicating + * @set and @ns; this fixes bug #340400. + */ + if (cur == NULL) { + list = xsltNewAttrElem(old->attr); + if (old->set != NULL) { + list->set = xmlDictLookup(style->dict, old->set, -1); + if (old->ns != NULL) + list->ns = xmlDictLookup(style->dict, old->ns, -1); + } + } else if (add) { + cur->next = xsltNewAttrElem(old->attr); + if (old->set != NULL) { + cur->next->set = xmlDictLookup(style->dict, old->set, -1); + if (old->ns != NULL) + cur->next->ns = xmlDictLookup(style->dict, old->ns, -1); + } + } + } + + old = old->next; + } + return(list); +} + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +/** + * xsltParseStylesheetAttributeSet: + * @style: the XSLT stylesheet + * @cur: the "attribute-set" element + * + * parse an XSLT stylesheet attribute-set element + */ + +void +xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) { + const xmlChar *ncname; + const xmlChar *prefix; + xmlChar *value; + xmlNodePtr child; + xsltAttrElemPtr attrItems; + + if ((cur == NULL) || (style == NULL)) + return; + + value = xmlGetNsProp(cur, (const xmlChar *)"name", NULL); + if (value == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsl:attribute-set : name is missing\n"); + return; + } + + ncname = xsltSplitQName(style->dict, value, &prefix); + xmlFree(value); + value = NULL; + + if (style->attributeSets == NULL) { +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "creating attribute set table\n"); +#endif + style->attributeSets = xmlHashCreate(10); + } + if (style->attributeSets == NULL) + return; + + attrItems = xmlHashLookup2(style->attributeSets, ncname, prefix); + + /* + * Parse the content. Only xsl:attribute elements are allowed. + */ + child = cur->children; + while (child != NULL) { + /* + * Report invalid nodes. + */ + if ((child->type != XML_ELEMENT_NODE) || + (child->ns == NULL) || + (! IS_XSLT_ELEM(child))) + { + if (child->type == XML_ELEMENT_NODE) + xsltTransformError(NULL, style, child, + "xsl:attribute-set : unexpected child %s\n", + child->name); + else + xsltTransformError(NULL, style, child, + "xsl:attribute-set : child of unexpected type\n"); + } else if (!IS_XSLT_NAME(child, "attribute")) { + xsltTransformError(NULL, style, child, + "xsl:attribute-set : unexpected child xsl:%s\n", + child->name); + } else { +#ifdef XSLT_REFACTORED + xsltAttrElemPtr nextAttr, curAttr; + + /* + * Process xsl:attribute + * --------------------- + */ + +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "add attribute to list %s\n", ncname); +#endif + /* + * The following was taken over from + * xsltAddAttrElemList(). + */ + if (attrItems == NULL) { + attrItems = xsltNewAttrElem(child); + } else { + curAttr = attrItems; + while (curAttr != NULL) { + nextAttr = curAttr->next; + if (curAttr->attr == child) { + /* + * URGENT TODO: Can somebody explain + * why attrItems is set to curAttr + * here? Is this somehow related to + * avoidance of recursions? + */ + attrItems = curAttr; + goto next_child; + } + if (curAttr->next == NULL) + curAttr->next = xsltNewAttrElem(child); + curAttr = nextAttr; + } + } + /* + * Parse the xsl:attribute and its content. + */ + xsltParseAnyXSLTElem(XSLT_CCTXT(style), child); +#else +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "add attribute to list %s\n", ncname); +#endif + /* + * OLD behaviour: + */ + attrItems = xsltAddAttrElemList(attrItems, child); +#endif + } + +#ifdef XSLT_REFACTORED +next_child: +#endif + child = child->next; + } + + /* + * Process attribue "use-attribute-sets". + */ + /* TODO check recursion */ + value = xmlGetNsProp(cur, (const xmlChar *)"use-attribute-sets", + NULL); + if (value != NULL) { + const xmlChar *curval, *endval; + curval = value; + while (*curval != 0) { + while (IS_BLANK(*curval)) curval++; + if (*curval == 0) + break; + endval = curval; + while ((*endval != 0) && (!IS_BLANK(*endval))) endval++; + curval = xmlDictLookup(style->dict, curval, endval - curval); + if (curval) { + const xmlChar *ncname2 = NULL; + const xmlChar *prefix2 = NULL; + xsltAttrElemPtr refAttrItems; + +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "xsl:attribute-set : %s adds use %s\n", ncname, curval); +#endif + ncname2 = xsltSplitQName(style->dict, curval, &prefix2); + refAttrItems = xsltNewAttrElem(NULL); + if (refAttrItems != NULL) { + refAttrItems->set = ncname2; + refAttrItems->ns = prefix2; + attrItems = xsltMergeAttrElemList(style, + attrItems, refAttrItems); + xsltFreeAttrElem(refAttrItems); + } + } + curval = endval; + } + xmlFree(value); + value = NULL; + } + + /* + * Update the value + */ + /* + * TODO: Why is this dummy entry needed.? + */ + if (attrItems == NULL) + attrItems = xsltNewAttrElem(NULL); + xmlHashUpdateEntry2(style->attributeSets, ncname, prefix, attrItems, NULL); +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "updated attribute list %s\n", ncname); +#endif +} + +/** + * xsltGetSAS: + * @style: the XSLT stylesheet + * @name: the attribute list name + * @ns: the attribute list namespace + * + * lookup an attribute set based on the style cascade + * + * Returns the attribute set or NULL + */ +static xsltAttrElemPtr +xsltGetSAS(xsltStylesheetPtr style, const xmlChar *name, const xmlChar *ns) { + xsltAttrElemPtr values; + + while (style != NULL) { + values = xmlHashLookup2(style->attributeSets, name, ns); + if (values != NULL) + return(values); + style = xsltNextImport(style); + } + return(NULL); +} + +/** + * xsltResolveSASCallback,: + * @style: the XSLT stylesheet + * + * resolve the references in an attribute set. + */ +static void +xsltResolveSASCallback(xsltAttrElemPtr values, xsltStylesheetPtr style, + const xmlChar *name, const xmlChar *ns, + ATTRIBUTE_UNUSED const xmlChar *ignored) { + xsltAttrElemPtr tmp; + xsltAttrElemPtr refs; + + tmp = values; + while (tmp != NULL) { + if (tmp->set != NULL) { + /* + * Check against cycles ! + */ + if ((xmlStrEqual(name, tmp->set)) && (xmlStrEqual(ns, tmp->ns))) { + xsltGenericError(xsltGenericErrorContext, + "xsl:attribute-set : use-attribute-sets recursion detected on %s\n", + name); + } else { +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "Importing attribute list %s\n", tmp->set); +#endif + + refs = xsltGetSAS(style, tmp->set, tmp->ns); + if (refs == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsl:attribute-set : use-attribute-sets %s reference missing %s\n", + name, tmp->set); + } else { + /* + * recurse first for cleanup + */ + xsltResolveSASCallback(refs, style, name, ns, NULL); + /* + * Then merge + */ + xsltMergeAttrElemList(style, values, refs); + /* + * Then suppress the reference + */ + tmp->set = NULL; + tmp->ns = NULL; + } + } + } + tmp = tmp->next; + } +} + +/** + * xsltMergeSASCallback,: + * @style: the XSLT stylesheet + * + * Merge an attribute set from an imported stylesheet. + */ +static void +xsltMergeSASCallback(xsltAttrElemPtr values, xsltStylesheetPtr style, + const xmlChar *name, const xmlChar *ns, + ATTRIBUTE_UNUSED const xmlChar *ignored) { + int ret; + xsltAttrElemPtr topSet; + + ret = xmlHashAddEntry2(style->attributeSets, name, ns, values); + if (ret < 0) { + /* + * Add failed, this attribute set can be removed. + */ +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "attribute set %s present already in top stylesheet" + " - merging\n", name); +#endif + topSet = xmlHashLookup2(style->attributeSets, name, ns); + if (topSet==NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsl:attribute-set : logic error merging from imports for" + " attribute-set %s\n", name); + } else { + topSet = xsltMergeAttrElemList(style, topSet, values); + xmlHashUpdateEntry2(style->attributeSets, name, ns, topSet, NULL); + } + xsltFreeAttrElemList(values); +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + } else { + xsltGenericDebug(xsltGenericDebugContext, + "attribute set %s moved to top stylesheet\n", + name); +#endif + } +} + +/** + * xsltResolveStylesheetAttributeSet: + * @style: the XSLT stylesheet + * + * resolve the references between attribute sets. + */ +void +xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) { + xsltStylesheetPtr cur; + +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "Resolving attribute sets references\n"); +#endif + /* + * First aggregate all the attribute sets definitions from the imports + */ + cur = xsltNextImport(style); + while (cur != NULL) { + if (cur->attributeSets != NULL) { + if (style->attributeSets == NULL) { +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "creating attribute set table\n"); +#endif + style->attributeSets = xmlHashCreate(10); + } + xmlHashScanFull(cur->attributeSets, + (xmlHashScannerFull) xsltMergeSASCallback, style); + /* + * the attribute lists have either been migrated to style + * or freed directly in xsltMergeSASCallback() + */ + xmlHashFree(cur->attributeSets, NULL); + cur->attributeSets = NULL; + } + cur = xsltNextImport(cur); + } + + /* + * Then resolve all the references and computes the resulting sets + */ + if (style->attributeSets != NULL) { + xmlHashScanFull(style->attributeSets, + (xmlHashScannerFull) xsltResolveSASCallback, style); + } +} + +/** + * xsltAttributeInternal: + * @ctxt: a XSLT process context + * @node: the current node in the source tree + * @inst: the xsl:attribute element + * @comp: precomputed information + * @fromAttributeSet: the attribute comes from an attribute-set + * + * Process the xslt attribute node on the source node + */ +static void +xsltAttributeInternal(xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, + xmlNodePtr inst, + xsltStylePreCompPtr castedComp, + int fromAttributeSet) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemAttributePtr comp = + (xsltStyleItemAttributePtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xmlNodePtr targetElem; + xmlChar *prop = NULL; + const xmlChar *name = NULL, *prefix = NULL, *nsName = NULL; + xmlChar *value = NULL; + xmlNsPtr ns = NULL; + xmlAttrPtr attr; + + if ((ctxt == NULL) || (contextNode == NULL) || (inst == 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; + /* + * BIG NOTE: This previously used xsltGetSpecialNamespace() and + * xsltGetNamespace(), but since both are not appropriate, we + * will process namespace lookup here to avoid adding yet another + * ns-lookup function to namespaces.c. + */ + /* + * SPEC XSLT 1.0: Error cases: + * - Creating nodes other than text nodes during the instantiation of + * the content of the xsl:attribute element; implementations may + * either signal the error or ignore the offending nodes." + */ + + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltAttributeInternal(): " + "The XSLT 'attribute' instruction was not compiled.\n"); + return; + } + /* + * TODO: Shouldn't ctxt->insert == NULL be treated as an internal error? + * So report an internal error? + */ + if (ctxt->insert == NULL) + return; + /* + * SPEC XSLT 1.0: + * "Adding an attribute to a node that is not an element; + * implementations may either signal the error or ignore the attribute." + * + * TODO: I think we should signal such errors in the future, and maybe + * provide an option to ignore such errors. + */ + targetElem = ctxt->insert; + if (targetElem->type != XML_ELEMENT_NODE) + return; + + /* + * SPEC XSLT 1.0: + * "Adding an attribute to an element after children have been added + * to it; implementations may either signal the error or ignore the + * attribute." + * + * TODO: We should decide whether not to report such errors or + * to ignore them; note that we *ignore* if the parent is not an + * element, but here we report an error. + */ + if (targetElem->children != NULL) { + /* + * NOTE: Ah! This seems to be intended to support streamed + * result generation!. + */ + xsltTransformError(ctxt, NULL, inst, + "xsl:attribute: Cannot add attributes to an " + "element if children have been already added " + "to the element.\n"); + return; + } + + /* + * Process the name + * ---------------- + */ + +#ifdef WITH_DEBUGGER + if (ctxt->debugStatus != XSLT_DEBUG_NONE) + xslHandleDebugger(inst, contextNode, NULL, ctxt); +#endif + + 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:attribute: The attribute 'name' is missing.\n"); + goto error; + } + if (xmlValidateQName(prop, 0)) { + xsltTransformError(ctxt, NULL, inst, + "xsl:attribute: 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); + + /* + * Reject a prefix of "xmlns". + */ + if ((prefix != NULL) && + (!xmlStrncasecmp(prefix, (xmlChar *) "xmlns", 5))) + { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltAttribute: xmlns prefix forbidden\n"); +#endif + /* + * SPEC XSLT 1.0: + * "It is an error if the string that results from instantiating + * the attribute value template is not a QName or is the string + * xmlns. An XSLT processor may signal the error; if it does not + * signal the error, it must recover by not adding the attribute + * to the result tree." + * TODO: Decide which way to go here. + */ + goto error; + } + + } else { + /* + * The "name" value was static. + */ +#ifdef XSLT_REFACTORED + prefix = comp->nsPrefix; + name = comp->name; +#else + name = xsltSplitQName(ctxt->dict, comp->name, &prefix); +#endif + } + + /* + * Process namespace semantics + * --------------------------- + * + * Evaluate the namespace name. + */ + if (comp->has_ns) { + /* + * The "namespace" attribute was existent. + */ + 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); + /* + * This fixes bug #302020: The AVT might also evaluate to the + * empty string; this means that the empty string also indicates + * "no 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); + }; + } else if (prefix != NULL) { + /* + * 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:attribute element, *not* including any + * default namespace declaration." + */ + ns = xmlSearchNs(inst->doc, inst, prefix); + if (ns == NULL) { + /* + * Note that this is treated as an error now (checked with + * Saxon, Xalan-J and MSXML). + */ + xsltTransformError(ctxt, NULL, inst, + "xsl:attribute: 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; + } + + if (fromAttributeSet) { + /* + * This tries to ensure that xsl:attribute(s) coming + * from an xsl:attribute-set won't override attribute of + * literal result elements or of explicit xsl:attribute(s). + * URGENT TODO: This might be buggy, since it will miss to + * overwrite two equal attributes both from attribute sets. + */ + attr = xmlHasNsProp(targetElem, name, nsName); + if (attr != NULL) + return; + } + + /* + * Find/create a matching ns-decl in the result tree. + */ + ns = NULL; + +#if 0 + if (0) { + /* + * OPTIMIZE TODO: How do we know if we are adding to a + * fragment or to the result tree? + * + * If we are adding to a result tree fragment (i.e., not to the + * actual result tree), we'll don't bother searching for the + * ns-decl, but just store it in the dummy-doc of the result + * tree fragment. + */ + if (nsName != NULL) { + /* + * TODO: Get the doc of @targetElem. + */ + ns = xsltTreeAcquireStoredNs(some doc, nsName, prefix); + } + } +#endif + + if (nsName != NULL) { + /* + * Something about ns-prefixes: + * SPEC XSLT 1.0: + * "XSLT processors may make use of the prefix of the QName specified + * in the name attribute when selecting the prefix used for outputting + * the created attribute as XML; however, they are not required to do + * so and, if the prefix is xmlns, they must not do so" + */ + /* + * xsl:attribute can produce a scenario where the prefix is NULL, + * so generate a prefix. + */ + if (prefix == NULL) { + xmlChar *pref = xmlStrdup(BAD_CAST "ns_1"); + + ns = xsltGetSpecialNamespace(ctxt, inst, nsName, BAD_CAST pref, + targetElem); + + xmlFree(pref); + } else { + ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix, + targetElem); + } + if (ns == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Namespace fixup error: Failed to acquire an in-scope " + "namespace binding for the generated attribute '{%s}%s'.\n", + nsName, name); + goto error; + } + } + /* + * Construction of the value + * ------------------------- + */ + if (inst->children == NULL) { + /* + * No content. + * TODO: Do we need to put the empty string in ? + */ + attr = xmlSetNsProp(ctxt->insert, ns, name, (const xmlChar *) ""); + } else if ((inst->children->next == NULL) && + ((inst->children->type == XML_TEXT_NODE) || + (inst->children->type == XML_CDATA_SECTION_NODE))) + { + xmlNodePtr copyTxt; + + /* + * xmlSetNsProp() will take care of duplicates. + */ + attr = xmlSetNsProp(ctxt->insert, ns, name, NULL); + if (attr == NULL) /* TODO: report error ? */ + goto error; + /* + * This was taken over from xsltCopyText() (transform.c). + */ + if (ctxt->internalized && + (ctxt->insert->doc != NULL) && + (ctxt->insert->doc->dict == ctxt->dict)) + { + copyTxt = xmlNewText(NULL); + if (copyTxt == NULL) /* TODO: report error */ + goto error; + /* + * This is a safe scenario where we don't need to lookup + * the dict. + */ + copyTxt->content = inst->children->content; + /* + * Copy "disable-output-escaping" information. + * TODO: Does this have any effect for attribute values + * anyway? + */ + if (inst->children->name == xmlStringTextNoenc) + copyTxt->name = xmlStringTextNoenc; + } else { + /* + * Copy the value. + */ + copyTxt = xmlNewText(inst->children->content); + if (copyTxt == NULL) /* TODO: report error */ + goto error; + } + attr->children = attr->last = copyTxt; + copyTxt->parent = (xmlNodePtr) attr; + copyTxt->doc = attr->doc; + /* + * Copy "disable-output-escaping" information. + * TODO: Does this have any effect for attribute values + * anyway? + */ + if (inst->children->name == xmlStringTextNoenc) + copyTxt->name = xmlStringTextNoenc; + + } else { + /* + * The sequence constructor might be complex, so instantiate it. + */ + value = xsltEvalTemplateString(ctxt, contextNode, inst); + if (value != NULL) { + attr = xmlSetNsProp(ctxt->insert, ns, name, value); + xmlFree(value); + } else { + /* + * TODO: Do we have to add the empty string to the attr? + * TODO: Does a value of NULL indicate an + * error in xsltEvalTemplateString() ? + */ + attr = xmlSetNsProp(ctxt->insert, ns, name, + (const xmlChar *) ""); + } + } + +error: + return; +} + +/** + * xsltAttribute: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt attribute node + * @comp: precomputed information + * + * Process the xslt attribute node on the source node + */ +void +xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr comp) { + xsltAttributeInternal(ctxt, node, inst, comp, 0); +} + +/** + * xsltApplyAttributeSet: + * @ctxt: the XSLT stylesheet + * @node: the node in the source tree. + * @inst: the attribute node "xsl:use-attribute-sets" + * @attrSets: the list of QNames of the attribute-sets to be applied + * + * Apply the xsl:use-attribute-sets. + * If @attrSets is NULL, then @inst will be used to exctract this + * value. + * If both, @attrSets and @inst, are NULL, then this will do nothing. + */ +void +xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, + const xmlChar *attrSets) +{ + const xmlChar *ncname = NULL; + const xmlChar *prefix = NULL; + const xmlChar *curstr, *endstr; + xsltAttrElemPtr attrs; + xsltStylesheetPtr style; + + if (attrSets == NULL) { + if (inst == NULL) + return; + else { + /* + * Extract the value from @inst. + */ + if (inst->type == XML_ATTRIBUTE_NODE) { + if ( ((xmlAttrPtr) inst)->children != NULL) + attrSets = ((xmlAttrPtr) inst)->children->content; + + } + if (attrSets == NULL) { + /* + * TODO: Return an error? + */ + return; + } + } + } + /* + * Parse/apply the list of QNames. + */ + curstr = attrSets; + while (*curstr != 0) { + while (IS_BLANK(*curstr)) + curstr++; + if (*curstr == 0) + break; + endstr = curstr; + while ((*endstr != 0) && (!IS_BLANK(*endstr))) + endstr++; + curstr = xmlDictLookup(ctxt->dict, curstr, endstr - curstr); + if (curstr) { + /* + * TODO: Validate the QName. + */ + +#ifdef WITH_XSLT_DEBUG_curstrUTES + xsltGenericDebug(xsltGenericDebugContext, + "apply curstrute set %s\n", curstr); +#endif + ncname = xsltSplitQName(ctxt->dict, curstr, &prefix); + + style = ctxt->style; + +#ifdef WITH_DEBUGGER + if ((style != NULL) && + (style->attributeSets != NULL) && + (ctxt->debugStatus != XSLT_DEBUG_NONE)) + { + attrs = + xmlHashLookup2(style->attributeSets, ncname, prefix); + if ((attrs != NULL) && (attrs->attr != NULL)) + xslHandleDebugger(attrs->attr->parent, node, NULL, + ctxt); + } +#endif + /* + * Lookup the referenced curstrute-set. + */ + while (style != NULL) { + attrs = + xmlHashLookup2(style->attributeSets, ncname, prefix); + while (attrs != NULL) { + if (attrs->attr != NULL) { + xsltAttributeInternal(ctxt, node, attrs->attr, + attrs->attr->psvi, 1); + } + attrs = attrs->next; + } + style = xsltNextImport(style); + } + } + curstr = endstr; + } +} + +/** + * xsltFreeAttributeSetsHashes: + * @style: an XSLT stylesheet + * + * Free up the memory used by attribute sets + */ +void +xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) { + if (style->attributeSets != NULL) + xmlHashFree((xmlHashTablePtr) style->attributeSets, + (xmlHashDeallocator) xsltFreeAttrElemList); + style->attributeSets = NULL; +} diff --git a/libxslt/documents.c b/libxslt/documents.c index 75fd90d2..c3d5400e 100644 --- a/libxslt/documents.c +++ b/libxslt/documents.c @@ -1,437 +1,439 @@ -/* - * documents.c: Implementation of the documents handling - * - * See Copyright for the status of this software. - * - * daniel@veillard.com - */ - -#define IN_LIBXSLT -#include "libxslt.h" - -#include - -#include -#include -#include -#include -#include -#include "xslt.h" -#include "xsltInternals.h" -#include "xsltutils.h" -#include "documents.h" -#include "transform.h" -#include "imports.h" -#include "keys.h" -#include "security.h" - -#ifdef LIBXML_XINCLUDE_ENABLED -#include -#endif - -#define WITH_XSLT_DEBUG_DOCUMENTS - -#ifdef WITH_XSLT_DEBUG -#define WITH_XSLT_DEBUG_DOCUMENTS -#endif - -/************************************************************************ - * * - * Hooks for the document loader * - * * - ************************************************************************/ - -/** - * xsltDocDefaultLoaderFunc: - * @URI: the URI of the document to load - * @dict: the dictionnary to use when parsing that document - * @options: parsing options, a set of xmlParserOption - * @ctxt: the context, either a stylesheet or a transformation context - * @type: the xsltLoadType indicating the kind of loading required - * - * Default function to load document not provided by the compilation or - * transformation API themselve, for example when an xsl:import, - * xsl:include is found at compilation time or when a document() - * call is made at runtime. - * - * Returns the pointer to the document (which will be modified and - * freed by the engine later), or NULL in case of error. - */ -static xmlDocPtr -xsltDocDefaultLoaderFunc(const xmlChar * URI, xmlDictPtr dict, int options, - void *ctxt ATTRIBUTE_UNUSED, - xsltLoadType type ATTRIBUTE_UNUSED) -{ - xmlParserCtxtPtr pctxt; - xmlParserInputPtr inputStream; - xmlDocPtr doc; - - pctxt = xmlNewParserCtxt(); - if (pctxt == NULL) - return(NULL); - if ((dict != NULL) && (pctxt->dict != NULL)) { - xmlDictFree(pctxt->dict); - pctxt->dict = NULL; - } - if (dict != NULL) { - pctxt->dict = dict; - xmlDictReference(pctxt->dict); -#ifdef WITH_XSLT_DEBUG - xsltGenericDebug(xsltGenericDebugContext, - "Reusing dictionary for document\n"); -#endif - } - xmlCtxtUseOptions(pctxt, options); - inputStream = xmlLoadExternalEntity((const char *) URI, NULL, pctxt); - if (inputStream == NULL) { - xmlFreeParserCtxt(pctxt); - return(NULL); - } - inputPush(pctxt, inputStream); - if (pctxt->directory == NULL) - pctxt->directory = xmlParserGetDirectory((const char *) URI); - - xmlParseDocument(pctxt); - - if (pctxt->wellFormed) { - doc = pctxt->myDoc; - } - else { - doc = NULL; - xmlFreeDoc(pctxt->myDoc); - pctxt->myDoc = NULL; - } - xmlFreeParserCtxt(pctxt); - - return(doc); -} - - -xsltDocLoaderFunc xsltDocDefaultLoader = xsltDocDefaultLoaderFunc; - -/** - * xsltSetLoaderFunc: - * @f: the new function to handle document loading. - * - * Set the new function to load document, if NULL it resets it to the - * default function. - */ - -void -xsltSetLoaderFunc(xsltDocLoaderFunc f) { - if (f == NULL) - xsltDocDefaultLoader = xsltDocDefaultLoaderFunc; - else - xsltDocDefaultLoader = f; -} - -/************************************************************************ - * * - * Module interfaces * - * * - ************************************************************************/ - -/** - * xsltNewDocument: - * @ctxt: an XSLT transformation context (or NULL) - * @doc: a parsed XML document - * - * Register a new document, apply key computations - * - * Returns a handler to the document - */ -xsltDocumentPtr -xsltNewDocument(xsltTransformContextPtr ctxt, xmlDocPtr doc) { - xsltDocumentPtr cur; - - cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument)); - if (cur == NULL) { - xsltTransformError(ctxt, NULL, (xmlNodePtr) doc, - "xsltNewDocument : malloc failed\n"); - return(NULL); - } - memset(cur, 0, sizeof(xsltDocument)); - cur->doc = doc; - if (ctxt != NULL) { - if (! XSLT_IS_RES_TREE_FRAG(doc)) { - cur->next = ctxt->docList; - ctxt->docList = cur; - } -#ifdef XSLT_REFACTORED_KEYCOMP - /* - * A key with a specific name for a specific document - * will only be computed if there's a call to the key() - * function using that specific name for that specific - * document. I.e. computation of keys will be done in - * xsltGetKey() (keys.c) on an on-demand basis. - */ -#else - /* - * Old behaviour. - */ - xsltInitCtxtKeys(ctxt, cur); -#endif - } - return(cur); -} - -/** - * xsltNewStyleDocument: - * @style: an XSLT style sheet - * @doc: a parsed XML document - * - * Register a new document, apply key computations - * - * Returns a handler to the document - */ -xsltDocumentPtr -xsltNewStyleDocument(xsltStylesheetPtr style, xmlDocPtr doc) { - xsltDocumentPtr cur; - - cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument)); - if (cur == NULL) { - xsltTransformError(NULL, style, (xmlNodePtr) doc, - "xsltNewStyleDocument : malloc failed\n"); - return(NULL); - } - memset(cur, 0, sizeof(xsltDocument)); - cur->doc = doc; - if (style != NULL) { - cur->next = style->docList; - style->docList = cur; - } - return(cur); -} - -/** - * xsltFreeStyleDocuments: - * @style: an XSLT stylesheet (representing a stylesheet-level) - * - * Frees the node-trees (and xsltDocument structures) of all - * stylesheet-modules of the stylesheet-level represented by - * the given @style. - */ -void -xsltFreeStyleDocuments(xsltStylesheetPtr style) { - xsltDocumentPtr doc, cur; -#ifdef XSLT_REFACTORED_XSLT_NSCOMP - xsltNsMapPtr nsMap; -#endif - - if (style == NULL) - return; - -#ifdef XSLT_REFACTORED_XSLT_NSCOMP - if (XSLT_HAS_INTERNAL_NSMAP(style)) - nsMap = XSLT_GET_INTERNAL_NSMAP(style); - else - nsMap = NULL; -#endif - - cur = style->docList; - while (cur != NULL) { - doc = cur; - cur = cur->next; -#ifdef XSLT_REFACTORED_XSLT_NSCOMP - /* - * Restore all changed namespace URIs of ns-decls. - */ - if (nsMap) - xsltRestoreDocumentNamespaces(nsMap, doc->doc); -#endif - xsltFreeDocumentKeys(doc); - if (!doc->main) - xmlFreeDoc(doc->doc); - xmlFree(doc); - } -} - -/** - * xsltFreeDocuments: - * @ctxt: an XSLT transformation context - * - * Free up all the space used by the loaded documents - */ -void -xsltFreeDocuments(xsltTransformContextPtr ctxt) { - xsltDocumentPtr doc, cur; - - cur = ctxt->docList; - while (cur != NULL) { - doc = cur; - cur = cur->next; - xsltFreeDocumentKeys(doc); - if (!doc->main) - xmlFreeDoc(doc->doc); - xmlFree(doc); - } - cur = ctxt->styleList; - while (cur != NULL) { - doc = cur; - cur = cur->next; - xsltFreeDocumentKeys(doc); - if (!doc->main) - xmlFreeDoc(doc->doc); - xmlFree(doc); - } -} - -/** - * xsltLoadDocument: - * @ctxt: an XSLT transformation context - * @URI: the computed URI of the document - * - * Try to load a document (not a stylesheet) - * within the XSLT transformation context - * - * Returns the new xsltDocumentPtr or NULL in case of error - */ -xsltDocumentPtr -xsltLoadDocument(xsltTransformContextPtr ctxt, const xmlChar *URI) { - xsltDocumentPtr ret; - xmlDocPtr doc; - - if ((ctxt == NULL) || (URI == NULL)) - return(NULL); - - /* - * Security framework check - */ - if (ctxt->sec != NULL) { - int res; - - res = xsltCheckRead(ctxt->sec, ctxt, URI); - if (res == 0) { - xsltTransformError(ctxt, NULL, NULL, - "xsltLoadDocument: read rights for %s denied\n", - URI); - return(NULL); - } - } - - /* - * Walk the context list to find the document if preparsed - */ - ret = ctxt->docList; - while (ret != NULL) { - if ((ret->doc != NULL) && (ret->doc->URL != NULL) && - (xmlStrEqual(ret->doc->URL, URI))) - return(ret); - ret = ret->next; - } - - doc = xsltDocDefaultLoader(URI, ctxt->dict, ctxt->parserOptions, - (void *) ctxt, XSLT_LOAD_DOCUMENT); - - if (doc == NULL) - return(NULL); - - if (ctxt->xinclude != 0) { -#ifdef LIBXML_XINCLUDE_ENABLED -#if LIBXML_VERSION >= 20603 - xmlXIncludeProcessFlags(doc, ctxt->parserOptions); -#else - xmlXIncludeProcess(doc); -#endif -#else - xsltTransformError(ctxt, NULL, NULL, - "xsltLoadDocument(%s) : XInclude processing not compiled in\n", - URI); -#endif - } - /* - * Apply white-space stripping if asked for - */ - if (xsltNeedElemSpaceHandling(ctxt)) - xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc)); - if (ctxt->debugStatus == XSLT_DEBUG_NONE) - xmlXPathOrderDocElems(doc); - - ret = xsltNewDocument(ctxt, doc); - return(ret); -} - -/** - * xsltLoadStyleDocument: - * @style: an XSLT style sheet - * @URI: the computed URI of the document - * - * Try to load a stylesheet document within the XSLT transformation context - * - * Returns the new xsltDocumentPtr or NULL in case of error - */ -xsltDocumentPtr -xsltLoadStyleDocument(xsltStylesheetPtr style, const xmlChar *URI) { - xsltDocumentPtr ret; - xmlDocPtr doc; - xsltSecurityPrefsPtr sec; - - if ((style == NULL) || (URI == NULL)) - return(NULL); - - /* - * Security framework check - */ - sec = xsltGetDefaultSecurityPrefs(); - if (sec != NULL) { - int res; - - res = xsltCheckRead(sec, NULL, URI); - if (res == 0) { - xsltTransformError(NULL, NULL, NULL, - "xsltLoadStyleDocument: read rights for %s denied\n", - URI); - return(NULL); - } - } - - /* - * Walk the context list to find the document if preparsed - */ - ret = style->docList; - while (ret != NULL) { - if ((ret->doc != NULL) && (ret->doc->URL != NULL) && - (xmlStrEqual(ret->doc->URL, URI))) - return(ret); - ret = ret->next; - } - - doc = xsltDocDefaultLoader(URI, style->dict, XSLT_PARSE_OPTIONS, - (void *) style, XSLT_LOAD_STYLESHEET); - if (doc == NULL) - return(NULL); - - ret = xsltNewStyleDocument(style, doc); - return(ret); -} - -/** - * xsltFindDocument: - * @ctxt: an XSLT transformation context - * @doc: a parsed XML document - * - * Try to find a document within the XSLT transformation context - * - * Returns the desired xsltDocumentPtr or NULL in case of error - */ -xsltDocumentPtr -xsltFindDocument (xsltTransformContextPtr ctxt, xmlDocPtr doc) { - xsltDocumentPtr ret; - - if ((ctxt == NULL) || (doc == NULL)) - return(NULL); - - /* - * Walk the context list to find the document - */ - ret = ctxt->docList; - while (ret != NULL) { - if (ret->doc == doc) - return(ret); - ret = ret->next; - } - if (doc == ctxt->style->doc) - return(ctxt->document); - return(NULL); -} - +/* + * documents.c: Implementation of the documents handling + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include + +#include +#include +#include +#include +#include +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "documents.h" +#include "transform.h" +#include "imports.h" +#include "keys.h" +#include "security.h" + +#ifdef LIBXML_XINCLUDE_ENABLED +#include +#endif + +#define WITH_XSLT_DEBUG_DOCUMENTS + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_DOCUMENTS +#endif + +/************************************************************************ + * * + * Hooks for the document loader * + * * + ************************************************************************/ + +/** + * xsltDocDefaultLoaderFunc: + * @URI: the URI of the document to load + * @dict: the dictionnary to use when parsing that document + * @options: parsing options, a set of xmlParserOption + * @ctxt: the context, either a stylesheet or a transformation context + * @type: the xsltLoadType indicating the kind of loading required + * + * Default function to load document not provided by the compilation or + * transformation API themselve, for example when an xsl:import, + * xsl:include is found at compilation time or when a document() + * call is made at runtime. + * + * Returns the pointer to the document (which will be modified and + * freed by the engine later), or NULL in case of error. + */ +static xmlDocPtr +xsltDocDefaultLoaderFunc(const xmlChar * URI, xmlDictPtr dict, int options, + void *ctxt ATTRIBUTE_UNUSED, + xsltLoadType type ATTRIBUTE_UNUSED) +{ + xmlParserCtxtPtr pctxt; + xmlParserInputPtr inputStream; + xmlDocPtr doc; + + pctxt = xmlNewParserCtxt(); + if (pctxt == NULL) + return(NULL); + if ((dict != NULL) && (pctxt->dict != NULL)) { + xmlDictFree(pctxt->dict); + pctxt->dict = NULL; + } + if (dict != NULL) { + pctxt->dict = dict; + xmlDictReference(pctxt->dict); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "Reusing dictionary for document\n"); +#endif + } + xmlCtxtUseOptions(pctxt, options); + inputStream = xmlLoadExternalEntity((const char *) URI, NULL, pctxt); + if (inputStream == NULL) { + xmlFreeParserCtxt(pctxt); + return(NULL); + } + inputPush(pctxt, inputStream); + if (pctxt->directory == NULL) + pctxt->directory = xmlParserGetDirectory((const char *) URI); + + xmlParseDocument(pctxt); + + if (pctxt->wellFormed) { + doc = pctxt->myDoc; + } + else { + doc = NULL; + xmlFreeDoc(pctxt->myDoc); + pctxt->myDoc = NULL; + } + xmlFreeParserCtxt(pctxt); + + return(doc); +} + + +xsltDocLoaderFunc xsltDocDefaultLoader = xsltDocDefaultLoaderFunc; + +/** + * xsltSetLoaderFunc: + * @f: the new function to handle document loading. + * + * Set the new function to load document, if NULL it resets it to the + * default function. + */ + +void +xsltSetLoaderFunc(xsltDocLoaderFunc f) { + if (f == NULL) + xsltDocDefaultLoader = xsltDocDefaultLoaderFunc; + else + xsltDocDefaultLoader = f; +} + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +/** + * xsltNewDocument: + * @ctxt: an XSLT transformation context (or NULL) + * @doc: a parsed XML document + * + * Register a new document, apply key computations + * + * Returns a handler to the document + */ +xsltDocumentPtr +xsltNewDocument(xsltTransformContextPtr ctxt, xmlDocPtr doc) { + xsltDocumentPtr cur; + + cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument)); + if (cur == NULL) { + xsltTransformError(ctxt, NULL, (xmlNodePtr) doc, + "xsltNewDocument : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltDocument)); + cur->doc = doc; + if (ctxt != NULL) { + if (! XSLT_IS_RES_TREE_FRAG(doc)) { + cur->next = ctxt->docList; + ctxt->docList = cur; + } +#ifdef XSLT_REFACTORED_KEYCOMP + /* + * A key with a specific name for a specific document + * will only be computed if there's a call to the key() + * function using that specific name for that specific + * document. I.e. computation of keys will be done in + * xsltGetKey() (keys.c) on an on-demand basis. + */ +#else + /* + * Old behaviour. + */ + xsltInitCtxtKeys(ctxt, cur); +#endif + } + return(cur); +} + +/** + * xsltNewStyleDocument: + * @style: an XSLT style sheet + * @doc: a parsed XML document + * + * Register a new document, apply key computations + * + * Returns a handler to the document + */ +xsltDocumentPtr +xsltNewStyleDocument(xsltStylesheetPtr style, xmlDocPtr doc) { + xsltDocumentPtr cur; + + cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument)); + if (cur == NULL) { + xsltTransformError(NULL, style, (xmlNodePtr) doc, + "xsltNewStyleDocument : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltDocument)); + cur->doc = doc; + if (style != NULL) { + cur->next = style->docList; + style->docList = cur; + } + return(cur); +} + +/** + * xsltFreeStyleDocuments: + * @style: an XSLT stylesheet (representing a stylesheet-level) + * + * Frees the node-trees (and xsltDocument structures) of all + * stylesheet-modules of the stylesheet-level represented by + * the given @style. + */ +void +xsltFreeStyleDocuments(xsltStylesheetPtr style) { + xsltDocumentPtr doc, cur; +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + xsltNsMapPtr nsMap; +#endif + + if (style == NULL) + return; + +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + if (XSLT_HAS_INTERNAL_NSMAP(style)) + nsMap = XSLT_GET_INTERNAL_NSMAP(style); + else + nsMap = NULL; +#endif + + cur = style->docList; + while (cur != NULL) { + doc = cur; + cur = cur->next; +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + /* + * Restore all changed namespace URIs of ns-decls. + */ + if (nsMap) + xsltRestoreDocumentNamespaces(nsMap, doc->doc); +#endif + xsltFreeDocumentKeys(doc); + if (!doc->main) + xmlFreeDoc(doc->doc); + xmlFree(doc); + } +} + +/** + * xsltFreeDocuments: + * @ctxt: an XSLT transformation context + * + * Free up all the space used by the loaded documents + */ +void +xsltFreeDocuments(xsltTransformContextPtr ctxt) { + xsltDocumentPtr doc, cur; + + cur = ctxt->docList; + while (cur != NULL) { + doc = cur; + cur = cur->next; + xsltFreeDocumentKeys(doc); + if (!doc->main) + xmlFreeDoc(doc->doc); + xmlFree(doc); + } + cur = ctxt->styleList; + while (cur != NULL) { + doc = cur; + cur = cur->next; + xsltFreeDocumentKeys(doc); + if (!doc->main) + xmlFreeDoc(doc->doc); + xmlFree(doc); + } +} + +/** + * xsltLoadDocument: + * @ctxt: an XSLT transformation context + * @URI: the computed URI of the document + * + * Try to load a document (not a stylesheet) + * within the XSLT transformation context + * + * Returns the new xsltDocumentPtr or NULL in case of error + */ +xsltDocumentPtr +xsltLoadDocument(xsltTransformContextPtr ctxt, const xmlChar *URI) { + xsltDocumentPtr ret; + xmlDocPtr doc; + + if ((ctxt == NULL) || (URI == NULL)) + return(NULL); + + /* + * Security framework check + */ + if (ctxt->sec != NULL) { + int res; + + res = xsltCheckRead(ctxt->sec, ctxt, URI); + if (res == 0) { + xsltTransformError(ctxt, NULL, NULL, + "xsltLoadDocument: read rights for %s denied\n", + URI); + return(NULL); + } + } + + /* + * Walk the context list to find the document if preparsed + */ + ret = ctxt->docList; + while (ret != NULL) { + if ((ret->doc != NULL) && (ret->doc->URL != NULL) && + (xmlStrEqual(ret->doc->URL, URI))) + return(ret); + ret = ret->next; + } + + doc = xsltDocDefaultLoader(URI, ctxt->dict, ctxt->parserOptions, + (void *) ctxt, XSLT_LOAD_DOCUMENT); + + if (doc == NULL) + return(NULL); + + if (ctxt->xinclude != 0) { +#ifdef LIBXML_XINCLUDE_ENABLED +#if LIBXML_VERSION >= 20603 + xmlXIncludeProcessFlags(doc, ctxt->parserOptions); +#else + xmlXIncludeProcess(doc); +#endif +#else + xsltTransformError(ctxt, NULL, NULL, + "xsltLoadDocument(%s) : XInclude processing not compiled in\n", + URI); +#endif + } + /* + * Apply white-space stripping if asked for + */ + if (xsltNeedElemSpaceHandling(ctxt)) + xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc)); + if (ctxt->debugStatus == XSLT_DEBUG_NONE) + xmlXPathOrderDocElems(doc); + + ret = xsltNewDocument(ctxt, doc); + return(ret); +} + +/** + * xsltLoadStyleDocument: + * @style: an XSLT style sheet + * @URI: the computed URI of the document + * + * Try to load a stylesheet document within the XSLT transformation context + * + * Returns the new xsltDocumentPtr or NULL in case of error + */ +xsltDocumentPtr +xsltLoadStyleDocument(xsltStylesheetPtr style, const xmlChar *URI) { + xsltDocumentPtr ret; + xmlDocPtr doc; + xsltSecurityPrefsPtr sec; + + if ((style == NULL) || (URI == NULL)) + return(NULL); + + /* + * Security framework check + */ + sec = xsltGetDefaultSecurityPrefs(); + if (sec != NULL) { + int res; + + res = xsltCheckRead(sec, NULL, URI); + if (res == 0) { + xsltTransformError(NULL, NULL, NULL, + "xsltLoadStyleDocument: read rights for %s denied\n", + URI); + return(NULL); + } + } + + /* + * Walk the context list to find the document if preparsed + */ + ret = style->docList; + while (ret != NULL) { + if ((ret->doc != NULL) && (ret->doc->URL != NULL) && + (xmlStrEqual(ret->doc->URL, URI))) + return(ret); + ret = ret->next; + } + + doc = xsltDocDefaultLoader(URI, style->dict, XSLT_PARSE_OPTIONS, + (void *) style, XSLT_LOAD_STYLESHEET); + if (doc == NULL) + return(NULL); + + ret = xsltNewStyleDocument(style, doc); + return(ret); +} + +/** + * xsltFindDocument: + * @ctxt: an XSLT transformation context + * @doc: a parsed XML document + * + * Try to find a document within the XSLT transformation context. + * This will not find document infos for temporary + * Result Tree Fragments. + * + * Returns the desired xsltDocumentPtr or NULL in case of error + */ +xsltDocumentPtr +xsltFindDocument (xsltTransformContextPtr ctxt, xmlDocPtr doc) { + xsltDocumentPtr ret; + + if ((ctxt == NULL) || (doc == NULL)) + return(NULL); + + /* + * Walk the context list to find the document + */ + ret = ctxt->docList; + while (ret != NULL) { + if (ret->doc == doc) + return(ret); + ret = ret->next; + } + if (doc == ctxt->style->doc) + return(ctxt->document); + return(NULL); +} + diff --git a/libxslt/functions.c b/libxslt/functions.c index 871c8b9f..f03d71c1 100644 --- a/libxslt/functions.c +++ b/libxslt/functions.c @@ -1,917 +1,971 @@ -/* - * functions.c: Implementation of the XSLT extra functions - * - * Reference: - * http://www.w3.org/TR/1999/REC-xslt-19991116 - * - * See Copyright for the status of this software. - * - * daniel@veillard.com - * Bjorn Reese for number formatting - */ - -#define IN_LIBXSLT -#include "libxslt.h" - -#include - -#ifdef HAVE_SYS_TYPES_H -#include -#endif -#ifdef HAVE_CTYPE_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "xslt.h" -#include "xsltInternals.h" -#include "xsltutils.h" -#include "functions.h" -#include "extensions.h" -#include "numbersInternals.h" -#include "keys.h" -#include "documents.h" - -#ifdef WITH_XSLT_DEBUG -#define WITH_XSLT_DEBUG_FUNCTION -#endif - -/* - * Some versions of DocBook XSL use the vendor string to detect - * supporting chunking, this is a workaround to be considered - * in the list of decent XSLT processors - */ -#define DOCBOOK_XSL_HACK - -/** - * xsltXPathFunctionLookup: - * @ctxt: a void * but the XSLT transformation context actually - * @name: the function name - * @ns_uri: the function namespace URI - * - * This is the entry point when a function is needed by the XPath - * interpretor. - * - * Returns the callback function or NULL if not found - */ -xmlXPathFunction -xsltXPathFunctionLookup (xmlXPathContextPtr ctxt, - const xmlChar *name, const xmlChar *ns_uri) { - xmlXPathFunction ret; - - if ((ctxt == NULL) || (name == NULL) || (ns_uri == NULL)) - return (NULL); - -#ifdef WITH_XSLT_DEBUG_FUNCTION - xsltGenericDebug(xsltGenericDebugContext, - "Lookup function {%s}%s\n", ns_uri, name); -#endif - - /* give priority to context-level functions */ - /* - ret = (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri); - */ - XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri); - - if (ret == NULL) - ret = xsltExtModuleFunctionLookup(name, ns_uri); - -#ifdef WITH_XSLT_DEBUG_FUNCTION - if (ret != NULL) - xsltGenericDebug(xsltGenericDebugContext, - "found function %s\n", name); -#endif - return(ret); -} - - -/************************************************************************ - * * - * Module interfaces * - * * - ************************************************************************/ - -static void -xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI) -{ - xsltTransformContextPtr tctxt; - xmlURIPtr uri; - xmlChar *fragment; - xsltDocumentPtr xsltdoc; - xmlDocPtr doc; - xmlXPathContextPtr xptrctxt = NULL; - xmlXPathObjectPtr object = NULL; - - tctxt = xsltXPathGetTransformContext(ctxt); - if (tctxt == NULL) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "document() : internal error tctxt == NULL\n"); - valuePush(ctxt, xmlXPathNewNodeSet(NULL)); - return; - } - - uri = xmlParseURI((const char *) URI); - if (uri == NULL) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "document() : failed to parse URI\n"); - valuePush(ctxt, xmlXPathNewNodeSet(NULL)); - return; - } - - /* - * check for and remove fragment identifier - */ - fragment = (xmlChar *)uri->fragment; - if (fragment != NULL) { - uri->fragment = NULL; - URI = xmlSaveUri(uri); - xsltdoc = xsltLoadDocument(tctxt, URI); - xmlFree(URI); - } else - xsltdoc = xsltLoadDocument(tctxt, URI); - xmlFreeURI(uri); - - if (xsltdoc == NULL) { - if ((URI == NULL) || - (URI[0] == '#') || - (xmlStrEqual(tctxt->style->doc->URL, URI))) { - doc = tctxt->style->doc; - } else { - valuePush(ctxt, xmlXPathNewNodeSet(NULL)); - - if (fragment != NULL) - xmlFree(fragment); - - return; - } - } else - doc = xsltdoc->doc; - - if ( fragment == NULL ) { - valuePush(ctxt, - xmlXPathNewNodeSet((xmlNodePtr) doc)); - return; - } - - /* use XPointer of HTML location for fragment ID */ -#ifdef LIBXML_XPTR_ENABLED - xptrctxt = xmlXPtrNewContext(doc, NULL, NULL); - if (xptrctxt == NULL) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "document() : internal error xptrctxt == NULL\n"); - goto out_fragment; - } - - object = xmlXPtrEval(fragment, xptrctxt); -#endif - xmlFree(fragment); - if (xptrctxt != NULL) - xmlXPathFreeContext(xptrctxt); - - if (object == NULL) - goto out_fragment; - - switch (object->type) { - case XPATH_NODESET: - break; - case XPATH_UNDEFINED: - case XPATH_BOOLEAN: - case XPATH_NUMBER: - case XPATH_STRING: - case XPATH_POINT: - case XPATH_USERS: - case XPATH_XSLT_TREE: - case XPATH_RANGE: - case XPATH_LOCATIONSET: - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "document() : XPointer does not select a node set: #%s\n", - fragment); - goto out_object; - } - - valuePush(ctxt, object); - return; - -out_object: - xmlXPathFreeObject(object); - -out_fragment: - valuePush(ctxt, xmlXPathNewNodeSet(NULL)); -} - -/** - * xsltDocumentFunction: - * @ctxt: the XPath Parser context - * @nargs: the number of arguments - * - * Implement the document() XSLT function - * node-set document(object, node-set?) - */ -void -xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs) -{ - xmlXPathObjectPtr obj, obj2 = NULL; - xmlChar *base = NULL, *URI; - - - if ((nargs < 1) || (nargs > 2)) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "document() : invalid number of args %d\n", - nargs); - ctxt->error = XPATH_INVALID_ARITY; - return; - } - if (ctxt->value == NULL) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "document() : invalid arg value\n"); - ctxt->error = XPATH_INVALID_TYPE; - return; - } - - if (nargs == 2) { - if (ctxt->value->type != XPATH_NODESET) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "document() : invalid arg expecting a nodeset\n"); - ctxt->error = XPATH_INVALID_TYPE; - return; - } - - obj2 = valuePop(ctxt); - } - - if (ctxt->value->type == XPATH_NODESET) { - int i; - xmlXPathObjectPtr newobj, ret; - - obj = valuePop(ctxt); - ret = xmlXPathNewNodeSet(NULL); - - if (obj->nodesetval) { - for (i = 0; i < obj->nodesetval->nodeNr; i++) { - valuePush(ctxt, - xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i])); - xmlXPathStringFunction(ctxt, 1); - if (nargs == 2) { - valuePush(ctxt, xmlXPathObjectCopy(obj2)); - } else { - valuePush(ctxt, - xmlXPathNewNodeSet(obj->nodesetval-> - nodeTab[i])); - } - xsltDocumentFunction(ctxt, 2); - newobj = valuePop(ctxt); - ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, - newobj->nodesetval); - xmlXPathFreeObject(newobj); - } - } - - xmlXPathFreeObject(obj); - if (obj2 != NULL) - xmlXPathFreeObject(obj2); - valuePush(ctxt, ret); - return; - } - /* - * Make sure it's converted to a string - */ - xmlXPathStringFunction(ctxt, 1); - if (ctxt->value->type != XPATH_STRING) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "document() : invalid arg expecting a string\n"); - ctxt->error = XPATH_INVALID_TYPE; - if (obj2 != NULL) - xmlXPathFreeObject(obj2); - return; - } - obj = valuePop(ctxt); - if (obj->stringval == NULL) { - valuePush(ctxt, xmlXPathNewNodeSet(NULL)); - } else { - if ((obj2 != NULL) && (obj2->nodesetval != NULL) && - (obj2->nodesetval->nodeNr > 0) && - IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) { - xmlNodePtr target; - - target = obj2->nodesetval->nodeTab[0]; - if ((target->type == XML_ATTRIBUTE_NODE) || - (target->type == XML_PI_NODE)) { - target = ((xmlAttrPtr) target)->parent; - } - base = xmlNodeGetBase(target->doc, target); - } else { - xsltTransformContextPtr tctxt; - - tctxt = xsltXPathGetTransformContext(ctxt); - if ((tctxt != NULL) && (tctxt->inst != NULL)) { - base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst); - } else if ((tctxt != NULL) && (tctxt->style != NULL) && - (tctxt->style->doc != NULL)) { - base = xmlNodeGetBase(tctxt->style->doc, - (xmlNodePtr) tctxt->style->doc); - } - } - URI = xmlBuildURI(obj->stringval, base); - if (base != NULL) - xmlFree(base); - if (URI == NULL) { - valuePush(ctxt, xmlXPathNewNodeSet(NULL)); - } else { - xsltDocumentFunctionLoadDocument( ctxt, URI ); - xmlFree(URI); - } - } - xmlXPathFreeObject(obj); - if (obj2 != NULL) - xmlXPathFreeObject(obj2); -} - -/** - * xsltKeyFunction: - * @ctxt: the XPath Parser context - * @nargs: the number of arguments - * - * Implement the key() XSLT function - * node-set key(string, object) - */ -void -xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){ - xmlNodeSetPtr nodelist; - xmlXPathObjectPtr obj1, obj2; - xmlChar *key = NULL, *value; - const xmlChar *keyURI; - xsltTransformContextPtr tctxt; - xsltDocumentPtr oldDocumentPtr; - xmlDocPtr oldXPathDocPtr; - - if (nargs != 2) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "key() : expects two arguments\n"); - ctxt->error = XPATH_INVALID_ARITY; - return; - } - - obj2 = valuePop(ctxt); - xmlXPathStringFunction(ctxt, 1); - if ((obj2 == NULL) || - (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "key() : invalid arg expecting a string\n"); - ctxt->error = XPATH_INVALID_TYPE; - xmlXPathFreeObject(obj2); - - return; - } - obj1 = valuePop(ctxt); - - if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) { - int i; - xmlXPathObjectPtr newobj, ret; - - ret = xmlXPathNewNodeSet(NULL); - - if (obj2->nodesetval != NULL) { - for (i = 0; i < obj2->nodesetval->nodeNr; i++) { - valuePush(ctxt, xmlXPathObjectCopy(obj1)); - valuePush(ctxt, - xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i])); - xmlXPathStringFunction(ctxt, 1); - xsltKeyFunction(ctxt, 2); - newobj = valuePop(ctxt); - ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, - newobj->nodesetval); - xmlXPathFreeObject(newobj); - } - } - valuePush(ctxt, ret); - } else { - xmlChar *qname, *prefix; - - /* - * Get the associated namespace URI if qualified name - */ - qname = obj1->stringval; - key = xmlSplitQName2(qname, &prefix); - if (key == NULL) { - key = xmlStrdup(obj1->stringval); - keyURI = NULL; - if (prefix != NULL) - xmlFree(prefix); - } else { - if (prefix != NULL) { - keyURI = xmlXPathNsLookup(ctxt->context, prefix); - if (keyURI == NULL) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "key() : prefix %s is not bound\n", prefix); - } - xmlFree(prefix); - } else { - keyURI = NULL; - } - } - - /* - * Force conversion of first arg to string - */ - valuePush(ctxt, obj2); - xmlXPathStringFunction(ctxt, 1); - if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "key() : invalid arg expecting a string\n"); - ctxt->error = XPATH_INVALID_TYPE; - xmlXPathFreeObject(obj1); - - return; - } - obj2 = valuePop(ctxt); - value = obj2->stringval; - - tctxt = xsltXPathGetTransformContext(ctxt); - oldDocumentPtr = tctxt->document; - oldXPathDocPtr = tctxt->xpathCtxt->doc; - if ((ctxt->context->doc != NULL) && - (tctxt->document->doc != ctxt->context->doc)) { - /* - * The xpath context document needs to be changed. If the - * current context document is a node-set, we must use an - * xsltDocument associated with the node-set, which may or - * may not currently exist. - */ - if (xmlStrEqual((const xmlChar *)ctxt->context->doc->name, - BAD_CAST " fake node libxslt")) { /* node-set */ - /* - * Check whether we already have an xsltDocument set up - */ - if (ctxt->context->doc->_private == NULL) /* nope */ - ctxt->context->doc->_private = - xsltNewDocument(tctxt, ctxt->context->doc); - tctxt->document = ctxt->context->doc->_private; - } - else { - tctxt->document = xsltFindDocument(tctxt, ctxt->context->doc); - if (tctxt->document == NULL) - tctxt->document = oldDocumentPtr; - else - tctxt->xpathCtxt->doc = ctxt->context->doc; - } - } - nodelist = xsltGetKey(tctxt, key, keyURI, value); - tctxt->document = oldDocumentPtr; - tctxt->xpathCtxt->doc = oldXPathDocPtr; - valuePush(ctxt, xmlXPathWrapNodeSet( - xmlXPathNodeSetMerge(NULL, nodelist))); - } - - - if (obj1 != NULL) - xmlXPathFreeObject(obj1); - if (obj2 != NULL) - xmlXPathFreeObject(obj2); - if (key != NULL) - xmlFree(key); -} - -/** - * xsltUnparsedEntityURIFunction: - * @ctxt: the XPath Parser context - * @nargs: the number of arguments - * - * Implement the unparsed-entity-uri() XSLT function - * string unparsed-entity-uri(string) - */ -void -xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){ - xmlXPathObjectPtr obj; - xmlChar *str; - - if ((nargs != 1) || (ctxt->value == NULL)) { - xsltGenericError(xsltGenericErrorContext, - "unparsed-entity-uri() : expects one string arg\n"); - ctxt->error = XPATH_INVALID_ARITY; - return; - } - obj = valuePop(ctxt); - if (obj->type != XPATH_STRING) { - obj = xmlXPathConvertString(obj); - } - - str = obj->stringval; - if (str == NULL) { - valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); - } else { - xmlEntityPtr entity; - - entity = xmlGetDocEntity(ctxt->context->doc, str); - if (entity == NULL) { - valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); - } else { - if (entity->URI != NULL) - valuePush(ctxt, xmlXPathNewString(entity->URI)); - else - valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); - } - } - xmlXPathFreeObject(obj); -} - -/** - * xsltFormatNumberFunction: - * @ctxt: the XPath Parser context - * @nargs: the number of arguments - * - * Implement the format-number() XSLT function - * string format-number(number, string, string?) - */ -void -xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) -{ - xmlXPathObjectPtr numberObj = NULL; - xmlXPathObjectPtr formatObj = NULL; - xmlXPathObjectPtr decimalObj = NULL; - xsltStylesheetPtr sheet; - xsltDecimalFormatPtr formatValues; - xmlChar *result; - xsltTransformContextPtr tctxt; - - tctxt = xsltXPathGetTransformContext(ctxt); - if (tctxt == NULL) - return; - sheet = tctxt->style; - if (sheet == NULL) - return; - formatValues = sheet->decimalFormat; - - switch (nargs) { - case 3: - CAST_TO_STRING; - decimalObj = valuePop(ctxt); - formatValues = xsltDecimalFormatGetByName(sheet, decimalObj->stringval); - if (formatValues == NULL) { - xsltTransformError(tctxt, NULL, NULL, - "format-number() : undeclared decimal format '%s'\n", - decimalObj->stringval); - } - /* Intentional fall-through */ - case 2: - CAST_TO_STRING; - formatObj = valuePop(ctxt); - CAST_TO_NUMBER; - numberObj = valuePop(ctxt); - break; - default: - XP_ERROR(XPATH_INVALID_ARITY); - } - - if (formatValues != NULL) { - if (xsltFormatNumberConversion(formatValues, - formatObj->stringval, - numberObj->floatval, - &result) == XPATH_EXPRESSION_OK) { - valuePush(ctxt, xmlXPathNewString(result)); - xmlFree(result); - } - } - - xmlXPathFreeObject(numberObj); - xmlXPathFreeObject(formatObj); - xmlXPathFreeObject(decimalObj); -} - -/** - * xsltGenerateIdFunction: - * @ctxt: the XPath Parser context - * @nargs: the number of arguments - * - * Implement the generate-id() XSLT function - * string generate-id(node-set?) - */ -void -xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){ - xmlNodePtr cur = NULL; - unsigned long val; - xmlChar str[20]; - - if (nargs == 0) { - cur = ctxt->context->node; - } else if (nargs == 1) { - xmlXPathObjectPtr obj; - xmlNodeSetPtr nodelist; - int i, ret; - - if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) { - ctxt->error = XPATH_INVALID_TYPE; - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "generate-id() : invalid arg expecting a node-set\n"); - return; - } - obj = valuePop(ctxt); - nodelist = obj->nodesetval; - if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) { - xmlXPathFreeObject(obj); - valuePush(ctxt, xmlXPathNewCString("")); - return; - } - cur = nodelist->nodeTab[0]; - for (i = 1;i < nodelist->nodeNr;i++) { - ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]); - if (ret == -1) - cur = nodelist->nodeTab[i]; - } - xmlXPathFreeObject(obj); - } else { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "generate-id() : invalid number of args %d\n", nargs); - ctxt->error = XPATH_INVALID_ARITY; - return; - } - /* - * Okay this is ugly but should work, use the NodePtr address - * to forge the ID - */ - val = (unsigned long)((char *)cur - (char *)0); - val /= sizeof(xmlNode); - sprintf((char *)str, "id%ld", val); - valuePush(ctxt, xmlXPathNewString(str)); -} - -/** - * xsltSystemPropertyFunction: - * @ctxt: the XPath Parser context - * @nargs: the number of arguments - * - * Implement the system-property() XSLT function - * object system-property(string) - */ -void -xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){ - xmlXPathObjectPtr obj; - xmlChar *prefix, *name; - const xmlChar *nsURI = NULL; - - if (nargs != 1) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "system-property() : expects one string arg\n"); - ctxt->error = XPATH_INVALID_ARITY; - return; - } - if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "system-property() : invalid arg expecting a string\n"); - ctxt->error = XPATH_INVALID_TYPE; - return; - } - obj = valuePop(ctxt); - if (obj->stringval == NULL) { - valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); - } else { - name = xmlSplitQName2(obj->stringval, &prefix); - if (name == NULL) { - name = xmlStrdup(obj->stringval); - } else { - nsURI = xmlXPathNsLookup(ctxt->context, prefix); - if (nsURI == NULL) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "system-property() : prefix %s is not bound\n", prefix); - } - } - - if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) { -#ifdef DOCBOOK_XSL_HACK - if (xmlStrEqual(name, (const xmlChar *)"vendor")) { - xsltStylesheetPtr sheet; - xsltTransformContextPtr tctxt; - - tctxt = xsltXPathGetTransformContext(ctxt); - if ((tctxt != NULL) && (tctxt->inst != NULL) && - (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) && - (tctxt->inst->parent != NULL) && - (xmlStrEqual(tctxt->inst->parent->name, - BAD_CAST "template"))) - sheet = tctxt->style; - else - sheet = NULL; - if ((sheet != NULL) && (sheet->doc != NULL) && - (sheet->doc->URL != NULL) && - (xmlStrstr(sheet->doc->URL, - (const xmlChar *)"chunk") != NULL)) { - valuePush(ctxt, xmlXPathNewString( - (const xmlChar *)"libxslt (SAXON 6.2 compatible)")); - - } else { - valuePush(ctxt, xmlXPathNewString( - (const xmlChar *)XSLT_DEFAULT_VENDOR)); - } - } else -#else - if (xmlStrEqual(name, (const xmlChar *)"vendor")) { - valuePush(ctxt, xmlXPathNewString( - (const xmlChar *)XSLT_DEFAULT_VENDOR)); - } else -#endif - if (xmlStrEqual(name, (const xmlChar *)"version")) { - valuePush(ctxt, xmlXPathNewString( - (const xmlChar *)XSLT_DEFAULT_VERSION)); - } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) { - valuePush(ctxt, xmlXPathNewString( - (const xmlChar *)XSLT_DEFAULT_URL)); - } else { - valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); - } - } - if (name != NULL) - xmlFree(name); - if (prefix != NULL) - xmlFree(prefix); - } - xmlXPathFreeObject(obj); -} - -/** - * xsltElementAvailableFunction: - * @ctxt: the XPath Parser context - * @nargs: the number of arguments - * - * Implement the element-available() XSLT function - * boolean element-available(string) - */ -void -xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ - xmlXPathObjectPtr obj; - xmlChar *prefix, *name; - const xmlChar *nsURI = NULL; - xsltTransformContextPtr tctxt; - - if (nargs != 1) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "element-available() : expects one string arg\n"); - ctxt->error = XPATH_INVALID_ARITY; - return; - } - if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "element-available() : invalid arg expecting a string\n"); - ctxt->error = XPATH_INVALID_TYPE; - return; - } - obj = valuePop(ctxt); - tctxt = xsltXPathGetTransformContext(ctxt); - if (tctxt == NULL) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "element-available() : internal error tctxt == NULL\n"); - xmlXPathFreeObject(obj); - valuePush(ctxt, xmlXPathNewBoolean(0)); - return; - } - - - name = xmlSplitQName2(obj->stringval, &prefix); - if (name == NULL) { - xmlNsPtr ns; - - name = xmlStrdup(obj->stringval); - ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL); - if (ns != NULL) nsURI = xmlStrdup(ns->href); - } else { - nsURI = xmlXPathNsLookup(ctxt->context, prefix); - if (nsURI == NULL) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "element-available() : prefix %s is not bound\n", prefix); - } - } - - if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) { - valuePush(ctxt, xmlXPathNewBoolean(1)); - } else { - valuePush(ctxt, xmlXPathNewBoolean(0)); - } - - xmlXPathFreeObject(obj); - if (name != NULL) - xmlFree(name); - if (prefix != NULL) - xmlFree(prefix); -} - -/** - * xsltFunctionAvailableFunction: - * @ctxt: the XPath Parser context - * @nargs: the number of arguments - * - * Implement the function-available() XSLT function - * boolean function-available(string) - */ -void -xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ - xmlXPathObjectPtr obj; - xmlChar *prefix, *name; - const xmlChar *nsURI = NULL; - - if (nargs != 1) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "function-available() : expects one string arg\n"); - ctxt->error = XPATH_INVALID_ARITY; - return; - } - if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "function-available() : invalid arg expecting a string\n"); - ctxt->error = XPATH_INVALID_TYPE; - return; - } - obj = valuePop(ctxt); - - name = xmlSplitQName2(obj->stringval, &prefix); - if (name == NULL) { - name = xmlStrdup(obj->stringval); - } else { - nsURI = xmlXPathNsLookup(ctxt->context, prefix); - if (nsURI == NULL) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "function-available() : prefix %s is not bound\n", prefix); - } - } - - if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) { - valuePush(ctxt, xmlXPathNewBoolean(1)); - } else { - valuePush(ctxt, xmlXPathNewBoolean(0)); - } - - xmlXPathFreeObject(obj); - if (name != NULL) - xmlFree(name); - if (prefix != NULL) - xmlFree(prefix); -} - -/** - * xsltCurrentFunction: - * @ctxt: the XPath Parser context - * @nargs: the number of arguments - * - * Implement the current() XSLT function - * node-set current() - */ -static void -xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){ - xsltTransformContextPtr tctxt; - - if (nargs != 0) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "current() : function uses no argument\n"); - ctxt->error = XPATH_INVALID_ARITY; - return; - } - tctxt = xsltXPathGetTransformContext(ctxt); - if (tctxt == NULL) { - xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, - "current() : internal error tctxt == NULL\n"); - valuePush(ctxt, xmlXPathNewNodeSet(NULL)); - } else { - valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */ - } -} - -/************************************************************************ - * * - * Registration of XSLT and libxslt functions * - * * - ************************************************************************/ - -/** - * xsltRegisterAllFunctions: - * @ctxt: the XPath context - * - * Registers all default XSLT functions in this context - */ -void -xsltRegisterAllFunctions(xmlXPathContextPtr ctxt) -{ - xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current", - xsltCurrentFunction); - xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document", - xsltDocumentFunction); - xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction); - xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri", - xsltUnparsedEntityURIFunction); - xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number", - xsltFormatNumberFunction); - xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id", - xsltGenerateIdFunction); - xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property", - xsltSystemPropertyFunction); - xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available", - xsltElementAvailableFunction); - xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available", - xsltFunctionAvailableFunction); -} +/* + * functions.c: Implementation of the XSLT extra functions + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + * Bjorn Reese for number formatting + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_CTYPE_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "functions.h" +#include "extensions.h" +#include "numbersInternals.h" +#include "keys.h" +#include "documents.h" + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_FUNCTION +#endif + +/* + * Some versions of DocBook XSL use the vendor string to detect + * supporting chunking, this is a workaround to be considered + * in the list of decent XSLT processors + */ +#define DOCBOOK_XSL_HACK + +/** + * xsltXPathFunctionLookup: + * @ctxt: a void * but the XSLT transformation context actually + * @name: the function name + * @ns_uri: the function namespace URI + * + * This is the entry point when a function is needed by the XPath + * interpretor. + * + * Returns the callback function or NULL if not found + */ +xmlXPathFunction +xsltXPathFunctionLookup (xmlXPathContextPtr ctxt, + const xmlChar *name, const xmlChar *ns_uri) { + xmlXPathFunction ret; + + if ((ctxt == NULL) || (name == NULL) || (ns_uri == NULL)) + return (NULL); + +#ifdef WITH_XSLT_DEBUG_FUNCTION + xsltGenericDebug(xsltGenericDebugContext, + "Lookup function {%s}%s\n", ns_uri, name); +#endif + + /* give priority to context-level functions */ + /* + ret = (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri); + */ + XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri); + + if (ret == NULL) + ret = xsltExtModuleFunctionLookup(name, ns_uri); + +#ifdef WITH_XSLT_DEBUG_FUNCTION + if (ret != NULL) + xsltGenericDebug(xsltGenericDebugContext, + "found function %s\n", name); +#endif + return(ret); +} + + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +static void +xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI) +{ + xsltTransformContextPtr tctxt; + xmlURIPtr uri; + xmlChar *fragment; + xsltDocumentPtr idoc; /* document info */ + xmlDocPtr doc; + xmlXPathContextPtr xptrctxt = NULL; + xmlXPathObjectPtr resObj = NULL; + + tctxt = xsltXPathGetTransformContext(ctxt); + if (tctxt == NULL) { + xsltTransformError(NULL, NULL, NULL, + "document() : internal error tctxt == NULL\n"); + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); + return; + } + + uri = xmlParseURI((const char *) URI); + if (uri == NULL) { + xsltTransformError(tctxt, NULL, NULL, + "document() : failed to parse URI\n"); + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); + return; + } + + /* + * check for and remove fragment identifier + */ + fragment = (xmlChar *)uri->fragment; + if (fragment != NULL) { + uri->fragment = NULL; + URI = xmlSaveUri(uri); + idoc = xsltLoadDocument(tctxt, URI); + xmlFree(URI); + } else + idoc = xsltLoadDocument(tctxt, URI); + xmlFreeURI(uri); + + if (idoc == NULL) { + if ((URI == NULL) || + (URI[0] == '#') || + (xmlStrEqual(tctxt->style->doc->URL, URI))) + { + /* + * This selects the stylesheet's doc itself. + */ + doc = tctxt->style->doc; + } else { + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); + + if (fragment != NULL) + xmlFree(fragment); + + return; + } + } else + doc = idoc->doc; + + if (fragment == NULL) { + valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc)); + return; + } + + /* use XPointer of HTML location for fragment ID */ +#ifdef LIBXML_XPTR_ENABLED + xptrctxt = xmlXPtrNewContext(doc, NULL, NULL); + if (xptrctxt == NULL) { + xsltTransformError(tctxt, NULL, NULL, + "document() : internal error xptrctxt == NULL\n"); + goto out_fragment; + } + + resObj = xmlXPtrEval(fragment, xptrctxt); + xmlXPathFreeContext(xptrctxt); +#endif + xmlFree(fragment); + + if (resObj == NULL) + goto out_fragment; + + switch (resObj->type) { + case XPATH_NODESET: + break; + case XPATH_UNDEFINED: + case XPATH_BOOLEAN: + case XPATH_NUMBER: + case XPATH_STRING: + case XPATH_POINT: + case XPATH_USERS: + case XPATH_XSLT_TREE: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + xsltTransformError(tctxt, NULL, NULL, + "document() : XPointer does not select a node set: #%s\n", + fragment); + goto out_object; + } + + valuePush(ctxt, resObj); + return; + +out_object: + xmlXPathFreeObject(resObj); + +out_fragment: + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); +} + +/** + * xsltDocumentFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the document() XSLT function + * node-set document(object, node-set?) + */ +void +xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs) +{ + xmlXPathObjectPtr obj, obj2 = NULL; + xmlChar *base = NULL, *URI; + + + if ((nargs < 1) || (nargs > 2)) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "document() : invalid number of args %d\n", + nargs); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + if (ctxt->value == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "document() : invalid arg value\n"); + ctxt->error = XPATH_INVALID_TYPE; + return; + } + + if (nargs == 2) { + if (ctxt->value->type != XPATH_NODESET) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "document() : invalid arg expecting a nodeset\n"); + ctxt->error = XPATH_INVALID_TYPE; + return; + } + + obj2 = valuePop(ctxt); + } + + if (ctxt->value->type == XPATH_NODESET) { + int i; + xmlXPathObjectPtr newobj, ret; + + obj = valuePop(ctxt); + ret = xmlXPathNewNodeSet(NULL); + + if (obj->nodesetval) { + for (i = 0; i < obj->nodesetval->nodeNr; i++) { + valuePush(ctxt, + xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i])); + xmlXPathStringFunction(ctxt, 1); + if (nargs == 2) { + valuePush(ctxt, xmlXPathObjectCopy(obj2)); + } else { + valuePush(ctxt, + xmlXPathNewNodeSet(obj->nodesetval-> + nodeTab[i])); + } + xsltDocumentFunction(ctxt, 2); + newobj = valuePop(ctxt); + ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, + newobj->nodesetval); + xmlXPathFreeObject(newobj); + } + } + + xmlXPathFreeObject(obj); + if (obj2 != NULL) + xmlXPathFreeObject(obj2); + valuePush(ctxt, ret); + return; + } + /* + * Make sure it's converted to a string + */ + xmlXPathStringFunction(ctxt, 1); + if (ctxt->value->type != XPATH_STRING) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "document() : invalid arg expecting a string\n"); + ctxt->error = XPATH_INVALID_TYPE; + if (obj2 != NULL) + xmlXPathFreeObject(obj2); + return; + } + obj = valuePop(ctxt); + if (obj->stringval == NULL) { + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); + } else { + if ((obj2 != NULL) && (obj2->nodesetval != NULL) && + (obj2->nodesetval->nodeNr > 0) && + IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) { + xmlNodePtr target; + + target = obj2->nodesetval->nodeTab[0]; + if ((target->type == XML_ATTRIBUTE_NODE) || + (target->type == XML_PI_NODE)) { + target = ((xmlAttrPtr) target)->parent; + } + base = xmlNodeGetBase(target->doc, target); + } else { + xsltTransformContextPtr tctxt; + + tctxt = xsltXPathGetTransformContext(ctxt); + if ((tctxt != NULL) && (tctxt->inst != NULL)) { + base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst); + } else if ((tctxt != NULL) && (tctxt->style != NULL) && + (tctxt->style->doc != NULL)) { + base = xmlNodeGetBase(tctxt->style->doc, + (xmlNodePtr) tctxt->style->doc); + } + } + URI = xmlBuildURI(obj->stringval, base); + if (base != NULL) + xmlFree(base); + if (URI == NULL) { + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); + } else { + xsltDocumentFunctionLoadDocument( ctxt, URI ); + xmlFree(URI); + } + } + xmlXPathFreeObject(obj); + if (obj2 != NULL) + xmlXPathFreeObject(obj2); +} + +/** + * xsltKeyFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the key() XSLT function + * node-set key(string, object) + */ +void +xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){ + xmlXPathObjectPtr obj1, obj2; + + if (nargs != 2) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "key() : expects two arguments\n"); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + + /* + * Get the key's value. + */ + obj2 = valuePop(ctxt); + xmlXPathStringFunction(ctxt, 1); + if ((obj2 == NULL) || + (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "key() : invalid arg expecting a string\n"); + ctxt->error = XPATH_INVALID_TYPE; + xmlXPathFreeObject(obj2); + + return; + } + /* + * Get the key's name. + */ + obj1 = valuePop(ctxt); + + if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) { + int i; + xmlXPathObjectPtr newobj, ret; + + ret = xmlXPathNewNodeSet(NULL); + + if (obj2->nodesetval != NULL) { + for (i = 0; i < obj2->nodesetval->nodeNr; i++) { + valuePush(ctxt, xmlXPathObjectCopy(obj1)); + valuePush(ctxt, + xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i])); + xmlXPathStringFunction(ctxt, 1); + xsltKeyFunction(ctxt, 2); + newobj = valuePop(ctxt); + ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, + newobj->nodesetval); + xmlXPathFreeObject(newobj); + } + } + valuePush(ctxt, ret); + } else { + xmlNodeSetPtr nodelist = NULL; + xmlChar *key = NULL, *value; + const xmlChar *keyURI; + xsltTransformContextPtr tctxt; + xmlChar *qname, *prefix; + xmlXPathContextPtr xpctxt = ctxt->context; + xmlNodePtr tmpNode = NULL; + xsltDocumentPtr oldDocInfo; + + tctxt = xsltXPathGetTransformContext(ctxt); + + oldDocInfo = tctxt->document; + + if (xpctxt->node == NULL) { + xsltTransformError(tctxt, NULL, tctxt->inst, + "Internal error in xsltKeyFunction(): " + "The context node is not set on the XPath context.\n"); + tctxt->state = XSLT_STATE_STOPPED; + goto error; + } + /* + * Get the associated namespace URI if qualified name + */ + qname = obj1->stringval; + key = xmlSplitQName2(qname, &prefix); + if (key == NULL) { + key = xmlStrdup(obj1->stringval); + keyURI = NULL; + if (prefix != NULL) + xmlFree(prefix); + } else { + if (prefix != NULL) { + keyURI = xmlXPathNsLookup(xpctxt, prefix); + if (keyURI == NULL) { + xsltTransformError(tctxt, NULL, tctxt->inst, + "key() : prefix %s is not bound\n", prefix); + /* + * TODO: Shouldn't we stop here? + */ + } + xmlFree(prefix); + } else { + keyURI = NULL; + } + } + + /* + * Force conversion of first arg to string + */ + valuePush(ctxt, obj2); + xmlXPathStringFunction(ctxt, 1); + if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { + xsltTransformError(tctxt, NULL, tctxt->inst, + "key() : invalid arg expecting a string\n"); + ctxt->error = XPATH_INVALID_TYPE; + goto error; + } + obj2 = valuePop(ctxt); + value = obj2->stringval; + + /* + * We need to ensure that ctxt->document is available for + * xsltGetKey(). + * First find the relevant doc, which is the context node's + * owner doc; using context->doc is not safe, since + * the doc could have been acquired via the document() function, + * or the doc might be a Result Tree Fragment. + * FUTURE INFO: In XSLT 2.0 the key() function takes an additional + * argument indicating the doc to use. + */ + if (xpctxt->node->type == XML_NAMESPACE_DECL) { + /* + * REVISIT: This is a libxml hack! Check xpath.c for details. + * The XPath module sets the owner element of a ns-node on + * the ns->next field. + */ + if ((((xmlNsPtr) xpctxt->node)->next != NULL) && + (((xmlNsPtr) xpctxt->node)->next->type == XML_ELEMENT_NODE)) + { + tmpNode = (xmlNodePtr) ((xmlNsPtr) xpctxt->node)->next; + } + } else + tmpNode = xpctxt->node; + + if ((tmpNode == NULL) || (tmpNode->doc == NULL)) { + xsltTransformError(tctxt, NULL, tctxt->inst, + "Internal error in xsltKeyFunction(): " + "Couldn't get the doc of the XPath context node.\n"); + goto error; + } + + if ((tctxt->document == NULL) || + (tctxt->document->doc != tmpNode->doc)) + { + if (tmpNode->doc->name && (tmpNode->doc->name[0] == ' ')) { + /* + * This is a Result Tree Fragment. + */ + if (tmpNode->doc->_private == NULL) { + tmpNode->doc->_private = xsltNewDocument(tctxt, tmpNode->doc); + if (tmpNode->doc->_private == NULL) + goto error; + } + tctxt->document = (xsltDocumentPtr) tmpNode->doc->_private; + } else { + /* + * May be the initial source doc or a doc acquired via the + * document() function. + */ + tctxt->document = xsltFindDocument(tctxt, tmpNode->doc); + } + if (tctxt->document == NULL) { + xsltTransformError(tctxt, NULL, tctxt->inst, + "Internal error in xsltKeyFunction(): " + "Could not get the document info of a context doc.\n"); + tctxt->state = XSLT_STATE_STOPPED; + goto error; + } + } + /* + * Get/compute the key value. + */ + nodelist = xsltGetKey(tctxt, key, keyURI, value); + +error: + tctxt->document = oldDocInfo; + valuePush(ctxt, xmlXPathWrapNodeSet( + xmlXPathNodeSetMerge(NULL, nodelist))); + if (key != NULL) + xmlFree(key); + } + + if (obj1 != NULL) + xmlXPathFreeObject(obj1); + if (obj2 != NULL) + xmlXPathFreeObject(obj2); +} + +/** + * xsltUnparsedEntityURIFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the unparsed-entity-uri() XSLT function + * string unparsed-entity-uri(string) + */ +void +xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){ + xmlXPathObjectPtr obj; + xmlChar *str; + + if ((nargs != 1) || (ctxt->value == NULL)) { + xsltGenericError(xsltGenericErrorContext, + "unparsed-entity-uri() : expects one string arg\n"); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + obj = valuePop(ctxt); + if (obj->type != XPATH_STRING) { + obj = xmlXPathConvertString(obj); + } + + str = obj->stringval; + if (str == NULL) { + valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); + } else { + xmlEntityPtr entity; + + entity = xmlGetDocEntity(ctxt->context->doc, str); + if (entity == NULL) { + valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); + } else { + if (entity->URI != NULL) + valuePush(ctxt, xmlXPathNewString(entity->URI)); + else + valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); + } + } + xmlXPathFreeObject(obj); +} + +/** + * xsltFormatNumberFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the format-number() XSLT function + * string format-number(number, string, string?) + */ +void +xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) +{ + xmlXPathObjectPtr numberObj = NULL; + xmlXPathObjectPtr formatObj = NULL; + xmlXPathObjectPtr decimalObj = NULL; + xsltStylesheetPtr sheet; + xsltDecimalFormatPtr formatValues; + xmlChar *result; + xsltTransformContextPtr tctxt; + + tctxt = xsltXPathGetTransformContext(ctxt); + if (tctxt == NULL) + return; + sheet = tctxt->style; + if (sheet == NULL) + return; + formatValues = sheet->decimalFormat; + + switch (nargs) { + case 3: + CAST_TO_STRING; + decimalObj = valuePop(ctxt); + formatValues = xsltDecimalFormatGetByName(sheet, decimalObj->stringval); + if (formatValues == NULL) { + xsltTransformError(tctxt, NULL, NULL, + "format-number() : undeclared decimal format '%s'\n", + decimalObj->stringval); + } + /* Intentional fall-through */ + case 2: + CAST_TO_STRING; + formatObj = valuePop(ctxt); + CAST_TO_NUMBER; + numberObj = valuePop(ctxt); + break; + default: + XP_ERROR(XPATH_INVALID_ARITY); + } + + if (formatValues != NULL) { + if (xsltFormatNumberConversion(formatValues, + formatObj->stringval, + numberObj->floatval, + &result) == XPATH_EXPRESSION_OK) { + valuePush(ctxt, xmlXPathNewString(result)); + xmlFree(result); + } + } + + xmlXPathFreeObject(numberObj); + xmlXPathFreeObject(formatObj); + xmlXPathFreeObject(decimalObj); +} + +/** + * xsltGenerateIdFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the generate-id() XSLT function + * string generate-id(node-set?) + */ +void +xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){ + xmlNodePtr cur = NULL; + unsigned long val; + xmlChar str[20]; + + if (nargs == 0) { + cur = ctxt->context->node; + } else if (nargs == 1) { + xmlXPathObjectPtr obj; + xmlNodeSetPtr nodelist; + int i, ret; + + if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) { + ctxt->error = XPATH_INVALID_TYPE; + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "generate-id() : invalid arg expecting a node-set\n"); + return; + } + obj = valuePop(ctxt); + nodelist = obj->nodesetval; + if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) { + xmlXPathFreeObject(obj); + valuePush(ctxt, xmlXPathNewCString("")); + return; + } + cur = nodelist->nodeTab[0]; + for (i = 1;i < nodelist->nodeNr;i++) { + ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]); + if (ret == -1) + cur = nodelist->nodeTab[i]; + } + xmlXPathFreeObject(obj); + } else { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "generate-id() : invalid number of args %d\n", nargs); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + /* + * Okay this is ugly but should work, use the NodePtr address + * to forge the ID + */ + val = (unsigned long)((char *)cur - (char *)0); + val /= sizeof(xmlNode); + sprintf((char *)str, "id%ld", val); + valuePush(ctxt, xmlXPathNewString(str)); +} + +/** + * xsltSystemPropertyFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the system-property() XSLT function + * object system-property(string) + */ +void +xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){ + xmlXPathObjectPtr obj; + xmlChar *prefix, *name; + const xmlChar *nsURI = NULL; + + if (nargs != 1) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "system-property() : expects one string arg\n"); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "system-property() : invalid arg expecting a string\n"); + ctxt->error = XPATH_INVALID_TYPE; + return; + } + obj = valuePop(ctxt); + if (obj->stringval == NULL) { + valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); + } else { + name = xmlSplitQName2(obj->stringval, &prefix); + if (name == NULL) { + name = xmlStrdup(obj->stringval); + } else { + nsURI = xmlXPathNsLookup(ctxt->context, prefix); + if (nsURI == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "system-property() : prefix %s is not bound\n", prefix); + } + } + + if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) { +#ifdef DOCBOOK_XSL_HACK + if (xmlStrEqual(name, (const xmlChar *)"vendor")) { + xsltStylesheetPtr sheet; + xsltTransformContextPtr tctxt; + + tctxt = xsltXPathGetTransformContext(ctxt); + if ((tctxt != NULL) && (tctxt->inst != NULL) && + (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) && + (tctxt->inst->parent != NULL) && + (xmlStrEqual(tctxt->inst->parent->name, + BAD_CAST "template"))) + sheet = tctxt->style; + else + sheet = NULL; + if ((sheet != NULL) && (sheet->doc != NULL) && + (sheet->doc->URL != NULL) && + (xmlStrstr(sheet->doc->URL, + (const xmlChar *)"chunk") != NULL)) { + valuePush(ctxt, xmlXPathNewString( + (const xmlChar *)"libxslt (SAXON 6.2 compatible)")); + + } else { + valuePush(ctxt, xmlXPathNewString( + (const xmlChar *)XSLT_DEFAULT_VENDOR)); + } + } else +#else + if (xmlStrEqual(name, (const xmlChar *)"vendor")) { + valuePush(ctxt, xmlXPathNewString( + (const xmlChar *)XSLT_DEFAULT_VENDOR)); + } else +#endif + if (xmlStrEqual(name, (const xmlChar *)"version")) { + valuePush(ctxt, xmlXPathNewString( + (const xmlChar *)XSLT_DEFAULT_VERSION)); + } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) { + valuePush(ctxt, xmlXPathNewString( + (const xmlChar *)XSLT_DEFAULT_URL)); + } else { + valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); + } + } + if (name != NULL) + xmlFree(name); + if (prefix != NULL) + xmlFree(prefix); + } + xmlXPathFreeObject(obj); +} + +/** + * xsltElementAvailableFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the element-available() XSLT function + * boolean element-available(string) + */ +void +xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ + xmlXPathObjectPtr obj; + xmlChar *prefix, *name; + const xmlChar *nsURI = NULL; + xsltTransformContextPtr tctxt; + + if (nargs != 1) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "element-available() : expects one string arg\n"); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "element-available() : invalid arg expecting a string\n"); + ctxt->error = XPATH_INVALID_TYPE; + return; + } + obj = valuePop(ctxt); + tctxt = xsltXPathGetTransformContext(ctxt); + if (tctxt == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "element-available() : internal error tctxt == NULL\n"); + xmlXPathFreeObject(obj); + valuePush(ctxt, xmlXPathNewBoolean(0)); + return; + } + + + name = xmlSplitQName2(obj->stringval, &prefix); + if (name == NULL) { + xmlNsPtr ns; + + name = xmlStrdup(obj->stringval); + ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL); + if (ns != NULL) nsURI = xmlStrdup(ns->href); + } else { + nsURI = xmlXPathNsLookup(ctxt->context, prefix); + if (nsURI == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "element-available() : prefix %s is not bound\n", prefix); + } + } + + if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) { + valuePush(ctxt, xmlXPathNewBoolean(1)); + } else { + valuePush(ctxt, xmlXPathNewBoolean(0)); + } + + xmlXPathFreeObject(obj); + if (name != NULL) + xmlFree(name); + if (prefix != NULL) + xmlFree(prefix); +} + +/** + * xsltFunctionAvailableFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the function-available() XSLT function + * boolean function-available(string) + */ +void +xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ + xmlXPathObjectPtr obj; + xmlChar *prefix, *name; + const xmlChar *nsURI = NULL; + + if (nargs != 1) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "function-available() : expects one string arg\n"); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "function-available() : invalid arg expecting a string\n"); + ctxt->error = XPATH_INVALID_TYPE; + return; + } + obj = valuePop(ctxt); + + name = xmlSplitQName2(obj->stringval, &prefix); + if (name == NULL) { + name = xmlStrdup(obj->stringval); + } else { + nsURI = xmlXPathNsLookup(ctxt->context, prefix); + if (nsURI == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "function-available() : prefix %s is not bound\n", prefix); + } + } + + if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) { + valuePush(ctxt, xmlXPathNewBoolean(1)); + } else { + valuePush(ctxt, xmlXPathNewBoolean(0)); + } + + xmlXPathFreeObject(obj); + if (name != NULL) + xmlFree(name); + if (prefix != NULL) + xmlFree(prefix); +} + +/** + * xsltCurrentFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the current() XSLT function + * node-set current() + */ +static void +xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){ + xsltTransformContextPtr tctxt; + + if (nargs != 0) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "current() : function uses no argument\n"); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + tctxt = xsltXPathGetTransformContext(ctxt); + if (tctxt == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "current() : internal error tctxt == NULL\n"); + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); + } else { + valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */ + } +} + +/************************************************************************ + * * + * Registration of XSLT and libxslt functions * + * * + ************************************************************************/ + +/** + * xsltRegisterAllFunctions: + * @ctxt: the XPath context + * + * Registers all default XSLT functions in this context + */ +void +xsltRegisterAllFunctions(xmlXPathContextPtr ctxt) +{ + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current", + xsltCurrentFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document", + xsltDocumentFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri", + xsltUnparsedEntityURIFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number", + xsltFormatNumberFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id", + xsltGenerateIdFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property", + xsltSystemPropertyFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available", + xsltElementAvailableFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available", + xsltFunctionAvailableFunction); +} diff --git a/libxslt/keys.c b/libxslt/keys.c index 220b7415..c0545619 100644 --- a/libxslt/keys.c +++ b/libxslt/keys.c @@ -1,779 +1,854 @@ -/* - * keys.c: Implemetation of the keys support - * - * Reference: - * http://www.w3.org/TR/1999/REC-xslt-19991116 - * - * See Copyright for the status of this software. - * - * daniel@veillard.com - */ - -#define IN_LIBXSLT -#include "libxslt.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include "xslt.h" -#include "xsltInternals.h" -#include "xsltutils.h" -#include "imports.h" -#include "templates.h" -#include "keys.h" - -#ifdef WITH_XSLT_DEBUG -#define WITH_XSLT_DEBUG_KEYS -#endif - - -/************************************************************************ - * * - * Type functions * - * * - ************************************************************************/ - -/** - * xsltNewKeyDef: - * @name: the key name or NULL - * @nameURI: the name URI or NULL - * - * Create a new XSLT KeyDef - * - * Returns the newly allocated xsltKeyDefPtr or NULL in case of error - */ -static xsltKeyDefPtr -xsltNewKeyDef(const xmlChar *name, const xmlChar *nameURI) { - xsltKeyDefPtr cur; - - cur = (xsltKeyDefPtr) xmlMalloc(sizeof(xsltKeyDef)); - if (cur == NULL) { - xsltTransformError(NULL, NULL, NULL, - "xsltNewKeyDef : malloc failed\n"); - return(NULL); - } - memset(cur, 0, sizeof(xsltKeyDef)); - if (name != NULL) - cur->name = xmlStrdup(name); - if (nameURI != NULL) - cur->nameURI = xmlStrdup(nameURI); - cur->nsList = NULL; - return(cur); -} - -/** - * xsltFreeKeyDef: - * @keyd: an XSLT key definition - * - * Free up the memory allocated by @keyd - */ -static void -xsltFreeKeyDef(xsltKeyDefPtr keyd) { - if (keyd == NULL) - return; - if (keyd->comp != NULL) - xmlXPathFreeCompExpr(keyd->comp); - if (keyd->usecomp != NULL) - xmlXPathFreeCompExpr(keyd->usecomp); - if (keyd->name != NULL) - xmlFree(keyd->name); - if (keyd->nameURI != NULL) - xmlFree(keyd->nameURI); - if (keyd->match != NULL) - xmlFree(keyd->match); - if (keyd->use != NULL) - xmlFree(keyd->use); - if (keyd->nsList != NULL) - xmlFree(keyd->nsList); - memset(keyd, -1, sizeof(xsltKeyDef)); - xmlFree(keyd); -} - -/** - * xsltFreeKeyDefList: - * @keyd: an XSLT key definition list - * - * Free up the memory allocated by all the elements of @keyd - */ -static void -xsltFreeKeyDefList(xsltKeyDefPtr keyd) { - xsltKeyDefPtr cur; - - while (keyd != NULL) { - cur = keyd; - keyd = keyd->next; - xsltFreeKeyDef(cur); - } -} - -/** - * xsltNewKeyTable: - * @name: the key name or NULL - * @nameURI: the name URI or NULL - * - * Create a new XSLT KeyTable - * - * Returns the newly allocated xsltKeyTablePtr or NULL in case of error - */ -static xsltKeyTablePtr -xsltNewKeyTable(const xmlChar *name, const xmlChar *nameURI) { - xsltKeyTablePtr cur; - - cur = (xsltKeyTablePtr) xmlMalloc(sizeof(xsltKeyTable)); - if (cur == NULL) { - xsltTransformError(NULL, NULL, NULL, - "xsltNewKeyTable : malloc failed\n"); - return(NULL); - } - memset(cur, 0, sizeof(xsltKeyTable)); - if (name != NULL) - cur->name = xmlStrdup(name); - if (nameURI != NULL) - cur->nameURI = xmlStrdup(nameURI); - cur->keys = xmlHashCreate(0); - return(cur); -} - -/** - * xsltFreeKeyTable: - * @keyt: an XSLT key table - * - * Free up the memory allocated by @keyt - */ -static void -xsltFreeKeyTable(xsltKeyTablePtr keyt) { - if (keyt == NULL) - return; - if (keyt->name != NULL) - xmlFree(keyt->name); - if (keyt->nameURI != NULL) - xmlFree(keyt->nameURI); - if (keyt->keys != NULL) - xmlHashFree(keyt->keys, - (xmlHashDeallocator) xmlXPathFreeNodeSet); - memset(keyt, -1, sizeof(xsltKeyTable)); - xmlFree(keyt); -} - -/** - * xsltFreeKeyTableList: - * @keyt: an XSLT key table list - * - * Free up the memory allocated by all the elements of @keyt - */ -static void -xsltFreeKeyTableList(xsltKeyTablePtr keyt) { - xsltKeyTablePtr cur; - - while (keyt != NULL) { - cur = keyt; - keyt = keyt->next; - xsltFreeKeyTable(cur); - } -} - -/************************************************************************ - * * - * The interpreter for the precompiled patterns * - * * - ************************************************************************/ - - -/** - * xsltFreeKeys: - * @style: an XSLT stylesheet - * - * Free up the memory used by XSLT keys in a stylesheet - */ -void -xsltFreeKeys(xsltStylesheetPtr style) { - if (style->keys) - xsltFreeKeyDefList((xsltKeyDefPtr) style->keys); -} - -/** - * skipString: - * @cur: the current pointer - * @end: the current offset - * - * skip a string delimited by " or ' - * - * Returns the byte after the string or -1 in case of error - */ -static int -skipString(const xmlChar *cur, int end) { - xmlChar limit; - - if ((cur == NULL) || (end < 0)) return(-1); - if ((cur[end] == '\'') || (cur[end] == '"')) limit = cur[end]; - else return(end); - end++; - while (cur[end] != 0) { - if (cur[end] == limit) - return(end + 1); - end++; - } - return(-1); -} - -/** - * skipPredicate: - * @cur: the current pointer - * @end: the current offset - * - * skip a predicate - * - * Returns the byte after the predicate or -1 in case of error - */ -static int -skipPredicate(const xmlChar *cur, int end) { - if ((cur == NULL) || (end < 0)) return(-1); - if (cur[end] != '[') return(end); - end++; - while (cur[end] != 0) { - if ((cur[end] == '\'') || (cur[end] == '"')) { - end = skipString(cur, end); - if (end <= 0) - return(-1); - continue; - } else if (cur[end] == '[') { - end = skipPredicate(cur, end); - if (end <= 0) - return(-1); - continue; - } else if (cur[end] == ']') - return(end + 1); - end++; - } - return(-1); -} - -/** - * xsltAddKey: - * @style: an XSLT stylesheet - * @name: the key name or NULL - * @nameURI: the name URI or NULL - * @match: the match value - * @use: the use value - * @inst: the key instruction - * - * add a key definition to a stylesheet - * - * Returns 0 in case of success, and -1 in case of failure. - */ -int -xsltAddKey(xsltStylesheetPtr style, const xmlChar *name, - const xmlChar *nameURI, const xmlChar *match, - const xmlChar *use, xmlNodePtr inst) { - xsltKeyDefPtr key; - xmlChar *pattern = NULL; - int current, end, start, i = 0; - - if ((style == NULL) || (name == NULL) || (match == NULL) || (use == NULL)) - return(-1); - -#ifdef WITH_XSLT_DEBUG_KEYS - xsltGenericDebug(xsltGenericDebugContext, - "Add key %s, match %s, use %s\n", name, match, use); -#endif - - key = xsltNewKeyDef(name, nameURI); - key->match = xmlStrdup(match); - key->use = xmlStrdup(use); - key->inst = inst; - key->nsList = xmlGetNsList(inst->doc, inst); - if (key->nsList != NULL) { - while (key->nsList[i] != NULL) - i++; - } - key->nsNr = i; - - /* - * Split the | and register it as as many keys - */ - current = end = 0; - while (match[current] != 0) { - start = current; - while (IS_BLANK_CH(match[current])) - current++; - end = current; - while ((match[end] != 0) && (match[end] != '|')) { - if (match[end] == '[') { - end = skipPredicate(match, end); - if (end <= 0) { - xsltTransformError(NULL, style, inst, - "key pattern is malformed: %s", - key->match); - if (style != NULL) style->errors++; - goto error; - } - } else - end++; - } - if (current == end) { - xsltTransformError(NULL, style, inst, - "key pattern is empty\n"); - if (style != NULL) style->errors++; - goto error; - } - if (match[start] != '/') { - pattern = xmlStrcat(pattern, (xmlChar *)"//"); - if (pattern == NULL) { - if (style != NULL) style->errors++; - goto error; - } - } - pattern = xmlStrncat(pattern, &match[start], end - start); - if (pattern == NULL) { - if (style != NULL) style->errors++; - goto error; - } - - if (match[end] == '|') { - pattern = xmlStrcat(pattern, (xmlChar *)"|"); - end++; - } - current = end; - } -#ifdef WITH_XSLT_DEBUG_KEYS - xsltGenericDebug(xsltGenericDebugContext, - " resulting pattern %s\n", pattern); -#endif - /* - * XSLT-1: "It is an error for the value of either the use - * attribute or the match attribute to contain a - * VariableReference." - * TODO: We should report a variable-reference at compile-time. - * Maybe a search for "$", if it occurs outside of quotation - * marks, could be sufficient. - */ - key->comp = xsltXPathCompile(style, pattern); - if (key->comp == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:key : XPath pattern compilation failed '%s'\n", - pattern); - if (style != NULL) style->errors++; - } - key->usecomp = xsltXPathCompile(style, use); - if (key->usecomp == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:key : XPath pattern compilation failed '%s'\n", - use); - if (style != NULL) style->errors++; - } - key->next = style->keys; - style->keys = key; -error: - if (pattern != NULL) - xmlFree(pattern); - return(0); -} - -/** - * xsltGetKey: - * @ctxt: an XSLT transformation context - * @name: the key name or NULL - * @nameURI: the name URI or NULL - * @value: the key value to look for - * - * Lookup a key - * - * Returns the nodeset resulting from the query or NULL - */ -xmlNodeSetPtr -xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name, - const xmlChar *nameURI, const xmlChar *value) { - xmlNodeSetPtr ret; - xsltKeyTablePtr table; -#ifdef XSLT_REFACTORED_KEYCOMP - int found = 0; -#endif - - if ((ctxt == NULL) || (name == NULL) || (value == NULL) || - (ctxt->document == NULL)) - return(NULL); - -#ifdef WITH_XSLT_DEBUG_KEYS - xsltGenericDebug(xsltGenericDebugContext, - "Get key %s, value %s\n", name, value); -#endif - table = (xsltKeyTablePtr) ctxt->document->keys; - while (table != NULL) { - if (((nameURI != NULL) == (table->nameURI != NULL)) && - xmlStrEqual(table->name, name) && - xmlStrEqual(table->nameURI, nameURI)) - { -#ifdef XSLT_REFACTORED_KEYCOMP - found = 1; -#endif - ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value); - return(ret); - } - table = table->next; - } -#ifdef XSLT_REFACTORED_KEYCOMP - if (! found) { - xsltStylesheetPtr style = ctxt->style; - xsltKeyDefPtr keyd; - /* - * This might be the first call to the key with the specified - * name and the specified document. - * Find all keys with a matching name and compute them for the - * current tree. - */ - found = 0; - while (style != NULL) { - keyd = (xsltKeyDefPtr) style->keys; - while (keyd != NULL) { - if (((nameURI != NULL) == (keyd->nameURI != NULL)) && - xmlStrEqual(keyd->name, name) && - xmlStrEqual(keyd->nameURI, nameURI)) - { - found = 1; - xsltInitCtxtKey(ctxt, ctxt->document, keyd); - } - keyd = keyd->next; - } - style = xsltNextImport(style); - } - if (found) { - /* - * The key was computed, so look it up. - */ - table = (xsltKeyTablePtr) ctxt->document->keys; - while (table != NULL) { - if (((nameURI != NULL) == (table->nameURI != NULL)) && - xmlStrEqual(table->name, name) && - xmlStrEqual(table->nameURI, nameURI)) - { - ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value); - return(ret); - } - table = table->next; - } - - } - } -#endif - return(NULL); -} - -/** - * xsltEvalXPathKeys: - * @ctxt: the XSLT transformation context - * @comp: the compiled XPath expression - * - * Process the expression using XPath to get the list of keys - * - * Returns the array of computed string value or NULL, must be deallocated - * by the caller. - */ -static xmlChar ** -xsltEvalXPathKeys(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, - xsltKeyDefPtr keyd) { - xmlChar **ret = NULL; - xmlXPathObjectPtr res; - xmlNodePtr oldInst; - xmlNodePtr oldNode; - int oldPos, oldSize; - int oldNsNr; - xmlNsPtr *oldNamespaces; - - oldInst = ctxt->inst; - oldNode = ctxt->node; - oldPos = ctxt->xpathCtxt->proximityPosition; - oldSize = ctxt->xpathCtxt->contextSize; - oldNsNr = ctxt->xpathCtxt->nsNr; - oldNamespaces = ctxt->xpathCtxt->namespaces; - - ctxt->xpathCtxt->node = ctxt->node; - ctxt->xpathCtxt->namespaces = keyd->nsList; - ctxt->xpathCtxt->nsNr = keyd->nsNr; - res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); - if (res != NULL) { - if (res->type == XPATH_NODESET) { - int len, i, j; - - if (res->nodesetval != NULL) - len = res->nodesetval->nodeNr; - else - len = 0; - if (len != 0) { - ret = (xmlChar **) xmlMalloc((len + 1) * sizeof(xmlChar *)); - if (ret != NULL) { - for (i = 0,j = 0;i < len;i++) { - ret[j] = xmlXPathCastNodeToString( - res->nodesetval->nodeTab[i]); - if (ret[j] != NULL) - j++; - } - ret[j] = NULL; - } - } - } else { - if (res->type != XPATH_STRING) - res = xmlXPathConvertString(res); - if (res->type == XPATH_STRING) { - ret = (xmlChar **) xmlMalloc(2 * sizeof(xmlChar *)); - if (ret != NULL) { - ret[0] = res->stringval; - ret[1] = NULL; - res->stringval = NULL; - } - } else { - xsltTransformError(ctxt, NULL, NULL, - "xpath : string() function didn't return a String\n"); - } - } - xmlXPathFreeObject(res); - } else { - ctxt->state = XSLT_STATE_STOPPED; - } -#ifdef WITH_XSLT_DEBUG_TEMPLATES - xsltGenericDebug(xsltGenericDebugContext, - "xsltEvalXPathString: returns %s\n", ret); -#endif - ctxt->inst = oldInst; - ctxt->node = oldNode; - ctxt->xpathCtxt->contextSize = oldSize; - ctxt->xpathCtxt->proximityPosition = oldPos; - ctxt->xpathCtxt->nsNr = oldNsNr; - ctxt->xpathCtxt->namespaces = oldNamespaces; - return(ret); -} - -/** - * xsltInitCtxtKey: - * @ctxt: an XSLT transformation context - * @doc: an XSLT document - * @keyd: the key definition - * - * Computes the key tables this key and for the current input document. - */ -int -xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr doc, - xsltKeyDefPtr keyd) { - int i; - xmlNodeSetPtr nodelist = NULL, keylist; - xmlXPathObjectPtr res = NULL; - xmlChar *str, **list; - xsltKeyTablePtr table; - int oldPos, oldSize; - xmlNodePtr oldInst; - xmlNodePtr oldNode; - xsltDocumentPtr oldDoc; - xmlDocPtr oldXDoc; - int oldNsNr; - xmlNsPtr *oldNamespaces; - - doc->nbKeysComputed++; - /* - * Evaluate the nodelist - */ - - oldXDoc= ctxt->xpathCtxt->doc; - oldPos = ctxt->xpathCtxt->proximityPosition; - oldSize = ctxt->xpathCtxt->contextSize; - oldNsNr = ctxt->xpathCtxt->nsNr; - oldNamespaces = ctxt->xpathCtxt->namespaces; - oldInst = ctxt->inst; - oldDoc = ctxt->document; - oldNode = ctxt->node; - - if (keyd->comp == NULL) - goto error; - if (keyd->usecomp == NULL) - goto error; - - ctxt->document = doc; - ctxt->xpathCtxt->doc = doc->doc; - ctxt->xpathCtxt->node = (xmlNodePtr) doc->doc; - ctxt->node = (xmlNodePtr) doc->doc; - /* TODO : clarify the use of namespaces in keys evaluation */ - ctxt->xpathCtxt->namespaces = keyd->nsList; - ctxt->xpathCtxt->nsNr = keyd->nsNr; - ctxt->inst = keyd->inst; - res = xmlXPathCompiledEval(keyd->comp, ctxt->xpathCtxt); - ctxt->xpathCtxt->contextSize = oldSize; - ctxt->xpathCtxt->proximityPosition = oldPos; - ctxt->inst = oldInst; - - if (res != NULL) { - if (res->type == XPATH_NODESET) { - nodelist = res->nodesetval; -#ifdef WITH_XSLT_DEBUG_KEYS - if (nodelist != NULL) - XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, - "xsltInitCtxtKey: %s evaluates to %d nodes\n", - keyd->match, nodelist->nodeNr)); -#endif - } else { -#ifdef WITH_XSLT_DEBUG_KEYS - XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, - "xsltInitCtxtKey: %s is not a node set\n", keyd->match)); -#endif - goto error; - } - } else { -#ifdef WITH_XSLT_DEBUG_KEYS - XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, - "xsltInitCtxtKey: %s evaluation failed\n", keyd->match)); -#endif - ctxt->state = XSLT_STATE_STOPPED; - goto error; - } - - /* - * for each node in the list evaluate the key and insert the node - */ - if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) - goto error; - - /** - * Multiple key definitions for the same name are allowed, so - * we must check if the key is already present for this doc - */ - table = (xsltKeyTablePtr) doc->keys; - while (table != NULL) { - if (xmlStrEqual(table->name, keyd->name) && - (((keyd->nameURI == NULL) && (table->nameURI == NULL)) || - ((keyd->nameURI != NULL) && (table->nameURI != NULL) && - (xmlStrEqual(table->nameURI, keyd->nameURI))))) - break; - table = table->next; - } - /** - * If the key was not previously defined, create it now and - * chain it to the list of keys for the doc - */ - if (table == NULL) { - table = xsltNewKeyTable(keyd->name, keyd->nameURI); - if (table == NULL) - goto error; - table->next = doc->keys; - doc->keys = table; - } - - for (i = 0;i < nodelist->nodeNr;i++) { - if (IS_XSLT_REAL_NODE(nodelist->nodeTab[i])) { - ctxt->node = nodelist->nodeTab[i]; - - list = xsltEvalXPathKeys(ctxt, keyd->usecomp, keyd); - if (list != NULL) { - int ix = 0; - - str = list[ix++]; - while (str != NULL) { -#ifdef WITH_XSLT_DEBUG_KEYS - XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, - "xsl:key : node associated to(%s,%s)\n", - keyd->name, str)); -#endif - keylist = xmlHashLookup(table->keys, str); - if (keylist == NULL) { - keylist = xmlXPathNodeSetCreate(nodelist->nodeTab[i]); - xmlHashAddEntry(table->keys, str, keylist); - } else { - xmlXPathNodeSetAdd(keylist, nodelist->nodeTab[i]); - } - switch (nodelist->nodeTab[i]->type) { - case XML_ELEMENT_NODE: - case XML_TEXT_NODE: - case XML_CDATA_SECTION_NODE: - case XML_PI_NODE: - case XML_COMMENT_NODE: - nodelist->nodeTab[i]->psvi = keyd; - break; - case XML_ATTRIBUTE_NODE: { - xmlAttrPtr attr = (xmlAttrPtr) - nodelist->nodeTab[i]; - attr->psvi = keyd; - break; - } - case XML_DOCUMENT_NODE: - case XML_HTML_DOCUMENT_NODE: { - xmlDocPtr kdoc = (xmlDocPtr) - nodelist->nodeTab[i]; - kdoc->psvi = keyd; - break; - } - default: - break; - } - xmlFree(str); - str = list[ix++]; - } - xmlFree(list); -#ifdef WITH_XSLT_DEBUG_KEYS - } else { - XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, - "xsl:key : use %s failed to return strings\n", - keyd->use)); -#endif - } - } - } - -error: - ctxt->document = oldDoc; - ctxt->xpathCtxt->doc = oldXDoc; - ctxt->xpathCtxt->nsNr = oldNsNr; - ctxt->xpathCtxt->namespaces = oldNamespaces; - ctxt->node = oldNode; - if (res != NULL) - xmlXPathFreeObject(res); - return(0); -} - -/** - * xsltInitCtxtKeys: - * @ctxt: an XSLT transformation context - * @doc: an XSLT document - * - * Computes all the keys tables for the current input document. - * Should be done before global varibales are initialized. - * NOTE: Not used anymore in the refactored code. - */ -void -xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr doc) { - xsltStylesheetPtr style; - xsltKeyDefPtr keyd; - - if ((ctxt == NULL) || (doc == NULL)) - return; -#ifdef WITH_XSLT_DEBUG_KEYS - if ((doc->doc != NULL) && (doc->doc->URL != NULL)) - XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n", - doc->doc->URL)); -#endif - style = ctxt->style; - while (style != NULL) { - keyd = (xsltKeyDefPtr) style->keys; - while (keyd != NULL) { - xsltInitCtxtKey(ctxt, doc, keyd); - - keyd = keyd->next; - } - - style = xsltNextImport(style); - } -} - -/** - * xsltFreeDocumentKeys: - * @doc: a XSLT document - * - * Free the keys associated to a document - */ -void -xsltFreeDocumentKeys(xsltDocumentPtr doc) { - if (doc != NULL) - xsltFreeKeyTableList(doc->keys); -} - +/* + * keys.c: Implemetation of the keys support + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "imports.h" +#include "templates.h" +#include "keys.h" + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_KEYS +#endif + + +/************************************************************************ + * * + * Type functions * + * * + ************************************************************************/ + +/** + * xsltNewKeyDef: + * @name: the key name or NULL + * @nameURI: the name URI or NULL + * + * Create a new XSLT KeyDef + * + * Returns the newly allocated xsltKeyDefPtr or NULL in case of error + */ +static xsltKeyDefPtr +xsltNewKeyDef(const xmlChar *name, const xmlChar *nameURI) { + xsltKeyDefPtr cur; + + cur = (xsltKeyDefPtr) xmlMalloc(sizeof(xsltKeyDef)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewKeyDef : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltKeyDef)); + if (name != NULL) + cur->name = xmlStrdup(name); + if (nameURI != NULL) + cur->nameURI = xmlStrdup(nameURI); + cur->nsList = NULL; + return(cur); +} + +/** + * xsltFreeKeyDef: + * @keyd: an XSLT key definition + * + * Free up the memory allocated by @keyd + */ +static void +xsltFreeKeyDef(xsltKeyDefPtr keyd) { + if (keyd == NULL) + return; + if (keyd->comp != NULL) + xmlXPathFreeCompExpr(keyd->comp); + if (keyd->usecomp != NULL) + xmlXPathFreeCompExpr(keyd->usecomp); + if (keyd->name != NULL) + xmlFree(keyd->name); + if (keyd->nameURI != NULL) + xmlFree(keyd->nameURI); + if (keyd->match != NULL) + xmlFree(keyd->match); + if (keyd->use != NULL) + xmlFree(keyd->use); + if (keyd->nsList != NULL) + xmlFree(keyd->nsList); + memset(keyd, -1, sizeof(xsltKeyDef)); + xmlFree(keyd); +} + +/** + * xsltFreeKeyDefList: + * @keyd: an XSLT key definition list + * + * Free up the memory allocated by all the elements of @keyd + */ +static void +xsltFreeKeyDefList(xsltKeyDefPtr keyd) { + xsltKeyDefPtr cur; + + while (keyd != NULL) { + cur = keyd; + keyd = keyd->next; + xsltFreeKeyDef(cur); + } +} + +/** + * xsltNewKeyTable: + * @name: the key name or NULL + * @nameURI: the name URI or NULL + * + * Create a new XSLT KeyTable + * + * Returns the newly allocated xsltKeyTablePtr or NULL in case of error + */ +static xsltKeyTablePtr +xsltNewKeyTable(const xmlChar *name, const xmlChar *nameURI) { + xsltKeyTablePtr cur; + + cur = (xsltKeyTablePtr) xmlMalloc(sizeof(xsltKeyTable)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewKeyTable : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltKeyTable)); + if (name != NULL) + cur->name = xmlStrdup(name); + if (nameURI != NULL) + cur->nameURI = xmlStrdup(nameURI); + cur->keys = xmlHashCreate(0); + return(cur); +} + +/** + * xsltFreeKeyTable: + * @keyt: an XSLT key table + * + * Free up the memory allocated by @keyt + */ +static void +xsltFreeKeyTable(xsltKeyTablePtr keyt) { + if (keyt == NULL) + return; + if (keyt->name != NULL) + xmlFree(keyt->name); + if (keyt->nameURI != NULL) + xmlFree(keyt->nameURI); + if (keyt->keys != NULL) + xmlHashFree(keyt->keys, + (xmlHashDeallocator) xmlXPathFreeNodeSet); + memset(keyt, -1, sizeof(xsltKeyTable)); + xmlFree(keyt); +} + +/** + * xsltFreeKeyTableList: + * @keyt: an XSLT key table list + * + * Free up the memory allocated by all the elements of @keyt + */ +static void +xsltFreeKeyTableList(xsltKeyTablePtr keyt) { + xsltKeyTablePtr cur; + + while (keyt != NULL) { + cur = keyt; + keyt = keyt->next; + xsltFreeKeyTable(cur); + } +} + +/************************************************************************ + * * + * The interpreter for the precompiled patterns * + * * + ************************************************************************/ + + +/** + * xsltFreeKeys: + * @style: an XSLT stylesheet + * + * Free up the memory used by XSLT keys in a stylesheet + */ +void +xsltFreeKeys(xsltStylesheetPtr style) { + if (style->keys) + xsltFreeKeyDefList((xsltKeyDefPtr) style->keys); +} + +/** + * skipString: + * @cur: the current pointer + * @end: the current offset + * + * skip a string delimited by " or ' + * + * Returns the byte after the string or -1 in case of error + */ +static int +skipString(const xmlChar *cur, int end) { + xmlChar limit; + + if ((cur == NULL) || (end < 0)) return(-1); + if ((cur[end] == '\'') || (cur[end] == '"')) limit = cur[end]; + else return(end); + end++; + while (cur[end] != 0) { + if (cur[end] == limit) + return(end + 1); + end++; + } + return(-1); +} + +/** + * skipPredicate: + * @cur: the current pointer + * @end: the current offset + * + * skip a predicate + * + * Returns the byte after the predicate or -1 in case of error + */ +static int +skipPredicate(const xmlChar *cur, int end) { + if ((cur == NULL) || (end < 0)) return(-1); + if (cur[end] != '[') return(end); + end++; + while (cur[end] != 0) { + if ((cur[end] == '\'') || (cur[end] == '"')) { + end = skipString(cur, end); + if (end <= 0) + return(-1); + continue; + } else if (cur[end] == '[') { + end = skipPredicate(cur, end); + if (end <= 0) + return(-1); + continue; + } else if (cur[end] == ']') + return(end + 1); + end++; + } + return(-1); +} + +/** + * xsltAddKey: + * @style: an XSLT stylesheet + * @name: the key name or NULL + * @nameURI: the name URI or NULL + * @match: the match value + * @use: the use value + * @inst: the key instruction + * + * add a key definition to a stylesheet + * + * Returns 0 in case of success, and -1 in case of failure. + */ +int +xsltAddKey(xsltStylesheetPtr style, const xmlChar *name, + const xmlChar *nameURI, const xmlChar *match, + const xmlChar *use, xmlNodePtr inst) { + xsltKeyDefPtr key; + xmlChar *pattern = NULL; + int current, end, start, i = 0; + + if ((style == NULL) || (name == NULL) || (match == NULL) || (use == NULL)) + return(-1); + +#ifdef WITH_XSLT_DEBUG_KEYS + xsltGenericDebug(xsltGenericDebugContext, + "Add key %s, match %s, use %s\n", name, match, use); +#endif + + key = xsltNewKeyDef(name, nameURI); + key->match = xmlStrdup(match); + key->use = xmlStrdup(use); + key->inst = inst; + key->nsList = xmlGetNsList(inst->doc, inst); + if (key->nsList != NULL) { + while (key->nsList[i] != NULL) + i++; + } + key->nsNr = i; + + /* + * Split the | and register it as as many keys + */ + current = end = 0; + while (match[current] != 0) { + start = current; + while (IS_BLANK_CH(match[current])) + current++; + end = current; + while ((match[end] != 0) && (match[end] != '|')) { + if (match[end] == '[') { + end = skipPredicate(match, end); + if (end <= 0) { + xsltTransformError(NULL, style, inst, + "key pattern is malformed: %s", + key->match); + if (style != NULL) style->errors++; + goto error; + } + } else + end++; + } + if (current == end) { + xsltTransformError(NULL, style, inst, + "key pattern is empty\n"); + if (style != NULL) style->errors++; + goto error; + } + if (match[start] != '/') { + pattern = xmlStrcat(pattern, (xmlChar *)"//"); + if (pattern == NULL) { + if (style != NULL) style->errors++; + goto error; + } + } + pattern = xmlStrncat(pattern, &match[start], end - start); + if (pattern == NULL) { + if (style != NULL) style->errors++; + goto error; + } + + if (match[end] == '|') { + pattern = xmlStrcat(pattern, (xmlChar *)"|"); + end++; + } + current = end; + } +#ifdef WITH_XSLT_DEBUG_KEYS + xsltGenericDebug(xsltGenericDebugContext, + " resulting pattern %s\n", pattern); +#endif + /* + * XSLT-1: "It is an error for the value of either the use + * attribute or the match attribute to contain a + * VariableReference." + * TODO: We should report a variable-reference at compile-time. + * Maybe a search for "$", if it occurs outside of quotation + * marks, could be sufficient. + */ + key->comp = xsltXPathCompile(style, pattern); + if (key->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:key : XPath pattern compilation failed '%s'\n", + pattern); + if (style != NULL) style->errors++; + } + key->usecomp = xsltXPathCompile(style, use); + if (key->usecomp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:key : XPath pattern compilation failed '%s'\n", + use); + if (style != NULL) style->errors++; + } + key->next = style->keys; + style->keys = key; +error: + if (pattern != NULL) + xmlFree(pattern); + return(0); +} + +/** + * xsltGetKey: + * @ctxt: an XSLT transformation context + * @name: the key name or NULL + * @nameURI: the name URI or NULL + * @value: the key value to look for + * + * Looks up a key of the in current source doc (the document info + * on @ctxt->document). Computes the key if not already done + * for the current source doc. + * + * Returns the nodeset resulting from the query or NULL + */ +xmlNodeSetPtr +xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name, + const xmlChar *nameURI, const xmlChar *value) { + xmlNodeSetPtr ret; + xsltKeyTablePtr table; +#ifdef XSLT_REFACTORED_KEYCOMP + int found = 0; +#endif + + if ((ctxt == NULL) || (name == NULL) || (value == NULL) || + (ctxt->document == NULL)) + return(NULL); + +#ifdef WITH_XSLT_DEBUG_KEYS + xsltGenericDebug(xsltGenericDebugContext, + "Get key %s, value %s\n", name, value); +#endif + + table = (xsltKeyTablePtr) ctxt->document->keys; + while (table != NULL) { + if (((nameURI != NULL) == (table->nameURI != NULL)) && + xmlStrEqual(table->name, name) && + xmlStrEqual(table->nameURI, nameURI)) + { +#ifdef XSLT_REFACTORED_KEYCOMP + found = 1; +#endif + ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value); + return(ret); + } + table = table->next; + } +#ifdef XSLT_REFACTORED_KEYCOMP + if (! found) { + xsltStylesheetPtr style = ctxt->style; + xsltKeyDefPtr keyd; + /* + * This might be the first call to the key with the specified + * name and the specified document. + * Find all keys with a matching name and compute them for the + * current tree. + */ + found = 0; + while (style != NULL) { + keyd = (xsltKeyDefPtr) style->keys; + while (keyd != NULL) { + if (((nameURI != NULL) == (keyd->nameURI != NULL)) && + xmlStrEqual(keyd->name, name) && + xmlStrEqual(keyd->nameURI, nameURI)) + { + found = 1; + xsltInitCtxtKey(ctxt, ctxt->document, keyd); + } + keyd = keyd->next; + } + style = xsltNextImport(style); + } + if (found) { + /* + * The key was computed, so look it up. + */ + table = (xsltKeyTablePtr) ctxt->document->keys; + while (table != NULL) { + if (((nameURI != NULL) == (table->nameURI != NULL)) && + xmlStrEqual(table->name, name) && + xmlStrEqual(table->nameURI, nameURI)) + { + ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value); + return(ret); + } + table = table->next; + } + + } + } +#endif + return(NULL); +} + +#if 0 /* Merged with xsltInitCtxtKey() */ +/** + * xsltEvalXPathKeys: + * @ctxt: the XSLT transformation context + * @comp: the compiled XPath expression + * + * Process the expression using XPath to get the list of keys + * + * Returns the array of computed string value or NULL, must be deallocated + * by the caller. + */ +static xmlChar ** +xsltEvalXPathKeys(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, + xsltKeyDefPtr keyd) { + xmlChar **ret = NULL; + xmlXPathObjectPtr res; + xmlNodePtr oldInst; + xmlNodePtr oldNode; + int oldPos, oldSize; + int oldNsNr; + xmlNsPtr *oldNamespaces; + + oldInst = ctxt->inst; + oldNode = ctxt->node; + oldPos = ctxt->xpathCtxt->proximityPosition; + oldSize = ctxt->xpathCtxt->contextSize; + oldNsNr = ctxt->xpathCtxt->nsNr; + oldNamespaces = ctxt->xpathCtxt->namespaces; + + ctxt->xpathCtxt->node = ctxt->node; + ctxt->xpathCtxt->namespaces = keyd->nsList; + ctxt->xpathCtxt->nsNr = keyd->nsNr; + res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); + if (res != NULL) { + if (res->type == XPATH_NODESET) { + int len, i, j; + + if (res->nodesetval != NULL) + len = res->nodesetval->nodeNr; + else + len = 0; + if (len != 0) { + ret = (xmlChar **) xmlMalloc((len + 1) * sizeof(xmlChar *)); + if (ret != NULL) { + for (i = 0,j = 0;i < len;i++) { + ret[j] = xmlXPathCastNodeToString( + res->nodesetval->nodeTab[i]); + if (ret[j] != NULL) + j++; + } + ret[j] = NULL; + } + } + } else { + if (res->type != XPATH_STRING) + res = xmlXPathConvertString(res); + if (res->type == XPATH_STRING) { + ret = (xmlChar **) xmlMalloc(2 * sizeof(xmlChar *)); + if (ret != NULL) { + ret[0] = res->stringval; + ret[1] = NULL; + res->stringval = NULL; + } + } else { + xsltTransformError(ctxt, NULL, NULL, + "xpath : string() function didn't return a String\n"); + } + } + xmlXPathFreeObject(res); + } else { + ctxt->state = XSLT_STATE_STOPPED; + } +#ifdef WITH_XSLT_DEBUG_TEMPLATES + xsltGenericDebug(xsltGenericDebugContext, + "xsltEvalXPathString: returns %s\n", ret); +#endif + ctxt->inst = oldInst; + ctxt->node = oldNode; + ctxt->xpathCtxt->contextSize = oldSize; + ctxt->xpathCtxt->proximityPosition = oldPos; + ctxt->xpathCtxt->nsNr = oldNsNr; + ctxt->xpathCtxt->namespaces = oldNamespaces; + return(ret); +} +#endif + +/** + * xsltInitCtxtKey: + * @ctxt: an XSLT transformation context + * @idoc: the document information (holds key values) + * @keyDef: the key definition + * + * Computes the key tables this key and for the current input document. + */ +int +xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc, + xsltKeyDefPtr keyDef) +{ + int i, len, k; + xmlNodeSetPtr matchList = NULL, keylist; + xmlXPathObjectPtr matchRes = NULL, useRes = NULL; + xmlChar *str = NULL; + xsltKeyTablePtr table; + xmlNodePtr oldInst, cur; + xmlNodePtr oldContextNode; + xsltDocumentPtr oldDocInfo; + int oldXPPos, oldXPSize; + xmlDocPtr oldXPDoc; + int oldXPNsNr; + xmlNsPtr *oldXPNamespaces; + xmlXPathContextPtr xpctxt; + + if ((keyDef->comp == NULL) || (keyDef->usecomp == NULL)) + return(-1); + + xpctxt = ctxt->xpathCtxt; + idoc->nbKeysComputed++; + /* + * Save context state. + */ + oldInst = ctxt->inst; + oldDocInfo = ctxt->document; + oldContextNode = ctxt->node; + + oldXPDoc = xpctxt->doc; + oldXPPos = xpctxt->proximityPosition; + oldXPSize = xpctxt->contextSize; + oldXPNsNr = xpctxt->nsNr; + oldXPNamespaces = xpctxt->namespaces; + + /* + * Set up contexts. + */ + ctxt->document = idoc; + ctxt->node = (xmlNodePtr) idoc->doc; + ctxt->inst = keyDef->inst; + + xpctxt->doc = idoc->doc; + xpctxt->node = (xmlNodePtr) idoc->doc; + /* TODO : clarify the use of namespaces in keys evaluation */ + xpctxt->namespaces = keyDef->nsList; + xpctxt->nsNr = keyDef->nsNr; + + /* + * Evaluate the 'match' expression of the xsl:key. + * TODO: The 'match' is a *pattern*. + */ + matchRes = xmlXPathCompiledEval(keyDef->comp, xpctxt); + if (matchRes == NULL) { + +#ifdef WITH_XSLT_DEBUG_KEYS + XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, + "xsltInitCtxtKey: %s evaluation failed\n", keyDef->match)); +#endif + xsltTransformError(ctxt, NULL, keyDef->inst, + "Failed to evaluate the 'match' expression.\n"); + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } else { + if (matchRes->type == XPATH_NODESET) { + matchList = matchRes->nodesetval; + +#ifdef WITH_XSLT_DEBUG_KEYS + if (matchList != NULL) + XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, + "xsltInitCtxtKey: %s evaluates to %d nodes\n", + keyDef->match, matchList->nodeNr)); +#endif + } else { + /* + * Is not a node set, but must be. + */ +#ifdef WITH_XSLT_DEBUG_KEYS + XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, + "xsltInitCtxtKey: %s is not a node set\n", keyDef->match)); +#endif + xsltTransformError(ctxt, NULL, keyDef->inst, + "The 'match' expression did not evaluate to a node set.\n"); + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + } + if ((matchList == NULL) || (matchList->nodeNr <= 0)) + goto exit; + + /** + * Multiple key definitions for the same name are allowed, so + * we must check if the key is already present for this doc + */ + table = (xsltKeyTablePtr) idoc->keys; + while (table != NULL) { + if (xmlStrEqual(table->name, keyDef->name) && + (((keyDef->nameURI == NULL) && (table->nameURI == NULL)) || + ((keyDef->nameURI != NULL) && (table->nameURI != NULL) && + (xmlStrEqual(table->nameURI, keyDef->nameURI))))) + break; + table = table->next; + } + /** + * If the key was not previously defined, create it now and + * chain it to the list of keys for the doc + */ + if (table == NULL) { + table = xsltNewKeyTable(keyDef->name, keyDef->nameURI); + if (table == NULL) + goto error; + table->next = idoc->keys; + idoc->keys = table; + } + + /* + * SPEC XSLT 1.0 (XSLT 2.0 does not clarify the context size!) + * "...the use attribute of the xsl:key element is evaluated with x as + " the current node and with a node list containing just x as the + * current node list" + */ + xpctxt->contextSize = 1; + xpctxt->proximityPosition = 1; + + for (i = 0; i < matchList->nodeNr; i++) { + cur = matchList->nodeTab[i]; + if (! IS_XSLT_REAL_NODE(cur)) + continue; + xpctxt->node = cur; + /* + * Process the 'use' of the xsl:key. + * SPEC XSLT 1.0: + * "The use attribute is an expression specifying the values of + * the key; the expression is evaluated once for each node that + * matches the pattern." + */ + if (useRes != NULL) + xmlXPathFreeObject(useRes); + useRes = xmlXPathCompiledEval(keyDef->usecomp, xpctxt); + if (useRes == NULL) { + xsltTransformError(ctxt, NULL, keyDef->inst, + "Failed to evaluate the 'use' expression.\n"); + ctxt->state = XSLT_STATE_STOPPED; + break; + } + if (useRes->type == XPATH_NODESET) { + if ((useRes->nodesetval != NULL) && + (useRes->nodesetval->nodeNr != 0)) + { + len = useRes->nodesetval->nodeNr; + str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[0]); + } else { + continue; + } + } else { + len = 1; + if (useRes->type == XPATH_STRING) { + /* + * Consume the string value. + */ + str = useRes->stringval; + useRes->stringval = NULL; + } else { + str = xmlXPathCastToString(useRes); + } + } + /* + * Process all strings. + */ + k = 0; + while (1) { + if (str == NULL) + goto next_string; + +#ifdef WITH_XSLT_DEBUG_KEYS + XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, + "xsl:key : node associated to ('%s', '%s')\n", keyDef->name, str)); +#endif + + keylist = xmlHashLookup(table->keys, str); + if (keylist == NULL) { + keylist = xmlXPathNodeSetCreate(cur); + if (keylist == NULL) + goto error; + xmlHashAddEntry(table->keys, str, keylist); + } else { + /* + * TODO: How do we know if this function failed? + */ + xmlXPathNodeSetAdd(keylist, cur); + } + switch (cur->type) { + case XML_ELEMENT_NODE: + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + cur->psvi = keyDef; + break; + case XML_ATTRIBUTE_NODE: + ((xmlAttrPtr) cur)->psvi = keyDef; + break; + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + ((xmlDocPtr) cur)->psvi = keyDef; + break; + default: + break; + } + xmlFree(str); + str = NULL; + +next_string: + k++; + if (k >= len) + break; + str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[k]); + } + } + +exit: +error: + /* + * Restore context state. + */ + xpctxt->doc = oldXPDoc; + xpctxt->nsNr = oldXPNsNr; + xpctxt->namespaces = oldXPNamespaces; + xpctxt->proximityPosition = oldXPPos; + xpctxt->contextSize = oldXPSize; + + ctxt->node = oldContextNode; + ctxt->document = oldDocInfo; + ctxt->inst = oldInst; + + if (str) + xmlFree(str); + if (useRes != NULL) + xmlXPathFreeObject(useRes); + if (matchRes != NULL) + xmlXPathFreeObject(matchRes); + return(0); +} + +/** + * xsltInitCtxtKeys: + * @ctxt: an XSLT transformation context + * @idoc: a document info + * + * Computes all the keys tables for the current input document. + * Should be done before global varibales are initialized. + * NOTE: Not used anymore in the refactored code. + */ +void +xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc) { + xsltStylesheetPtr style; + xsltKeyDefPtr keyDef; + + if ((ctxt == NULL) || (idoc == NULL)) + return; +#ifdef WITH_XSLT_DEBUG_KEYS + if ((idoc->doc != NULL) && (idoc->doc->URL != NULL)) + XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n", + idoc->doc->URL)); +#endif + style = ctxt->style; + while (style != NULL) { + keyDef = (xsltKeyDefPtr) style->keys; + while (keyDef != NULL) { + xsltInitCtxtKey(ctxt, idoc, keyDef); + + keyDef = keyDef->next; + } + + style = xsltNextImport(style); + } +} + +/** + * xsltFreeDocumentKeys: + * @doc: a XSLT document + * + * Free the keys associated to a document + */ +void +xsltFreeDocumentKeys(xsltDocumentPtr idoc) { + if (idoc != NULL) + xsltFreeKeyTableList(idoc->keys); +} + diff --git a/libxslt/namespaces.c b/libxslt/namespaces.c index 0cb302d0..5202630b 100644 --- a/libxslt/namespaces.c +++ b/libxslt/namespaces.c @@ -1,844 +1,842 @@ -/* - * namespaces.c: Implementation of the XSLT namespaces handling - * - * Reference: - * http://www.w3.org/TR/1999/REC-xslt-19991116 - * - * See Copyright for the status of this software. - * - * daniel@veillard.com - */ - -#define IN_LIBXSLT -#include "libxslt.h" - -#include - -#ifdef HAVE_SYS_TYPES_H -#include -#endif -#ifdef HAVE_MATH_H -#include -#endif -#ifdef HAVE_FLOAT_H -#include -#endif -#ifdef HAVE_IEEEFP_H -#include -#endif -#ifdef HAVE_NAN_H -#include -#endif -#ifdef HAVE_CTYPE_H -#include -#endif - -#include -#include -#include -#include -#include -#include "xslt.h" -#include "xsltInternals.h" -#include "xsltutils.h" -#include "namespaces.h" -#include "imports.h" - - - -/************************************************************************ - * * - * Module interfaces * - * * - ************************************************************************/ - -#ifdef XSLT_REFACTORED -static xsltNsAliasPtr -xsltNewNsAlias(xsltCompilerCtxtPtr cctxt) -{ - xsltNsAliasPtr ret; - - if (cctxt == NULL) - return(NULL); - - ret = (xsltNsAliasPtr) xmlMalloc(sizeof(xsltNsAlias)); - if (ret == NULL) { - xsltTransformError(NULL, cctxt->style, NULL, - "Internal error in xsltNewNsAlias(): Memory allocation failed.\n"); - cctxt->style->errors++; - return(NULL); - } - memset(ret, 0, sizeof(xsltNsAlias)); - /* - * TODO: Store the item at current stylesheet-level. - */ - ret->next = cctxt->nsAliases; - cctxt->nsAliases = ret; - - return(ret); -} -#endif /* XSLT_REFACTORED */ -/** - * xsltNamespaceAlias: - * @style: the XSLT stylesheet - * @node: the xsl:namespace-alias node - * - * Read the stylesheet-prefix and result-prefix attributes, register - * them as well as the corresponding namespace. - */ -void -xsltNamespaceAlias(xsltStylesheetPtr style, xmlNodePtr node) -{ - xmlChar *resultPrefix = NULL; - xmlChar *stylePrefix = NULL; - xmlNsPtr literalNs = NULL; - xmlNsPtr targetNs = NULL; - -#ifdef XSLT_REFACTORED - xsltNsAliasPtr alias; - - if ((style == NULL) || (node == NULL)) - return; - - /* - * SPEC XSLT 1.0: - * "If a namespace URI is declared to be an alias for multiple - * different namespace URIs, then the declaration with the highest - * import precedence is used. It is an error if there is more than - * one such declaration. An XSLT processor may signal the error; - * if it does not signal the error, it must recover by choosing, - * from amongst the declarations with the highest import precedence, - * the one that occurs last in the stylesheet." - * - * SPEC TODO: Check for the errors mentioned above. - */ - /* - * NOTE that the XSLT 2.0 also *does* use the NULL namespace if - * "#default" is used and there's no default namespace is scope. - * I.e., this is *not* an error. - * Most XSLT 1.0 implementations work this way. - * The XSLT 1.0 spec has nothing to say on the subject. - */ - /* - * Attribute "stylesheet-prefix". - */ - stylePrefix = xmlGetNsProp(node, (const xmlChar *)"stylesheet-prefix", NULL); - if (stylePrefix == NULL) { - xsltTransformError(NULL, style, node, - "The attribute 'stylesheet-prefix' is missing.\n"); - return; - } - if (xmlStrEqual(stylePrefix, (const xmlChar *)"#default")) - literalNs = xmlSearchNs(node->doc, node, NULL); - else { - literalNs = xmlSearchNs(node->doc, node, stylePrefix); - if (literalNs == NULL) { - xsltTransformError(NULL, style, node, - "Attribute 'stylesheet-prefix': There's no namespace " - "declaration in scope for the prefix '%s'.\n", - stylePrefix); - goto error; - } - } - /* - * Attribute "result-prefix". - */ - resultPrefix = xmlGetNsProp(node, (const xmlChar *)"result-prefix", NULL); - if (resultPrefix == NULL) { - xsltTransformError(NULL, style, node, - "The attribute 'result-prefix' is missing.\n"); - goto error; - } - if (xmlStrEqual(resultPrefix, (const xmlChar *)"#default")) - targetNs = xmlSearchNs(node->doc, node, NULL); - else { - targetNs = xmlSearchNs(node->doc, node, resultPrefix); - - if (targetNs == NULL) { - xsltTransformError(NULL, style, node, - "Attribute 'result-prefix': There's no namespace " - "declaration in scope for the prefix '%s'.\n", - stylePrefix); - goto error; - } - } - /* - * - * Same alias for multiple different target namespace URIs: - * TODO: The one with the highest import precedence is used. - * Example: - * - * - * - * - * Same target namespace URI for multiple different aliases: - * All alias-definitions will be used. - * Example: - * - * - * - * Cases using #default: - * - * TODO: Has this an effect at all? - * - * - * From namespace to no namespace. - * - * - * From no namespace to namespace. - */ - - - /* - * Store the ns-node in the alias-object. - */ - alias = xsltNewNsAlias(XSLT_CCTXT(style)); - if (alias == NULL) - return; - alias->literalNs = literalNs; - alias->targetNs = targetNs; - XSLT_CCTXT(style)->hasNsAliases = 1; - - -#else /* XSLT_REFACTORED */ - const xmlChar *literalNsName; - const xmlChar *targetNsName; - - - if ((style == NULL) || (node == NULL)) - return; - - stylePrefix = xmlGetNsProp(node, (const xmlChar *)"stylesheet-prefix", NULL); - if (stylePrefix == NULL) { - xsltTransformError(NULL, style, node, - "namespace-alias: stylesheet-prefix attribute missing\n"); - return; - } - resultPrefix = xmlGetNsProp(node, (const xmlChar *)"result-prefix", NULL); - if (resultPrefix == NULL) { - xsltTransformError(NULL, style, node, - "namespace-alias: result-prefix attribute missing\n"); - goto error; - } - - if (xmlStrEqual(stylePrefix, (const xmlChar *)"#default")) { - literalNs = xmlSearchNs(node->doc, node, NULL); - if (literalNs == NULL) { - literalNsName = NULL; - } else - literalNsName = literalNs->href; /* Yes - set for nsAlias table */ - } else { - literalNs = xmlSearchNs(node->doc, node, stylePrefix); - - if ((literalNs == NULL) || (literalNs->href == NULL)) { - xsltTransformError(NULL, style, node, - "namespace-alias: prefix %s not bound to any namespace\n", - stylePrefix); - goto error; - } else - literalNsName = literalNs->href; - } - - /* - * When "#default" is used for result, if a default namespace has not - * been explicitly declared the special value UNDEFINED_DEFAULT_NS is - * put into the nsAliases table - */ - if (xmlStrEqual(resultPrefix, (const xmlChar *)"#default")) { - targetNs = xmlSearchNs(node->doc, node, NULL); - if (targetNs == NULL) { - targetNsName = UNDEFINED_DEFAULT_NS; - } else - targetNsName = targetNs->href; - } else { - targetNs = xmlSearchNs(node->doc, node, resultPrefix); - - if ((targetNs == NULL) || (targetNs->href == NULL)) { - xsltTransformError(NULL, style, node, - "namespace-alias: prefix %s not bound to any namespace\n", - resultPrefix); - goto error; - } else - targetNsName = targetNs->href; - } - /* - * Special case: if #default is used for - * the stylesheet-prefix (literal namespace) and there's no default - * namespace in scope, we'll use style->defaultAlias for this. - */ - if (literalNsName == NULL) { - if (targetNs != NULL) { - /* - * BUG TODO: Is it not sufficient to have only 1 field for - * this, since subsequently alias declarations will - * overwrite this. - * Example: - * - * - * The mapping for "foo" won't be visible anymore. - */ - style->defaultAlias = targetNs->href; - } - } else { - if (style->nsAliases == NULL) - style->nsAliases = xmlHashCreate(10); - if (style->nsAliases == NULL) { - xsltTransformError(NULL, style, node, - "namespace-alias: cannot create hash table\n"); - goto error; - } - xmlHashAddEntry((xmlHashTablePtr) style->nsAliases, - literalNsName, (void *) targetNsName); - } -#endif /* else of XSLT_REFACTORED */ - -error: - if (stylePrefix != NULL) - xmlFree(stylePrefix); - if (resultPrefix != NULL) - xmlFree(resultPrefix); -} - -/** - * xsltGetSpecialNamespace: - * @ctxt: the transformation context - * @invocNode: the invoking node; e.g. a literal result element/attr; - * only used for error reports - * @nsName: the namespace name (or NULL) - * @nsPrefix: the suggested namespace prefix (or NULL) - * @target: the result element on which to anchor a namespace - * - * Find a matching (prefix and ns-name) ns-declaration - * for the requested @nsName and @nsPrefix in the result tree. - * If none is found then a new ns-declaration will be - * added to @resultElem. If, in this case, the given prefix is - * already in use, then a ns-declaration with a modified ns-prefix - * be we created. Note that this function's priority is to - * preserve ns-prefixes; it will only change a prefix if there's - * a namespace clash. - * If both @nsName and @nsPrefix are NULL, then this will try to - * "undeclare" a default namespace by declaring an xmlns="". - * - * Returns a namespace declaration or NULL. - */ -xmlNsPtr -xsltGetSpecialNamespace(xsltTransformContextPtr ctxt, xmlNodePtr invocNode, - const xmlChar *nsName, const xmlChar *nsPrefix, - xmlNodePtr target) -{ - xmlNsPtr ns; - int prefixOccupied = 0; - - if ((ctxt == NULL) || (target == NULL) || - (target->type != XML_ELEMENT_NODE)) - return(NULL); - - /* - * NOTE: Namespace exclusion and ns-aliasing is performed at - * compilation-time in the refactored code; so this need not be done - * here (it was in the old code). - * NOTE: @invocNode was named @cur in the old code and was documented to - * be an input node; since it was only used to anchor an error report - * somewhere, we can safely change this to @invocNode, which now - * will be the XSLT instruction (also a literal result element/attribute), - * which was responsible for this call. - */ - /* - * OPTIMIZE TODO: This all could be optimized by keeping track of - * the ns-decls currently in-scope via a specialized context. - */ - if ((nsPrefix == NULL) && ((nsName == NULL) || (nsName[0] == 0))) { - /* - * NOTE: the "undeclaration" of the default namespace was - * part of the logic of the old xsltGetSpecialNamespace() code, - * so we'll keep that mechanism. - * Related to the old code: bug #302020: - */ - /* - * OPTIMIZE TODO: This all could be optimized by keeping track of - * the ns-decls currently in-scope via a specialized context. - */ - /* - * Search on the result element itself. - */ - if (target->nsDef != NULL) { - ns = target->nsDef; - do { - if (ns->prefix == NULL) { - if ((ns->href != NULL) && (ns->href[0] != 0)) { - /* - * Raise a namespace normalization error. - */ - xsltTransformError(ctxt, NULL, invocNode, - "Namespace normalization error: Cannot undeclare " - "the default namespace, since the default namespace " - "'%s' is already declared on the result element " - "'%s'.\n", ns->href, target->name); - return(NULL); - } else { - /* - * The default namespace was undeclared on the - * result element. - */ - return(NULL); - } - break; - } - ns = ns->next; - } while (ns != NULL); - } - if ((target->parent != NULL) && - (target->parent->type == XML_ELEMENT_NODE)) - { - /* - * The parent element is in no namespace, so assume - * that there is no default namespace in scope. - */ - if (target->parent->ns == NULL) - return(NULL); - - ns = xmlSearchNs(target->doc, target->parent, - NULL); - /* - * Fine if there's no default ns is scope, or if the - * default ns was undeclared. - */ - if ((ns == NULL) || (ns->href == NULL) || (ns->href[0] == 0)) - return(NULL); - - /* - * Undeclare the default namespace. - */ - xmlNewNs(target, BAD_CAST "", NULL); - /* TODO: Check result */ - return(NULL); - } - return(NULL); - } - /* - * Handle the XML namespace. - * QUESTION: Is this faster than using xmlStrEqual() anyway? - */ - if ((nsPrefix != NULL) && - (nsPrefix[0] == 'x') && (nsPrefix[1] == 'm') && - (nsPrefix[2] == 'l') && (nsPrefix[3] == 0)) - { - return(xmlSearchNs(target->doc, target, nsPrefix)); - } - /* - * First: search on the result element itself. - */ - if (target->nsDef != NULL) { - ns = target->nsDef; - do { - if ((ns->prefix == NULL) == (nsPrefix == NULL)) { - if (ns->prefix == nsPrefix) { - if (xmlStrEqual(ns->href, nsName)) - return(ns); - prefixOccupied = 1; - break; - } else if (xmlStrEqual(ns->prefix, nsPrefix)) { - if (xmlStrEqual(ns->href, nsName)) - return(ns); - prefixOccupied = 1; - break; - } - } - ns = ns->next; - } while (ns != NULL); - } - if (prefixOccupied) { - /* - * If the ns-prefix is occupied by an other ns-decl on the - * result element, then this means: - * 1) The desired prefix is shadowed - * 2) There's no way around changing the prefix - * - * Try a desperate search for an in-scope ns-decl - * with a matching ns-name before we use the last option, - * which is to recreate the ns-decl with a modified prefix. - */ - ns = xmlSearchNsByHref(target->doc, target, nsName); - if (ns != NULL) - return(ns); - - /* - * Fallback to changing the prefix. - */ - } else if ((target->parent != NULL) && - (target->parent->type == XML_ELEMENT_NODE)) - { - /* - * Try to find a matching ns-decl in the ancestor-axis. - * - * Check the common case: The parent element of the current - * result element is in the same namespace (with an equal ns-prefix). - */ - if ((target->parent->ns != NULL) && - ((target->parent->ns->prefix != NULL) == (nsPrefix != NULL))) - { - ns = target->parent->ns; - - if (nsPrefix == NULL) { - if (xmlStrEqual(ns->href, nsName)) - return(ns); - } else if (xmlStrEqual(ns->prefix, nsPrefix) && - xmlStrEqual(ns->href, nsName)) - { - return(ns); - } - } - /* - * Lookup the remaining in-scope namespaces. - */ - ns = xmlSearchNs(target->doc, target->parent, nsPrefix); - if (ns != NULL) { - if (xmlStrEqual(ns->href, nsName)) - return(ns); - /* - * Now check for a nasty case: We need to ensure that the new - * ns-decl won't shadow a prefix in-use by an existing attribute. - * - * - * - * val-b - * - * - */ - if (target->properties) { - xmlAttrPtr attr = target->properties; - do { - if ((attr->ns) && - xmlStrEqual(attr->ns->prefix, nsPrefix)) - { - /* - * Bad, this prefix is already in use. - * Since we'll change the prefix anyway, try - * a search for a matching ns-decl based on the - * namespace name. - */ - ns = xmlSearchNsByHref(target->doc, target, nsName); - if (ns != NULL) - return(ns); - goto declare_new_prefix; - } - attr = attr->next; - } while (attr != NULL); - } - } else { - /* - * Either no matching ns-prefix was found or the namespace is - * shadowed. - * Create a new ns-decl on the current result element. - * - * Hmm, we could also try to reuse an in-scope - * namespace with a matching ns-name but a different - * ns-prefix. - * What has higher priority? - * 1) If keeping the prefix: create a new ns-decl. - * 2) If reusal: first lookup ns-names; then fallback - * to creation of a new ns-decl. - * REVISIT: this currently uses case 1) although - * the old way was use xmlSearchNsByHref() and to let change - * the prefix. - */ -#if 0 - ns = xmlSearchNsByHref(target->doc, target, nsName); - if (ns != NULL) - return(ns); -#endif - } - /* - * Create the ns-decl on the current result element. - */ - ns = xmlNewNs(target, nsName, nsPrefix); - /* TODO: check errors */ - return(ns); - } else { - /* - * This is either the root of the tree or something weird is going on. - */ - ns = xmlNewNs(target, nsName, nsPrefix); - /* TODO: Check result */ - return(ns); - } - -declare_new_prefix: - /* - * Fallback: we need to generate a new prefix and declare the namespace - * on the result element. - */ - { - xmlChar pref[30]; - int counter = 1; - - do { - snprintf((char *) pref, 30, "%s_%d", nsPrefix, counter++); - ns = xmlSearchNs(target->doc, target, BAD_CAST pref); - if (counter > 1000) { - xsltTransformError(ctxt, NULL, invocNode, - "Internal error in xsltAcquireResultInScopeNs(): " - "Failed to compute a unique ns-prefix for the " - "generated element"); - return(NULL); - } - } while (ns != NULL); - ns = xmlNewNs(target, nsName, BAD_CAST pref); - /* TODO: Check result */ - return(ns); - } - return(NULL); -} - -/** - * xsltGetNamespace: - * @ctxt: a transformation context - * @cur: the input node - * @ns: the namespace - * @out: the output node (or its parent) - * - * Find a matching (prefix and ns-name) ns-declaration - * for the requested @ns->prefix and @ns->href in the result tree. - * If none is found then a new ns-declaration will be - * added to @resultElem. If, in this case, the given prefix is - * already in use, then a ns-declaration with a modified ns-prefix - * be we created. - * - * Called by: - * - xsltCopyPropList() (*not* anymore) - * - xsltShallowCopyElement() - * - xsltCopyTreeInternal() (*not* anymore) - * - xsltApplyOneTemplateInt() (*not* in the refactored code), - * - xsltElement() (*not* anymore) - * - * Returns a namespace declaration or NULL in case of - * namespace fixup failures or API or internal errors. - */ -xmlNsPtr -xsltGetNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, xmlNsPtr ns, - xmlNodePtr out) -{ - - if (ns == NULL) - return(NULL); - -#ifdef XSLT_REFACTORED - /* - * Namespace exclusion and ns-aliasing is performed at - * compilation-time in the refactored code. - * Additionally, aliasing is not intended for non Literal - * Result Elements. - */ - return(xsltGetSpecialNamespace(ctxt, cur, ns->href, ns->prefix, out)); -#else - { - xsltStylesheetPtr style; - const xmlChar *URI = NULL; /* the replacement URI */ - - if ((ctxt == NULL) || (cur == NULL) || (out == NULL)) - return(NULL); - - 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) { - return(xsltGetSpecialNamespace(ctxt, cur, NULL, NULL, out)); -#if 0 - /* - * TODO: Removed, since wrong. If there was no default - * namespace in the stylesheet then this must resolve to - * the NULL namespace. - */ - xmlNsPtr dflt; - dflt = xmlSearchNs(cur->doc, cur, NULL); - if (dflt != NULL) - URI = dflt->href; - else - return NULL; -#endif - } else if (URI == NULL) - URI = ns->href; - - return(xsltGetSpecialNamespace(ctxt, cur, URI, ns->prefix, out)); - } -#endif -} - -/** - * xsltGetPlainNamespace: - * @ctxt: a transformation context - * @cur: the input node - * @ns: the namespace - * @out: the result element - * - * Obsolete. - * *Not* called by any Libxslt/Libexslt function. - * Exaclty the same as xsltGetNamespace(). - * - * Returns a namespace declaration or NULL in case of - * namespace fixup failures or API or internal errors. - */ -xmlNsPtr -xsltGetPlainNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, - xmlNsPtr ns, xmlNodePtr out) -{ - return(xsltGetNamespace(ctxt, cur, ns, out)); -} - -/** - * xsltCopyNamespaceList: - * @ctxt: a transformation context - * @node: the target node - * @cur: the first namespace - * - * Do a copy of an namespace list. If @node is non-NULL the - * new namespaces are added automatically. This handles namespaces - * aliases. - * This function is intended only for *internal* use at - * transformation-time for copying ns-declarations of Literal - * Result Elements. - * - * Called by: - * xsltCopyTreeInternal() (transform.c) - * xsltShallowCopyElem() (transform.c) - * - * REVISIT: This function won't be used in the refactored code. - * - * Returns: a new xmlNsPtr, or NULL in case of error. - */ -xmlNsPtr -xsltCopyNamespaceList(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNsPtr cur) { - xmlNsPtr ret = NULL, tmp; - xmlNsPtr p = NULL,q; - - if (cur == NULL) - return(NULL); - if (cur->type != XML_NAMESPACE_DECL) - return(NULL); - - /* - * One can add namespaces only on element nodes - */ - if ((node != NULL) && (node->type != XML_ELEMENT_NODE)) - node = NULL; - - while (cur != NULL) { - if (cur->type != XML_NAMESPACE_DECL) - break; - - /* - * Avoid duplicating namespace declarations in the tree if - * a matching declaration is in scope. - */ - if (node != NULL) { - if ((node->ns != NULL) && - (xmlStrEqual(node->ns->prefix, cur->prefix)) && - (xmlStrEqual(node->ns->href, cur->href))) { - cur = cur->next; - continue; - } - tmp = xmlSearchNs(node->doc, node, cur->prefix); - if ((tmp != NULL) && (xmlStrEqual(tmp->href, cur->href))) { - cur = cur->next; - continue; - } - } -#ifdef XSLT_REFACTORED - /* - * Namespace exclusion and ns-aliasing is performed at - * compilation-time in the refactored code. - */ - q = xmlNewNs(node, cur->href, cur->prefix); - if (p == NULL) { - ret = p = q; - } else { - p->next = q; - p = q; - } -#else - /* - * TODO: Remove this if the refactored code gets enabled. - */ - if (!xmlStrEqual(cur->href, XSLT_NAMESPACE)) { - const xmlChar *URI; - /* TODO apply cascading */ - URI = (const xmlChar *) xmlHashLookup(ctxt->style->nsAliases, - cur->href); - if (URI == UNDEFINED_DEFAULT_NS) - continue; - if (URI != NULL) { - q = xmlNewNs(node, URI, cur->prefix); - } else { - q = xmlNewNs(node, cur->href, cur->prefix); - } - if (p == NULL) { - ret = p = q; - } else { - p->next = q; - p = q; - } - } -#endif - cur = cur->next; - } - return(ret); -} - -/** - * xsltCopyNamespace: - * @ctxt: a transformation context - * @elem: the target element node - * @ns: the namespace node - * - * Copies a namespace node (declaration). If @elem is not NULL, - * then the new namespace will be declared on @elem. - * - * Returns: a new xmlNsPtr, or NULL in case of an error. - */ -xmlNsPtr -xsltCopyNamespace(xsltTransformContextPtr ctxt, xmlNodePtr elem, - xmlNsPtr ns) -{ - if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL)) - return(NULL); - /* - * One can add namespaces only on element nodes - */ - if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE)) - return(xmlNewNs(NULL, ns->href, ns->prefix)); - else - return(xmlNewNs(elem, ns->href, ns->prefix)); -} - - -/** - * xsltFreeNamespaceAliasHashes: - * @style: an XSLT stylesheet - * - * Free up the memory used by namespaces aliases - */ -void -xsltFreeNamespaceAliasHashes(xsltStylesheetPtr style) { - if (style->nsAliases != NULL) - xmlHashFree((xmlHashTablePtr) style->nsAliases, NULL); - style->nsAliases = NULL; -} +/* + * namespaces.c: Implementation of the XSLT namespaces handling + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_MATH_H +#include +#endif +#ifdef HAVE_FLOAT_H +#include +#endif +#ifdef HAVE_IEEEFP_H +#include +#endif +#ifdef HAVE_NAN_H +#include +#endif +#ifdef HAVE_CTYPE_H +#include +#endif + +#include +#include +#include +#include +#include +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "namespaces.h" +#include "imports.h" + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +#ifdef XSLT_REFACTORED +static xsltNsAliasPtr +xsltNewNsAlias(xsltCompilerCtxtPtr cctxt) +{ + xsltNsAliasPtr ret; + + if (cctxt == NULL) + return(NULL); + + ret = (xsltNsAliasPtr) xmlMalloc(sizeof(xsltNsAlias)); + if (ret == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "Internal error in xsltNewNsAlias(): Memory allocation failed.\n"); + cctxt->style->errors++; + return(NULL); + } + memset(ret, 0, sizeof(xsltNsAlias)); + /* + * TODO: Store the item at current stylesheet-level. + */ + ret->next = cctxt->nsAliases; + cctxt->nsAliases = ret; + + return(ret); +} +#endif /* XSLT_REFACTORED */ +/** + * xsltNamespaceAlias: + * @style: the XSLT stylesheet + * @node: the xsl:namespace-alias node + * + * Read the stylesheet-prefix and result-prefix attributes, register + * them as well as the corresponding namespace. + */ +void +xsltNamespaceAlias(xsltStylesheetPtr style, xmlNodePtr node) +{ + xmlChar *resultPrefix = NULL; + xmlChar *stylePrefix = NULL; + xmlNsPtr literalNs = NULL; + xmlNsPtr targetNs = NULL; + +#ifdef XSLT_REFACTORED + xsltNsAliasPtr alias; + + if ((style == NULL) || (node == NULL)) + return; + + /* + * SPEC XSLT 1.0: + * "If a namespace URI is declared to be an alias for multiple + * different namespace URIs, then the declaration with the highest + * import precedence is used. It is an error if there is more than + * one such declaration. An XSLT processor may signal the error; + * if it does not signal the error, it must recover by choosing, + * from amongst the declarations with the highest import precedence, + * the one that occurs last in the stylesheet." + * + * SPEC TODO: Check for the errors mentioned above. + */ + /* + * NOTE that the XSLT 2.0 also *does* use the NULL namespace if + * "#default" is used and there's no default namespace is scope. + * I.e., this is *not* an error. + * Most XSLT 1.0 implementations work this way. + * The XSLT 1.0 spec has nothing to say on the subject. + */ + /* + * Attribute "stylesheet-prefix". + */ + stylePrefix = xmlGetNsProp(node, (const xmlChar *)"stylesheet-prefix", NULL); + if (stylePrefix == NULL) { + xsltTransformError(NULL, style, node, + "The attribute 'stylesheet-prefix' is missing.\n"); + return; + } + if (xmlStrEqual(stylePrefix, (const xmlChar *)"#default")) + literalNs = xmlSearchNs(node->doc, node, NULL); + else { + literalNs = xmlSearchNs(node->doc, node, stylePrefix); + if (literalNs == NULL) { + xsltTransformError(NULL, style, node, + "Attribute 'stylesheet-prefix': There's no namespace " + "declaration in scope for the prefix '%s'.\n", + stylePrefix); + goto error; + } + } + /* + * Attribute "result-prefix". + */ + resultPrefix = xmlGetNsProp(node, (const xmlChar *)"result-prefix", NULL); + if (resultPrefix == NULL) { + xsltTransformError(NULL, style, node, + "The attribute 'result-prefix' is missing.\n"); + goto error; + } + if (xmlStrEqual(resultPrefix, (const xmlChar *)"#default")) + targetNs = xmlSearchNs(node->doc, node, NULL); + else { + targetNs = xmlSearchNs(node->doc, node, resultPrefix); + + if (targetNs == NULL) { + xsltTransformError(NULL, style, node, + "Attribute 'result-prefix': There's no namespace " + "declaration in scope for the prefix '%s'.\n", + stylePrefix); + goto error; + } + } + /* + * + * Same alias for multiple different target namespace URIs: + * TODO: The one with the highest import precedence is used. + * Example: + * + * + * + * + * Same target namespace URI for multiple different aliases: + * All alias-definitions will be used. + * Example: + * + * + * + * Cases using #default: + * + * TODO: Has this an effect at all? + * + * + * From namespace to no namespace. + * + * + * From no namespace to namespace. + */ + + + /* + * Store the ns-node in the alias-object. + */ + alias = xsltNewNsAlias(XSLT_CCTXT(style)); + if (alias == NULL) + return; + alias->literalNs = literalNs; + alias->targetNs = targetNs; + XSLT_CCTXT(style)->hasNsAliases = 1; + + +#else /* XSLT_REFACTORED */ + const xmlChar *literalNsName; + const xmlChar *targetNsName; + + + if ((style == NULL) || (node == NULL)) + return; + + stylePrefix = xmlGetNsProp(node, (const xmlChar *)"stylesheet-prefix", NULL); + if (stylePrefix == NULL) { + xsltTransformError(NULL, style, node, + "namespace-alias: stylesheet-prefix attribute missing\n"); + return; + } + resultPrefix = xmlGetNsProp(node, (const xmlChar *)"result-prefix", NULL); + if (resultPrefix == NULL) { + xsltTransformError(NULL, style, node, + "namespace-alias: result-prefix attribute missing\n"); + goto error; + } + + if (xmlStrEqual(stylePrefix, (const xmlChar *)"#default")) { + literalNs = xmlSearchNs(node->doc, node, NULL); + if (literalNs == NULL) { + literalNsName = NULL; + } else + literalNsName = literalNs->href; /* Yes - set for nsAlias table */ + } else { + literalNs = xmlSearchNs(node->doc, node, stylePrefix); + + if ((literalNs == NULL) || (literalNs->href == NULL)) { + xsltTransformError(NULL, style, node, + "namespace-alias: prefix %s not bound to any namespace\n", + stylePrefix); + goto error; + } else + literalNsName = literalNs->href; + } + + /* + * When "#default" is used for result, if a default namespace has not + * been explicitly declared the special value UNDEFINED_DEFAULT_NS is + * put into the nsAliases table + */ + if (xmlStrEqual(resultPrefix, (const xmlChar *)"#default")) { + targetNs = xmlSearchNs(node->doc, node, NULL); + if (targetNs == NULL) { + targetNsName = UNDEFINED_DEFAULT_NS; + } else + targetNsName = targetNs->href; + } else { + targetNs = xmlSearchNs(node->doc, node, resultPrefix); + + if ((targetNs == NULL) || (targetNs->href == NULL)) { + xsltTransformError(NULL, style, node, + "namespace-alias: prefix %s not bound to any namespace\n", + resultPrefix); + goto error; + } else + targetNsName = targetNs->href; + } + /* + * Special case: if #default is used for + * the stylesheet-prefix (literal namespace) and there's no default + * namespace in scope, we'll use style->defaultAlias for this. + */ + if (literalNsName == NULL) { + if (targetNs != NULL) { + /* + * BUG TODO: Is it not sufficient to have only 1 field for + * this, since subsequently alias declarations will + * overwrite this. + * Example: + * + * + * The mapping for "foo" won't be visible anymore. + */ + style->defaultAlias = targetNs->href; + } + } else { + if (style->nsAliases == NULL) + style->nsAliases = xmlHashCreate(10); + if (style->nsAliases == NULL) { + xsltTransformError(NULL, style, node, + "namespace-alias: cannot create hash table\n"); + goto error; + } + xmlHashAddEntry((xmlHashTablePtr) style->nsAliases, + literalNsName, (void *) targetNsName); + } +#endif /* else of XSLT_REFACTORED */ + +error: + if (stylePrefix != NULL) + xmlFree(stylePrefix); + if (resultPrefix != NULL) + xmlFree(resultPrefix); +} + +/** + * xsltGetSpecialNamespace: + * @ctxt: the transformation context + * @invocNode: the invoking node; e.g. a literal result element/attr; + * only used for error reports + * @nsName: the namespace name (or NULL) + * @nsPrefix: the suggested namespace prefix (or NULL) + * @target: the result element on which to anchor a namespace + * + * Find a matching (prefix and ns-name) ns-declaration + * for the requested @nsName and @nsPrefix in the result tree. + * If none is found then a new ns-declaration will be + * added to @resultElem. If, in this case, the given prefix is + * already in use, then a ns-declaration with a modified ns-prefix + * be we created. Note that this function's priority is to + * preserve ns-prefixes; it will only change a prefix if there's + * a namespace clash. + * If both @nsName and @nsPrefix are NULL, then this will try to + * "undeclare" a default namespace by declaring an xmlns="". + * + * Returns a namespace declaration or NULL. + */ +xmlNsPtr +xsltGetSpecialNamespace(xsltTransformContextPtr ctxt, xmlNodePtr invocNode, + const xmlChar *nsName, const xmlChar *nsPrefix, + xmlNodePtr target) +{ + xmlNsPtr ns; + int prefixOccupied = 0; + + if ((ctxt == NULL) || (target == NULL) || + (target->type != XML_ELEMENT_NODE)) + return(NULL); + + /* + * NOTE: Namespace exclusion and ns-aliasing is performed at + * compilation-time in the refactored code; so this need not be done + * here (it was in the old code). + * NOTE: @invocNode was named @cur in the old code and was documented to + * be an input node; since it was only used to anchor an error report + * somewhere, we can safely change this to @invocNode, which now + * will be the XSLT instruction (also a literal result element/attribute), + * which was responsible for this call. + */ + /* + * OPTIMIZE TODO: This all could be optimized by keeping track of + * the ns-decls currently in-scope via a specialized context. + */ + if ((nsPrefix == NULL) && ((nsName == NULL) || (nsName[0] == 0))) { + /* + * NOTE: the "undeclaration" of the default namespace was + * part of the logic of the old xsltGetSpecialNamespace() code, + * so we'll keep that mechanism. + * Related to the old code: bug #302020: + */ + /* + * OPTIMIZE TODO: This all could be optimized by keeping track of + * the ns-decls currently in-scope via a specialized context. + */ + /* + * Search on the result element itself. + */ + if (target->nsDef != NULL) { + ns = target->nsDef; + do { + if (ns->prefix == NULL) { + if ((ns->href != NULL) && (ns->href[0] != 0)) { + /* + * Raise a namespace normalization error. + */ + xsltTransformError(ctxt, NULL, invocNode, + "Namespace normalization error: Cannot undeclare " + "the default namespace, since the default namespace " + "'%s' is already declared on the result element " + "'%s'.\n", ns->href, target->name); + return(NULL); + } else { + /* + * The default namespace was undeclared on the + * result element. + */ + return(NULL); + } + break; + } + ns = ns->next; + } while (ns != NULL); + } + if ((target->parent != NULL) && + (target->parent->type == XML_ELEMENT_NODE)) + { + /* + * The parent element is in no namespace, so assume + * that there is no default namespace in scope. + */ + if (target->parent->ns == NULL) + return(NULL); + + ns = xmlSearchNs(target->doc, target->parent, + NULL); + /* + * Fine if there's no default ns is scope, or if the + * default ns was undeclared. + */ + if ((ns == NULL) || (ns->href == NULL) || (ns->href[0] == 0)) + return(NULL); + + /* + * Undeclare the default namespace. + */ + xmlNewNs(target, BAD_CAST "", NULL); + /* TODO: Check result */ + return(NULL); + } + return(NULL); + } + /* + * Handle the XML namespace. + * QUESTION: Is this faster than using xmlStrEqual() anyway? + */ + if ((nsPrefix != NULL) && + (nsPrefix[0] == 'x') && (nsPrefix[1] == 'm') && + (nsPrefix[2] == 'l') && (nsPrefix[3] == 0)) + { + return(xmlSearchNs(target->doc, target, nsPrefix)); + } + /* + * First: search on the result element itself. + */ + if (target->nsDef != NULL) { + ns = target->nsDef; + do { + if ((ns->prefix == NULL) == (nsPrefix == NULL)) { + if (ns->prefix == nsPrefix) { + if (xmlStrEqual(ns->href, nsName)) + return(ns); + prefixOccupied = 1; + break; + } else if (xmlStrEqual(ns->prefix, nsPrefix)) { + if (xmlStrEqual(ns->href, nsName)) + return(ns); + prefixOccupied = 1; + break; + } + } + ns = ns->next; + } while (ns != NULL); + } + if (prefixOccupied) { + /* + * If the ns-prefix is occupied by an other ns-decl on the + * result element, then this means: + * 1) The desired prefix is shadowed + * 2) There's no way around changing the prefix + * + * Try a desperate search for an in-scope ns-decl + * with a matching ns-name before we use the last option, + * which is to recreate the ns-decl with a modified prefix. + */ + ns = xmlSearchNsByHref(target->doc, target, nsName); + if (ns != NULL) + return(ns); + + /* + * Fallback to changing the prefix. + */ + } else if ((target->parent != NULL) && + (target->parent->type == XML_ELEMENT_NODE)) + { + /* + * Try to find a matching ns-decl in the ancestor-axis. + * + * Check the common case: The parent element of the current + * result element is in the same namespace (with an equal ns-prefix). + */ + if ((target->parent->ns != NULL) && + ((target->parent->ns->prefix != NULL) == (nsPrefix != NULL))) + { + ns = target->parent->ns; + + if (nsPrefix == NULL) { + if (xmlStrEqual(ns->href, nsName)) + return(ns); + } else if (xmlStrEqual(ns->prefix, nsPrefix) && + xmlStrEqual(ns->href, nsName)) + { + return(ns); + } + } + /* + * Lookup the remaining in-scope namespaces. + */ + ns = xmlSearchNs(target->doc, target->parent, nsPrefix); + if (ns != NULL) { + if (xmlStrEqual(ns->href, nsName)) + return(ns); + /* + * Now check for a nasty case: We need to ensure that the new + * ns-decl won't shadow a prefix in-use by an existing attribute. + * + * + * + * val-b + * + * + */ + if (target->properties) { + xmlAttrPtr attr = target->properties; + do { + if ((attr->ns) && + xmlStrEqual(attr->ns->prefix, nsPrefix)) + { + /* + * Bad, this prefix is already in use. + * Since we'll change the prefix anyway, try + * a search for a matching ns-decl based on the + * namespace name. + */ + ns = xmlSearchNsByHref(target->doc, target, nsName); + if (ns != NULL) + return(ns); + goto declare_new_prefix; + } + attr = attr->next; + } while (attr != NULL); + } + } else { + /* + * Either no matching ns-prefix was found or the namespace is + * shadowed. + * Create a new ns-decl on the current result element. + * + * Hmm, we could also try to reuse an in-scope + * namespace with a matching ns-name but a different + * ns-prefix. + * What has higher priority? + * 1) If keeping the prefix: create a new ns-decl. + * 2) If reusal: first lookup ns-names; then fallback + * to creation of a new ns-decl. + * REVISIT: this currently uses case 1) although + * the old way was use xmlSearchNsByHref() and to let change + * the prefix. + */ +#if 0 + ns = xmlSearchNsByHref(target->doc, target, nsName); + if (ns != NULL) + return(ns); +#endif + } + /* + * Create the ns-decl on the current result element. + */ + ns = xmlNewNs(target, nsName, nsPrefix); + /* TODO: check errors */ + return(ns); + } else { + /* + * This is either the root of the tree or something weird is going on. + */ + ns = xmlNewNs(target, nsName, nsPrefix); + /* TODO: Check result */ + return(ns); + } + +declare_new_prefix: + /* + * Fallback: we need to generate a new prefix and declare the namespace + * on the result element. + */ + { + xmlChar pref[30]; + int counter = 1; + + do { + snprintf((char *) pref, 30, "%s_%d", nsPrefix, counter++); + ns = xmlSearchNs(target->doc, target, BAD_CAST pref); + if (counter > 1000) { + xsltTransformError(ctxt, NULL, invocNode, + "Internal error in xsltAcquireResultInScopeNs(): " + "Failed to compute a unique ns-prefix for the " + "generated element"); + return(NULL); + } + } while (ns != NULL); + ns = xmlNewNs(target, nsName, BAD_CAST pref); + /* TODO: Check result */ + return(ns); + } + return(NULL); +} + +/** + * xsltGetNamespace: + * @ctxt: a transformation context + * @cur: the input node + * @ns: the namespace + * @out: the output node (or its parent) + * + * Find a matching (prefix and ns-name) ns-declaration + * for the requested @ns->prefix and @ns->href in the result tree. + * If none is found then a new ns-declaration will be + * added to @resultElem. If, in this case, the given prefix is + * already in use, then a ns-declaration with a modified ns-prefix + * be we created. + * + * Called by: + * - xsltCopyPropList() (*not* anymore) + * - xsltShallowCopyElement() + * - xsltCopyTreeInternal() (*not* anymore) + * - xsltApplySequenceConstructor() (*not* in the refactored code), + * - xsltElement() (*not* anymore) + * + * Returns a namespace declaration or NULL in case of + * namespace fixup failures or API or internal errors. + */ +xmlNsPtr +xsltGetNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, xmlNsPtr ns, + xmlNodePtr out) +{ + + if (ns == NULL) + return(NULL); + +#ifdef XSLT_REFACTORED + /* + * Namespace exclusion and ns-aliasing is performed at + * compilation-time in the refactored code. + * Additionally, aliasing is not intended for non Literal + * Result Elements. + */ + return(xsltGetSpecialNamespace(ctxt, cur, ns->href, ns->prefix, out)); +#else + { + xsltStylesheetPtr style; + const xmlChar *URI = NULL; /* the replacement URI */ + + if ((ctxt == NULL) || (cur == NULL) || (out == NULL)) + return(NULL); + + 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) { + return(xsltGetSpecialNamespace(ctxt, cur, NULL, NULL, out)); +#if 0 + /* + * TODO: Removed, since wrong. If there was no default + * namespace in the stylesheet then this must resolve to + * the NULL namespace. + */ + xmlNsPtr dflt; + dflt = xmlSearchNs(cur->doc, cur, NULL); + if (dflt != NULL) + URI = dflt->href; + else + return NULL; +#endif + } else if (URI == NULL) + URI = ns->href; + + return(xsltGetSpecialNamespace(ctxt, cur, URI, ns->prefix, out)); + } +#endif +} + +/** + * xsltGetPlainNamespace: + * @ctxt: a transformation context + * @cur: the input node + * @ns: the namespace + * @out: the result element + * + * Obsolete. + * *Not* called by any Libxslt/Libexslt function. + * Exaclty the same as xsltGetNamespace(). + * + * Returns a namespace declaration or NULL in case of + * namespace fixup failures or API or internal errors. + */ +xmlNsPtr +xsltGetPlainNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, + xmlNsPtr ns, xmlNodePtr out) +{ + return(xsltGetNamespace(ctxt, cur, ns, out)); +} + +/** + * xsltCopyNamespaceList: + * @ctxt: a transformation context + * @node: the target node + * @cur: the first namespace + * + * Do a copy of an namespace list. If @node is non-NULL the + * new namespaces are added automatically. This handles namespaces + * aliases. + * This function is intended only for *internal* use at + * transformation-time for copying ns-declarations of Literal + * Result Elements. + * + * Called by: + * xsltCopyTreeInternal() (transform.c) + * xsltShallowCopyElem() (transform.c) + * + * REVISIT: This function won't be used in the refactored code. + * + * Returns: a new xmlNsPtr, or NULL in case of error. + */ +xmlNsPtr +xsltCopyNamespaceList(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNsPtr cur) { + xmlNsPtr ret = NULL, tmp; + xmlNsPtr p = NULL,q; + + if (cur == NULL) + return(NULL); + if (cur->type != XML_NAMESPACE_DECL) + return(NULL); + + /* + * One can add namespaces only on element nodes + */ + if ((node != NULL) && (node->type != XML_ELEMENT_NODE)) + node = NULL; + + while (cur != NULL) { + if (cur->type != XML_NAMESPACE_DECL) + break; + + /* + * Avoid duplicating namespace declarations in the tree if + * a matching declaration is in scope. + */ + if (node != NULL) { + if ((node->ns != NULL) && + (xmlStrEqual(node->ns->prefix, cur->prefix)) && + (xmlStrEqual(node->ns->href, cur->href))) { + cur = cur->next; + continue; + } + tmp = xmlSearchNs(node->doc, node, cur->prefix); + if ((tmp != NULL) && (xmlStrEqual(tmp->href, cur->href))) { + cur = cur->next; + continue; + } + } +#ifdef XSLT_REFACTORED + /* + * Namespace exclusion and ns-aliasing is performed at + * compilation-time in the refactored code. + */ + q = xmlNewNs(node, cur->href, cur->prefix); + if (p == NULL) { + ret = p = q; + } else { + p->next = q; + p = q; + } +#else + /* + * TODO: Remove this if the refactored code gets enabled. + */ + if (!xmlStrEqual(cur->href, XSLT_NAMESPACE)) { + const xmlChar *URI; + /* TODO apply cascading */ + URI = (const xmlChar *) xmlHashLookup(ctxt->style->nsAliases, + cur->href); + if (URI == UNDEFINED_DEFAULT_NS) + continue; + if (URI != NULL) { + q = xmlNewNs(node, URI, cur->prefix); + } else { + q = xmlNewNs(node, cur->href, cur->prefix); + } + if (p == NULL) { + ret = p = q; + } else { + p->next = q; + p = q; + } + } +#endif + cur = cur->next; + } + return(ret); +} + +/** + * xsltCopyNamespace: + * @ctxt: a transformation context + * @elem: the target element node + * @ns: the namespace node + * + * Copies a namespace node (declaration). If @elem is not NULL, + * then the new namespace will be declared on @elem. + * + * Returns: a new xmlNsPtr, or NULL in case of an error. + */ +xmlNsPtr +xsltCopyNamespace(xsltTransformContextPtr ctxt, xmlNodePtr elem, + xmlNsPtr ns) +{ + if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL)) + return(NULL); + /* + * One can add namespaces only on element nodes + */ + if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE)) + return(xmlNewNs(NULL, ns->href, ns->prefix)); + else + return(xmlNewNs(elem, ns->href, ns->prefix)); +} + + +/** + * xsltFreeNamespaceAliasHashes: + * @style: an XSLT stylesheet + * + * Free up the memory used by namespaces aliases + */ +void +xsltFreeNamespaceAliasHashes(xsltStylesheetPtr style) { + if (style->nsAliases != NULL) + xmlHashFree((xmlHashTablePtr) style->nsAliases, NULL); + style->nsAliases = NULL; +} diff --git a/libxslt/pattern.c b/libxslt/pattern.c index 4fd593ae..7df37a21 100644 --- a/libxslt/pattern.c +++ b/libxslt/pattern.c @@ -1,2564 +1,2605 @@ -/* - * pattern.c: Implemetation of the template match compilation and lookup - * - * Reference: - * http://www.w3.org/TR/1999/REC-xslt-19991116 - * - * See Copyright for the status of this software. - * - * daniel@veillard.com - */ - -/* - * TODO: handle pathological cases like *[*[@a="b"]] - * TODO: detect [number] at compilation, optimize accordingly - */ - -#define IN_LIBXSLT -#include "libxslt.h" - -#include - -#include -#include -#include -#include -#include -#include -#include "xslt.h" -#include "xsltInternals.h" -#include "xsltutils.h" -#include "imports.h" -#include "templates.h" -#include "keys.h" -#include "pattern.h" - -#ifdef WITH_XSLT_DEBUG -#define WITH_XSLT_DEBUG_PATTERN -#endif - -/* - * Types are private: - */ - -typedef enum { - XSLT_OP_END=0, - XSLT_OP_ROOT, - XSLT_OP_ELEM, - XSLT_OP_CHILD, - XSLT_OP_ATTR, - XSLT_OP_PARENT, - XSLT_OP_ANCESTOR, - XSLT_OP_ID, - XSLT_OP_KEY, - XSLT_OP_NS, - XSLT_OP_ALL, - XSLT_OP_PI, - XSLT_OP_COMMENT, - XSLT_OP_TEXT, - XSLT_OP_NODE, - XSLT_OP_PREDICATE -} xsltOp; - -typedef struct _xsltStepState xsltStepState; -typedef xsltStepState *xsltStepStatePtr; -struct _xsltStepState { - int step; - xmlNodePtr node; -}; - -typedef struct _xsltStepStates xsltStepStates; -typedef xsltStepStates *xsltStepStatesPtr; -struct _xsltStepStates { - int nbstates; - int maxstates; - xsltStepStatePtr states; -}; - -typedef struct _xsltStepOp xsltStepOp; -typedef xsltStepOp *xsltStepOpPtr; -struct _xsltStepOp { - xsltOp op; - xmlChar *value; - xmlChar *value2; - xmlChar *value3; - xmlXPathCompExprPtr comp; - /* - * Optimisations for count - */ - int previousExtra; - int indexExtra; - int lenExtra; -}; - -struct _xsltCompMatch { - struct _xsltCompMatch *next; /* siblings in the name hash */ - float priority; /* the priority */ - const xmlChar *pattern; /* the pattern */ - const xmlChar *mode; /* the mode */ - const xmlChar *modeURI; /* the mode URI */ - xsltTemplatePtr template; /* the associated template */ - - int direct; - /* TODO fix the statically allocated size steps[] */ - int nbStep; - int maxStep; - xmlNsPtr *nsList; /* the namespaces in scope */ - int nsNr; /* the number of namespaces in scope */ - xsltStepOp steps[40]; /* ops for computation */ -}; - -typedef struct _xsltParserContext xsltParserContext; -typedef xsltParserContext *xsltParserContextPtr; -struct _xsltParserContext { - xsltStylesheetPtr style; /* the stylesheet */ - xsltTransformContextPtr ctxt; /* the transformation or NULL */ - const xmlChar *cur; /* the current char being parsed */ - const xmlChar *base; /* the full expression */ - xmlDocPtr doc; /* the source document */ - xmlNodePtr elem; /* the source element */ - int error; /* error code */ - xsltCompMatchPtr comp; /* the result */ -}; - -/************************************************************************ - * * - * Type functions * - * * - ************************************************************************/ - -/** - * xsltNewCompMatch: - * - * Create a new XSLT CompMatch - * - * Returns the newly allocated xsltCompMatchPtr or NULL in case of error - */ -static xsltCompMatchPtr -xsltNewCompMatch(void) { - xsltCompMatchPtr cur; - - cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch)); - if (cur == NULL) { - xsltTransformError(NULL, NULL, NULL, - "xsltNewCompMatch : malloc failed\n"); - return(NULL); - } - memset(cur, 0, sizeof(xsltCompMatch)); - cur->maxStep = 40; - cur->nsNr = 0; - cur->nsList = NULL; - cur->direct = 0; - return(cur); -} - -/** - * xsltFreeCompMatch: - * @comp: an XSLT comp - * - * Free up the memory allocated by @comp - */ -static void -xsltFreeCompMatch(xsltCompMatchPtr comp) { - xsltStepOpPtr op; - int i; - - if (comp == NULL) - return; - if (comp->pattern != NULL) - xmlFree((xmlChar *)comp->pattern); - if (comp->nsList != NULL) - xmlFree(comp->nsList); - for (i = 0;i < comp->nbStep;i++) { - op = &comp->steps[i]; - if (op->value != NULL) - xmlFree(op->value); - if (op->value2 != NULL) - xmlFree(op->value2); - if (op->value3 != NULL) - xmlFree(op->value3); - if (op->comp != NULL) - xmlXPathFreeCompExpr(op->comp); - } - memset(comp, -1, sizeof(xsltCompMatch)); - xmlFree(comp); -} - -/** - * xsltFreeCompMatchList: - * @comp: an XSLT comp list - * - * Free up the memory allocated by all the elements of @comp - */ -void -xsltFreeCompMatchList(xsltCompMatchPtr comp) { - xsltCompMatchPtr cur; - - while (comp != NULL) { - cur = comp; - comp = comp->next; - xsltFreeCompMatch(cur); - } -} - -/** - * xsltNormalizeCompSteps: - * @payload: pointer to template hash table entry - * @data: pointer to the stylesheet - * @name: template match name - * - * This is a hashtable scanner function to normalize the compiled - * steps of an imported stylesheet. - */ -void xsltNormalizeCompSteps(void *payload, - void *data, const xmlChar *name ATTRIBUTE_UNUSED) { - xsltCompMatchPtr comp = payload; - xsltStylesheetPtr style = data; - int ix; - - for (ix = 0; ix < comp->nbStep; ix++) { - comp->steps[ix].previousExtra += style->extrasNr; - comp->steps[ix].indexExtra += style->extrasNr; - comp->steps[ix].lenExtra += style->extrasNr; - } -} - -/** - * xsltNewParserContext: - * @style: the stylesheet - * @ctxt: the transformation context, if done at run-time - * - * Create a new XSLT ParserContext - * - * Returns the newly allocated xsltParserContextPtr or NULL in case of error - */ -static xsltParserContextPtr -xsltNewParserContext(xsltStylesheetPtr style, xsltTransformContextPtr ctxt) { - xsltParserContextPtr cur; - - cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext)); - if (cur == NULL) { - xsltTransformError(NULL, NULL, NULL, - "xsltNewParserContext : malloc failed\n"); - return(NULL); - } - memset(cur, 0, sizeof(xsltParserContext)); - cur->style = style; - cur->ctxt = ctxt; - return(cur); -} - -/** - * xsltFreeParserContext: - * @ctxt: an XSLT parser context - * - * Free up the memory allocated by @ctxt - */ -static void -xsltFreeParserContext(xsltParserContextPtr ctxt) { - if (ctxt == NULL) - return; - memset(ctxt, -1, sizeof(xsltParserContext)); - xmlFree(ctxt); -} - -/** - * xsltCompMatchAdd: - * @comp: the compiled match expression - * @op: an op - * @value: the first value - * @value2: the second value - * @novar: flag to set XML_XPATH_NOVAR - * - * Add an step to an XSLT Compiled Match - * - * Returns -1 in case of failure, 0 otherwise. - */ -static int -xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp, - xsltOp op, xmlChar * value, xmlChar * value2, int novar) -{ - if (comp->nbStep >= 40) { - xsltTransformError(NULL, NULL, NULL, - "xsltCompMatchAdd: overflow\n"); - return (-1); - } - comp->steps[comp->nbStep].op = op; - comp->steps[comp->nbStep].value = value; - comp->steps[comp->nbStep].value2 = value2; - if (ctxt->ctxt != NULL) { - comp->steps[comp->nbStep].previousExtra = - xsltAllocateExtraCtxt(ctxt->ctxt); - comp->steps[comp->nbStep].indexExtra = - xsltAllocateExtraCtxt(ctxt->ctxt); - comp->steps[comp->nbStep].lenExtra = - xsltAllocateExtraCtxt(ctxt->ctxt); - } else { - comp->steps[comp->nbStep].previousExtra = - xsltAllocateExtra(ctxt->style); - comp->steps[comp->nbStep].indexExtra = - xsltAllocateExtra(ctxt->style); - comp->steps[comp->nbStep].lenExtra = - xsltAllocateExtra(ctxt->style); - } - if (op == XSLT_OP_PREDICATE) { - xmlXPathContextPtr xctxt; - - if (ctxt->style != NULL) - xctxt = xmlXPathNewContext(ctxt->style->doc); - else - xctxt = xmlXPathNewContext(NULL); -#ifdef XML_XPATH_NOVAR - if (novar != 0) - xctxt->flags = XML_XPATH_NOVAR; -#endif - if (ctxt->style != NULL) - xctxt->dict = ctxt->style->dict; - comp->steps[comp->nbStep].comp = xmlXPathCtxtCompile(xctxt, value); - xmlXPathFreeContext(xctxt); - if (comp->steps[comp->nbStep].comp == NULL) { - xsltTransformError(NULL, ctxt->style, ctxt->elem, - "Failed to compile predicate\n"); - if (ctxt->style != NULL) - ctxt->style->errors++; - } - } - comp->nbStep++; - return (0); -} - -/** - * xsltSwapTopCompMatch: - * @comp: the compiled match expression - * - * reverse the two top steps. - */ -static void -xsltSwapTopCompMatch(xsltCompMatchPtr comp) { - int i; - int j = comp->nbStep - 1; - - if (j > 0) { - register xmlChar *tmp; - register xsltOp op; - register xmlXPathCompExprPtr expr; - i = j - 1; - tmp = comp->steps[i].value; - comp->steps[i].value = comp->steps[j].value; - comp->steps[j].value = tmp; - tmp = comp->steps[i].value2; - comp->steps[i].value2 = comp->steps[j].value2; - comp->steps[j].value2 = tmp; - op = comp->steps[i].op; - comp->steps[i].op = comp->steps[j].op; - comp->steps[j].op = op; - expr = comp->steps[i].comp; - comp->steps[i].comp = comp->steps[j].comp; - comp->steps[j].comp = expr; - } -} - -/** - * xsltReverseCompMatch: - * @comp: the compiled match expression - * - * reverse all the stack of expressions - */ -static void -xsltReverseCompMatch(xsltCompMatchPtr comp) { - int i = 0; - int j = comp->nbStep - 1; - - while (j > i) { - register xmlChar *tmp; - register xsltOp op; - register xmlXPathCompExprPtr expr; - tmp = comp->steps[i].value; - comp->steps[i].value = comp->steps[j].value; - comp->steps[j].value = tmp; - tmp = comp->steps[i].value2; - comp->steps[i].value2 = comp->steps[j].value2; - comp->steps[j].value2 = tmp; - op = comp->steps[i].op; - comp->steps[i].op = comp->steps[j].op; - comp->steps[j].op = op; - expr = comp->steps[i].comp; - comp->steps[i].comp = comp->steps[j].comp; - comp->steps[j].comp = expr; - j--; - i++; - } - comp->steps[comp->nbStep++].op = XSLT_OP_END; - /* - * detect consecutive XSLT_OP_PREDICATE indicating a direct - * matching should be done. - */ - for (i = 0;i < comp->nbStep - 1;i++) { - if ((comp->steps[i].op == XSLT_OP_PREDICATE) && - (comp->steps[i + 1].op == XSLT_OP_PREDICATE)) { - - comp->direct = 1; - if (comp->pattern[0] != '/') { - xmlChar *query; - - query = xmlStrdup((const xmlChar *)"//"); - query = xmlStrcat(query, comp->pattern); - - xmlFree((xmlChar *) comp->pattern); - comp->pattern = query; - } - break; - } - } -} - -/************************************************************************ - * * - * The interpreter for the precompiled patterns * - * * - ************************************************************************/ - -static int -xsltPatPushState(xsltStepStates *states, int step, xmlNodePtr node) { - if ((states->states == NULL) || (states->maxstates <= 0)) { - states->maxstates = 4; - states->nbstates = 0; - states->states = xmlMalloc(4 * sizeof(xsltStepState)); - } - else if (states->maxstates <= states->nbstates) { - xsltStepState *tmp; - - tmp = (xsltStepStatePtr) xmlRealloc(states->states, - 2 * states->maxstates * sizeof(xsltStepState)); - if (tmp == NULL) - return(-1); - states->states = tmp; - states->maxstates *= 2; - } - states->states[states->nbstates].step = step; - states->states[states->nbstates++].node = node; -#if 0 - fprintf(stderr, "Push: %d, %s\n", step, node->name); -#endif - return(0); -} - -/** - * xsltTestCompMatchDirect: - * @ctxt: a XSLT process context - * @comp: the precompiled pattern - * @node: a node - * - * Test whether the node matches the pattern, do a direct evalutation - * and not a step by step evaluation. - * - * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure - */ -static int -xsltTestCompMatchDirect(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp, - xmlNodePtr node) { - xsltStepOpPtr sel = NULL; - xmlDocPtr prevdoc; - xmlDocPtr doc; - xmlXPathObjectPtr list; - int ix, j; - int nocache = 0; - int isRVT; - - doc = node->doc; - if (XSLT_IS_RES_TREE_FRAG(doc)) - isRVT = 1; - else - isRVT = 0; - sel = &comp->steps[0]; /* store extra in first step arbitrarily */ - - prevdoc = (xmlDocPtr) - XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); - ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival); - list = (xmlXPathObjectPtr) - XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra); - - if ((list == NULL) || (prevdoc != doc)) { - xmlXPathObjectPtr newlist; - xmlNodePtr parent = node->parent; - xmlDocPtr olddoc; - xmlNodePtr oldnode; - - oldnode = ctxt->xpathCtxt->node; - olddoc = ctxt->xpathCtxt->doc; - ctxt->xpathCtxt->node = node; - ctxt->xpathCtxt->doc = doc; - newlist = xmlXPathEval(comp->pattern, ctxt->xpathCtxt); - ctxt->xpathCtxt->node = oldnode; - ctxt->xpathCtxt->doc = olddoc; - if (newlist == NULL) - return(-1); - if (newlist->type != XPATH_NODESET) { - xmlXPathFreeObject(newlist); - return(-1); - } - ix = 0; - - if ((parent == NULL) || (node->doc == NULL) || isRVT) - nocache = 1; - - if (nocache == 0) { - if (list != NULL) - xmlXPathFreeObject(list); - list = newlist; - - XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) = - (void *) list; - XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = - (void *) doc; - XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = - 0; - XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) = - (xmlFreeFunc) xmlXPathFreeObject; - } else - list = newlist; - } - if ((list->nodesetval == NULL) || - (list->nodesetval->nodeNr <= 0)) { - if (nocache == 1) - xmlXPathFreeObject(list); - return(0); - } - /* TODO: store the index and use it for the scan */ - if (ix == 0) { - for (j = 0;j < list->nodesetval->nodeNr;j++) { - if (list->nodesetval->nodeTab[j] == node) { - if (nocache == 1) - xmlXPathFreeObject(list); - return(1); - } - } - } else { - } - if (nocache == 1) - xmlXPathFreeObject(list); - return(0); -} - -/** - * xsltTestCompMatch: - * @ctxt: a XSLT process context - * @comp: the precompiled pattern - * @node: a node - * @mode: the mode name or NULL - * @modeURI: the mode URI or NULL - * - * Test whether the node matches the pattern - * - * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure - */ -static int -xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp, - xmlNodePtr node, const xmlChar *mode, - const xmlChar *modeURI) { - int i; - xsltStepOpPtr step, sel = NULL; - xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */ - - if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) { - xsltTransformError(ctxt, NULL, node, - "xsltTestCompMatch: null arg\n"); - return(-1); - } - if (mode != NULL) { - if (comp->mode == NULL) - return(0); - /* - * both mode strings must be interned on the stylesheet dictionary - */ - if (comp->mode != mode) - return(0); - } else { - if (comp->mode != NULL) - return(0); - } - if (modeURI != NULL) { - if (comp->modeURI == NULL) - return(0); - /* - * both modeURI strings must be interned on the stylesheet dictionary - */ - if (comp->modeURI != modeURI) - return(0); - } else { - if (comp->modeURI != NULL) - return(0); - } - - i = 0; -restart: - for (;i < comp->nbStep;i++) { - step = &comp->steps[i]; - if (step->op != XSLT_OP_PREDICATE) - sel = step; - switch (step->op) { - case XSLT_OP_END: - goto found; - case XSLT_OP_ROOT: - if ((node->type == XML_DOCUMENT_NODE) || -#ifdef LIBXML_DOCB_ENABLED - (node->type == XML_DOCB_DOCUMENT_NODE) || -#endif - (node->type == XML_HTML_DOCUMENT_NODE)) - continue; - if ((node->type == XML_ELEMENT_NODE) && (node->name[0] == ' ')) - continue; - goto rollback; - case XSLT_OP_ELEM: - if (node->type != XML_ELEMENT_NODE) - goto rollback; - if (step->value == NULL) - continue; - if (step->value[0] != node->name[0]) - goto rollback; - if (!xmlStrEqual(step->value, node->name)) - goto rollback; - - /* Namespace test */ - if (node->ns == NULL) { - if (step->value2 != NULL) - goto rollback; - } else if (node->ns->href != NULL) { - if (step->value2 == NULL) - goto rollback; - if (!xmlStrEqual(step->value2, node->ns->href)) - goto rollback; - } - continue; - case XSLT_OP_CHILD: { - xmlNodePtr lst; - - if ((node->type != XML_ELEMENT_NODE) && - (node->type != XML_DOCUMENT_NODE) && -#ifdef LIBXML_DOCB_ENABLED - (node->type != XML_DOCB_DOCUMENT_NODE) && -#endif - (node->type != XML_HTML_DOCUMENT_NODE)) - goto rollback; - - lst = node->children; - - if (step->value != NULL) { - while (lst != NULL) { - if ((lst->type == XML_ELEMENT_NODE) && - (step->value[0] == lst->name[0]) && - (xmlStrEqual(step->value, lst->name))) - break; - lst = lst->next; - } - if (lst != NULL) - continue; - } - goto rollback; - } - case XSLT_OP_ATTR: - if (node->type != XML_ATTRIBUTE_NODE) - goto rollback; - if (step->value != NULL) { - if (step->value[0] != node->name[0]) - goto rollback; - if (!xmlStrEqual(step->value, node->name)) - goto rollback; - } - /* Namespace test */ - if (node->ns == NULL) { - if (step->value2 != NULL) - goto rollback; - } else if (step->value2 != NULL) { - if (!xmlStrEqual(step->value2, node->ns->href)) - goto rollback; - } - continue; - case XSLT_OP_PARENT: - if ((node->type == XML_DOCUMENT_NODE) || - (node->type == XML_HTML_DOCUMENT_NODE) || -#ifdef LIBXML_DOCB_ENABLED - (node->type == XML_DOCB_DOCUMENT_NODE) || -#endif - (node->type == XML_NAMESPACE_DECL)) - goto rollback; - node = node->parent; - if (node == NULL) - goto rollback; - if (step->value == NULL) - continue; - if (step->value[0] != node->name[0]) - goto rollback; - if (!xmlStrEqual(step->value, node->name)) - goto rollback; - /* Namespace test */ - if (node->ns == NULL) { - if (step->value2 != NULL) - goto rollback; - } else if (node->ns->href != NULL) { - if (step->value2 == NULL) - goto rollback; - if (!xmlStrEqual(step->value2, node->ns->href)) - goto rollback; - } - continue; - case XSLT_OP_ANCESTOR: - /* TODO: implement coalescing of ANCESTOR/NODE ops */ - if (step->value == NULL) { - step = &comp->steps[i+1]; - if (step->op == XSLT_OP_ROOT) - goto found; - /* added NS, ID and KEY as a result of bug 168208 */ - if ((step->op != XSLT_OP_ELEM) && - (step->op != XSLT_OP_ALL) && - (step->op != XSLT_OP_NS) && - (step->op != XSLT_OP_ID) && - (step->op != XSLT_OP_KEY)) - goto rollback; - } - if (node == NULL) - goto rollback; - if ((node->type == XML_DOCUMENT_NODE) || - (node->type == XML_HTML_DOCUMENT_NODE) || -#ifdef LIBXML_DOCB_ENABLED - (node->type == XML_DOCB_DOCUMENT_NODE) || -#endif - (node->type == XML_NAMESPACE_DECL)) - goto rollback; - node = node->parent; - if ((step->op != XSLT_OP_ELEM) && step->op != XSLT_OP_ALL) { - xsltPatPushState(&states, i, node); - continue; - } - i++; - if (step->value == NULL) { - xsltPatPushState(&states, i - 1, node); - continue; - } - while (node != NULL) { - if ((node->type == XML_ELEMENT_NODE) && - (step->value[0] == node->name[0]) && - (xmlStrEqual(step->value, node->name))) { - /* Namespace test */ - if (node->ns == NULL) { - if (step->value2 == NULL) - break; - } else if (node->ns->href != NULL) { - if ((step->value2 != NULL) && - (xmlStrEqual(step->value2, node->ns->href))) - break; - } - } - node = node->parent; - } - if (node == NULL) - goto rollback; - xsltPatPushState(&states, i - 1, node); - continue; - case XSLT_OP_ID: { - /* TODO Handle IDs decently, must be done differently */ - xmlAttrPtr id; - - if (node->type != XML_ELEMENT_NODE) - goto rollback; - - id = xmlGetID(node->doc, step->value); - if ((id == NULL) || (id->parent != node)) - goto rollback; - break; - } - case XSLT_OP_KEY: { - xmlNodeSetPtr list; - int indx; - - list = xsltGetKey(ctxt, step->value, - step->value3, step->value2); - if (list == NULL) - goto rollback; - for (indx = 0;indx < list->nodeNr;indx++) - if (list->nodeTab[indx] == node) - break; - if (indx >= list->nodeNr) - goto rollback; - break; - } - case XSLT_OP_NS: - if (node->type != XML_ELEMENT_NODE) - goto rollback; - if (node->ns == NULL) { - if (step->value != NULL) - goto rollback; - } else if (node->ns->href != NULL) { - if (step->value == NULL) - goto rollback; - if (!xmlStrEqual(step->value, node->ns->href)) - goto rollback; - } - break; - case XSLT_OP_ALL: - if (node->type != XML_ELEMENT_NODE) - goto rollback; - break; - case XSLT_OP_PREDICATE: { - xmlNodePtr oldNode; - xmlDocPtr doc; - int oldCS, oldCP; - int pos = 0, len = 0; - int isRVT; - - /* - * when there is cascading XSLT_OP_PREDICATE, then use a - * direct computation approach. It's not done directly - * at the beginning of the routine to filter out as much - * as possible this costly computation. - */ - if (comp->direct) { - if (states.states != NULL) { - /* Free the rollback states */ - xmlFree(states.states); - } - return(xsltTestCompMatchDirect(ctxt, comp, node)); - } - - doc = node->doc; - if (XSLT_IS_RES_TREE_FRAG(doc)) - isRVT = 1; - else - isRVT = 0; - - /* - * Depending on the last selection, one may need to - * recompute contextSize and proximityPosition. - */ - oldCS = ctxt->xpathCtxt->contextSize; - oldCP = ctxt->xpathCtxt->proximityPosition; - if ((sel != NULL) && - (sel->op == XSLT_OP_ELEM) && - (sel->value != NULL) && - (node->type == XML_ELEMENT_NODE) && - (node->parent != NULL)) { - xmlNodePtr previous; - int ix, nocache = 0; - - previous = (xmlNodePtr) - XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); - ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival); - if ((previous != NULL) && - (previous->parent == node->parent)) { - /* - * just walk back to adjust the index - */ - int indx = 0; - xmlNodePtr sibling = node; - - while (sibling != NULL) { - if (sibling == previous) - break; - if ((previous->type == XML_ELEMENT_NODE) && - (previous->name != NULL) && - (sibling->name != NULL) && - (previous->name[0] == sibling->name[0]) && - (xmlStrEqual(previous->name, sibling->name))) { - if ((sel->value2 == NULL) || - ((sibling->ns != NULL) && - (xmlStrEqual(sel->value2, - sibling->ns->href)))) - indx++; - } - sibling = sibling->prev; - } - if (sibling == NULL) { - /* hum going backward in document order ... */ - indx = 0; - sibling = node; - while (sibling != NULL) { - if (sibling == previous) - break; - if ((sel->value2 == NULL) || - ((sibling->ns != NULL) && - (xmlStrEqual(sel->value2, - sibling->ns->href)))) - indx--; - sibling = sibling->next; - } - } - if (sibling != NULL) { - pos = ix + indx; - /* - * If the node is in a Value Tree we need to - * save len, but cannot cache the node! - * (bugs 153137 and 158840) - */ - if (node->doc != NULL) { - len = XSLT_RUNTIME_EXTRA(ctxt, - sel->lenExtra, ival); - if (!isRVT) { - XSLT_RUNTIME_EXTRA(ctxt, - sel->previousExtra, ptr) = node; - XSLT_RUNTIME_EXTRA(ctxt, - sel->indexExtra, ival) = pos; - } - } - ix = pos; - } else - pos = 0; - } else { - /* - * recompute the index - */ - xmlNodePtr siblings = node->parent->children; - xmlNodePtr parent = node->parent; - - while (siblings != NULL) { - if (siblings->type == XML_ELEMENT_NODE) { - if (siblings == node) { - len++; - pos = len; - } else if ((node->name != NULL) && - (siblings->name != NULL) && - (node->name[0] == siblings->name[0]) && - (xmlStrEqual(node->name, siblings->name))) { - if ((sel->value2 == NULL) || - ((siblings->ns != NULL) && - (xmlStrEqual(sel->value2, - siblings->ns->href)))) - len++; - } - } - siblings = siblings->next; - } - if ((parent == NULL) || (node->doc == NULL)) - nocache = 1; - else { - while (parent->parent != NULL) - parent = parent->parent; - if (((parent->type != XML_DOCUMENT_NODE) && - (parent->type != XML_HTML_DOCUMENT_NODE)) || - (parent != (xmlNodePtr) node->doc)) - nocache = 1; - } - } - if (pos != 0) { - ctxt->xpathCtxt->contextSize = len; - ctxt->xpathCtxt->proximityPosition = pos; - /* - * If the node is in a Value Tree we cannot - * cache it ! - */ - if ((!isRVT) && (node->doc != NULL) && - (nocache == 0)) { - XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = - node; - XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = - pos; - XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = - len; - } - } - } else if ((sel != NULL) && (sel->op == XSLT_OP_ALL) && - (node->type == XML_ELEMENT_NODE)) { - xmlNodePtr previous; - int ix, nocache = 0; - - previous = (xmlNodePtr) - XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); - ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival); - if ((previous != NULL) && - (previous->parent == node->parent)) { - /* - * just walk back to adjust the index - */ - int indx = 0; - xmlNodePtr sibling = node; - - while (sibling != NULL) { - if (sibling == previous) - break; - if (sibling->type == XML_ELEMENT_NODE) - indx++; - sibling = sibling->prev; - } - if (sibling == NULL) { - /* hum going backward in document order ... */ - indx = 0; - sibling = node; - while (sibling != NULL) { - if (sibling == previous) - break; - if (sibling->type == XML_ELEMENT_NODE) - indx--; - sibling = sibling->next; - } - } - if (sibling != NULL) { - pos = ix + indx; - /* - * If the node is in a Value Tree we cannot - * cache it ! - */ - if ((node->doc != NULL) && !isRVT) { - len = XSLT_RUNTIME_EXTRA(ctxt, - sel->lenExtra, ival); - XSLT_RUNTIME_EXTRA(ctxt, - sel->previousExtra, ptr) = node; - XSLT_RUNTIME_EXTRA(ctxt, - sel->indexExtra, ival) = pos; - } - } else - pos = 0; - } else { - /* - * recompute the index - */ - xmlNodePtr siblings = node->parent->children; - xmlNodePtr parent = node->parent; - - while (siblings != NULL) { - if (siblings->type == XML_ELEMENT_NODE) { - len++; - if (siblings == node) { - pos = len; - } - } - siblings = siblings->next; - } - if ((parent == NULL) || (node->doc == NULL)) - nocache = 1; - else { - while (parent->parent != NULL) - parent = parent->parent; - if (((parent->type != XML_DOCUMENT_NODE) && - (parent->type != XML_HTML_DOCUMENT_NODE)) || - (parent != (xmlNodePtr) node->doc)) - nocache = 1; - } - } - if (pos != 0) { - ctxt->xpathCtxt->contextSize = len; - ctxt->xpathCtxt->proximityPosition = pos; - /* - * If the node is in a Value Tree we cannot - * cache it ! - */ - if ((node->doc != NULL) && (nocache == 0) && !isRVT) { - XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = - node; - XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = - pos; - XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = - len; - } - } - } - oldNode = ctxt->node; - ctxt->node = node; - - if (step->value == NULL) - goto wrong_index; - if (step->comp == NULL) - goto wrong_index; - - if (!xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList, - comp->nsNr)) - goto wrong_index; - - if (pos != 0) { - ctxt->xpathCtxt->contextSize = oldCS; - ctxt->xpathCtxt->proximityPosition = oldCP; - } - ctxt->node = oldNode; - break; -wrong_index: - if (pos != 0) { - ctxt->xpathCtxt->contextSize = oldCS; - ctxt->xpathCtxt->proximityPosition = oldCP; - } - ctxt->node = oldNode; - goto rollback; - } - case XSLT_OP_PI: - if (node->type != XML_PI_NODE) - goto rollback; - if (step->value != NULL) { - if (!xmlStrEqual(step->value, node->name)) - goto rollback; - } - break; - case XSLT_OP_COMMENT: - if (node->type != XML_COMMENT_NODE) - goto rollback; - break; - case XSLT_OP_TEXT: - if ((node->type != XML_TEXT_NODE) && - (node->type != XML_CDATA_SECTION_NODE)) - goto rollback; - break; - case XSLT_OP_NODE: - switch (node->type) { - case XML_ELEMENT_NODE: - case XML_CDATA_SECTION_NODE: - case XML_PI_NODE: - case XML_COMMENT_NODE: - case XML_TEXT_NODE: - break; - default: - goto rollback; - } - break; - } - } -found: - if (states.states != NULL) { - /* Free the rollback states */ - xmlFree(states.states); - } - return(1); -rollback: - /* got an error try to rollback */ - if (states.states == NULL) - return(0); - if (states.nbstates <= 0) { - xmlFree(states.states); - return(0); - } - states.nbstates--; - i = states.states[states.nbstates].step; - node = states.states[states.nbstates].node; -#if 0 - fprintf(stderr, "Pop: %d, %s\n", i, node->name); -#endif - goto restart; -} - -/** - * xsltTestCompMatchList: - * @ctxt: a XSLT process context - * @node: a node - * @comp: the precompiled pattern list - * - * Test whether the node matches one of the patterns in the list - * - * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure - */ -int -xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node, - xsltCompMatchPtr comp) { - int ret; - - if ((ctxt == NULL) || (node == NULL)) - return(-1); - while (comp != NULL) { - ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL); - if (ret == 1) - return(1); - comp = comp->next; - } - return(0); -} - -/************************************************************************ - * * - * Dedicated parser for templates * - * * - ************************************************************************/ - -#define CUR (*ctxt->cur) -#define SKIP(val) ctxt->cur += (val) -#define NXT(val) ctxt->cur[(val)] -#define CUR_PTR ctxt->cur - -#define SKIP_BLANKS \ - while (IS_BLANK_CH(CUR)) NEXT - -#define CURRENT (*ctxt->cur) -#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) - - -#define PUSH(op, val, val2, novar) \ - if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto error; - -#define SWAP() \ - xsltSwapTopCompMatch(ctxt->comp); - -#define XSLT_ERROR(X) \ - { xsltError(ctxt, __FILE__, __LINE__, X); \ - ctxt->error = (X); return; } - -#define XSLT_ERROR0(X) \ - { xsltError(ctxt, __FILE__, __LINE__, X); \ - ctxt->error = (X); return(0); } - -/** - * xsltScanLiteral: - * @ctxt: the XPath Parser context - * - * Parse an XPath Litteral: - * - * [29] Literal ::= '"' [^"]* '"' - * | "'" [^']* "'" - * - * Returns the Literal parsed or NULL - */ - -static xmlChar * -xsltScanLiteral(xsltParserContextPtr ctxt) { - const xmlChar *q, *cur; - xmlChar *ret = NULL; - int val, len; - - SKIP_BLANKS; - if (CUR == '"') { - NEXT; - cur = q = CUR_PTR; - val = xmlStringCurrentChar(NULL, cur, &len); - while ((IS_CHAR(val)) && (val != '"')) { - cur += len; - val = xmlStringCurrentChar(NULL, cur, &len); - } - if (!IS_CHAR(val)) { - ctxt->error = 1; - return(NULL); - } else { - ret = xmlStrndup(q, cur - q); - } - cur += len; - CUR_PTR = cur; - } else if (CUR == '\'') { - NEXT; - cur = q = CUR_PTR; - val = xmlStringCurrentChar(NULL, cur, &len); - while ((IS_CHAR(val)) && (val != '\'')) { - cur += len; - val = xmlStringCurrentChar(NULL, cur, &len); - } - if (!IS_CHAR(val)) { - ctxt->error = 1; - return(NULL); - } else { - ret = xmlStrndup(q, cur - q); - } - cur += len; - CUR_PTR = cur; - } else { - /* XP_ERROR(XPATH_START_LITERAL_ERROR); */ - ctxt->error = 1; - return(NULL); - } - return(ret); -} - -/** - * xsltScanName: - * @ctxt: the XPath Parser context - * - * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | - * CombiningChar | Extender - * - * [5] Name ::= (Letter | '_' | ':') (NameChar)* - * - * [6] Names ::= Name (S Name)* - * - * Returns the Name parsed or NULL - */ - -static xmlChar * -xsltScanName(xsltParserContextPtr ctxt) { - const xmlChar *q, *cur; - xmlChar *ret = NULL; - int val, len; - - SKIP_BLANKS; - - cur = q = CUR_PTR; - val = xmlStringCurrentChar(NULL, cur, &len); - if (!IS_LETTER(val) && (val != '_') && (val != ':')) - return(NULL); - - while ((IS_LETTER(val)) || (IS_DIGIT(val)) || - (val == '.') || (val == '-') || - (val == '_') || - (IS_COMBINING(val)) || - (IS_EXTENDER(val))) { - cur += len; - val = xmlStringCurrentChar(NULL, cur, &len); - } - ret = xmlStrndup(q, cur - q); - CUR_PTR = cur; - return(ret); -} - -/** - * xsltScanNCName: - * @ctxt: the XPath Parser context - * - * Parses a non qualified name - * - * Returns the Name parsed or NULL - */ - -static xmlChar * -xsltScanNCName(xsltParserContextPtr ctxt) { - const xmlChar *q, *cur; - xmlChar *ret = NULL; - int val, len; - - SKIP_BLANKS; - - cur = q = CUR_PTR; - val = xmlStringCurrentChar(NULL, cur, &len); - if (!IS_LETTER(val) && (val != '_')) - return(NULL); - - while ((IS_LETTER(val)) || (IS_DIGIT(val)) || - (val == '.') || (val == '-') || - (val == '_') || - (IS_COMBINING(val)) || - (IS_EXTENDER(val))) { - cur += len; - val = xmlStringCurrentChar(NULL, cur, &len); - } - ret = xmlStrndup(q, cur - q); - CUR_PTR = cur; - return(ret); -} - -/** - * xsltScanQName: - * @ctxt: the XPath Parser context - * @prefix: the place to store the prefix - * - * Parse a qualified name - * - * Returns the Name parsed or NULL - */ - -static xmlChar * -xsltScanQName(xsltParserContextPtr ctxt, xmlChar **prefix) { - xmlChar *ret = NULL; - - *prefix = NULL; - ret = xsltScanNCName(ctxt); - if (CUR == ':') { - *prefix = ret; - NEXT; - ret = xsltScanNCName(ctxt); - } - return(ret); -} - -/* - * xsltCompileIdKeyPattern: - * @ctxt: the compilation context - * @name: a preparsed name - * @aid: whether id/key are allowed there - * @novar: flag to prohibit xslt var - * - * Compile the XSLT LocationIdKeyPattern - * [3] IdKeyPattern ::= 'id' '(' Literal ')' - * | 'key' '(' Literal ',' Literal ')' - * - * also handle NodeType and PI from: - * - * [7] NodeTest ::= NameTest - * | NodeType '(' ')' - * | 'processing-instruction' '(' Literal ')' - */ -static void -xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, - int aid, int novar) { - xmlChar *lit = NULL; - xmlChar *lit2 = NULL; - - if (CUR != '(') { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileIdKeyPattern : ( expected\n"); - ctxt->error = 1; - return; - } - if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) { - NEXT; - SKIP_BLANKS; - lit = xsltScanLiteral(ctxt); - if (ctxt->error) - return; - SKIP_BLANKS; - if (CUR != ')') { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileIdKeyPattern : ) expected\n"); - ctxt->error = 1; - return; - } - NEXT; - PUSH(XSLT_OP_ID, lit, NULL, novar); - } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) { - NEXT; - SKIP_BLANKS; - lit = xsltScanLiteral(ctxt); - if (ctxt->error) - return; - SKIP_BLANKS; - if (CUR != ',') { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileIdKeyPattern : , expected\n"); - ctxt->error = 1; - return; - } - NEXT; - SKIP_BLANKS; - lit2 = xsltScanLiteral(ctxt); - if (ctxt->error) - return; - SKIP_BLANKS; - if (CUR != ')') { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileIdKeyPattern : ) expected\n"); - ctxt->error = 1; - return; - } - NEXT; - /* URGENT TODO: support namespace in keys */ - PUSH(XSLT_OP_KEY, lit, lit2, novar); - } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) { - NEXT; - SKIP_BLANKS; - if (CUR != ')') { - lit = xsltScanLiteral(ctxt); - if (ctxt->error) - return; - SKIP_BLANKS; - if (CUR != ')') { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileIdKeyPattern : ) expected\n"); - ctxt->error = 1; - return; - } - } - NEXT; - PUSH(XSLT_OP_PI, lit, NULL, novar); - } else if (xmlStrEqual(name, (const xmlChar *)"text")) { - NEXT; - SKIP_BLANKS; - if (CUR != ')') { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileIdKeyPattern : ) expected\n"); - ctxt->error = 1; - return; - } - NEXT; - PUSH(XSLT_OP_TEXT, NULL, NULL, novar); - } else if (xmlStrEqual(name, (const xmlChar *)"comment")) { - NEXT; - SKIP_BLANKS; - if (CUR != ')') { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileIdKeyPattern : ) expected\n"); - ctxt->error = 1; - return; - } - NEXT; - PUSH(XSLT_OP_COMMENT, NULL, NULL, novar); - } else if (xmlStrEqual(name, (const xmlChar *)"node")) { - NEXT; - SKIP_BLANKS; - if (CUR != ')') { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileIdKeyPattern : ) expected\n"); - ctxt->error = 1; - return; - } - NEXT; - PUSH(XSLT_OP_NODE, NULL, NULL, novar); - } else if (aid) { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n"); - ctxt->error = 1; - return; - } else { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileIdKeyPattern : node type\n"); - ctxt->error = 1; - return; - } -error: - if (name != NULL) - xmlFree(name); -} - -/** - * xsltCompileStepPattern: - * @ctxt: the compilation context - * @token: a posible precompiled name - * @novar: flag to prohibit xslt variables from pattern - * - * Compile the XSLT StepPattern and generates a precompiled - * form suitable for fast matching. - * - * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate* - * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier - * | ('child' | 'attribute') '::' - * from XPath - * [7] NodeTest ::= NameTest - * | NodeType '(' ')' - * | 'processing-instruction' '(' Literal ')' - * [8] Predicate ::= '[' PredicateExpr ']' - * [9] PredicateExpr ::= Expr - * [13] AbbreviatedAxisSpecifier ::= '@'? - * [37] NameTest ::= '*' | NCName ':' '*' | QName - */ - -static void -xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) { - xmlChar *name = NULL; - const xmlChar *URI = NULL; - xmlChar *URL = NULL; - int level; - - SKIP_BLANKS; - if ((token == NULL) && (CUR == '@')) { - xmlChar *prefix = NULL; - - NEXT; - if (CUR == '*') { - NEXT; - PUSH(XSLT_OP_ATTR, NULL, NULL, novar); - goto parse_predicate; - } - token = xsltScanQName(ctxt, &prefix); - if (prefix != NULL) { - xmlNsPtr ns; - - ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix); - if (ns == NULL) { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileStepPattern : no namespace bound to prefix %s\n", - prefix); - } else { - URL = xmlStrdup(ns->href); - } - xmlFree(prefix); - } - if (token == NULL) { - if (CUR == '*') { - NEXT; - PUSH(XSLT_OP_ATTR, NULL, URL, novar); - return; - } - xsltTransformError(NULL, NULL, NULL, - "xsltCompileStepPattern : Name expected\n"); - ctxt->error = 1; - goto error; - } - PUSH(XSLT_OP_ATTR, token, URL, novar); - goto parse_predicate; - } - if (token == NULL) - token = xsltScanName(ctxt); - if (token == NULL) { - if (CUR == '*') { - NEXT; - PUSH(XSLT_OP_ALL, token, NULL, novar); - goto parse_predicate; - } else { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileStepPattern : Name expected\n"); - ctxt->error = 1; - goto error; - } - } - - - SKIP_BLANKS; - if (CUR == '(') { - xsltCompileIdKeyPattern(ctxt, token, 0, novar); - if (ctxt->error) - goto error; - } else if (CUR == ':') { - NEXT; - if (CUR != ':') { - xmlChar *prefix = token; - xmlNsPtr ns; - - /* - * This is a namespace match - */ - token = xsltScanName(ctxt); - ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix); - if (ns == NULL) { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileStepPattern : no namespace bound to prefix %s\n", - prefix); - ctxt->error = 1; - goto error; - } else { - URL = xmlStrdup(ns->href); - } - xmlFree(prefix); - if (token == NULL) { - if (CUR == '*') { - NEXT; - PUSH(XSLT_OP_NS, URL, NULL, novar); - } else { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileStepPattern : Name expected\n"); - ctxt->error = 1; - goto error; - } - } else { - PUSH(XSLT_OP_ELEM, token, URL, novar); - } - } else { - NEXT; - if (xmlStrEqual(token, (const xmlChar *) "child")) { - xmlFree(token); - token = xsltScanName(ctxt); - if (token == NULL) { - if (CUR == '*') { - NEXT; - PUSH(XSLT_OP_ALL, token, NULL, novar); - goto parse_predicate; - } else { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileStepPattern : QName expected\n"); - ctxt->error = 1; - goto error; - } - } - URI = xsltGetQNameURI(ctxt->elem, &token); - if (token == NULL) { - ctxt->error = 1; - goto error; - } else { - name = xmlStrdup(token); - if (URI != NULL) - URL = xmlStrdup(URI); - } - PUSH(XSLT_OP_CHILD, name, URL, novar); - } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) { - xmlFree(token); - token = xsltScanName(ctxt); - if (token == NULL) { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileStepPattern : QName expected\n"); - ctxt->error = 1; - goto error; - } - URI = xsltGetQNameURI(ctxt->elem, &token); - if (token == NULL) { - ctxt->error = 1; - goto error; - } else { - name = xmlStrdup(token); - if (URI != NULL) - URL = xmlStrdup(URI); - } - PUSH(XSLT_OP_ATTR, name, URL, novar); - } else { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileStepPattern : 'child' or 'attribute' expected\n"); - ctxt->error = 1; - goto error; - } - xmlFree(token); - } - } else if (CUR == '*') { - NEXT; - PUSH(XSLT_OP_ALL, token, NULL, novar); - } else { - URI = xsltGetQNameURI(ctxt->elem, &token); - if (token == NULL) { - ctxt->error = 1; - goto error; - } - if (URI != NULL) - URL = xmlStrdup(URI); - PUSH(XSLT_OP_ELEM, token, URL, novar); - } -parse_predicate: - SKIP_BLANKS; - level = 0; - while (CUR == '[') { - const xmlChar *q; - xmlChar *ret = NULL; - - level++; - NEXT; - q = CUR_PTR; - while (CUR != 0) { - /* Skip over nested predicates */ - if (CUR == '[') - level++; - else if (CUR == ']') { - level--; - if (level == 0) - break; - } else if (CUR == '"') { - NEXT; - while ((CUR != 0) && (CUR != '"')) - NEXT; - } else if (CUR == '\'') { - NEXT; - while ((CUR != 0) && (CUR != '\'')) - NEXT; - } - NEXT; - } - if (CUR == 0) { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileStepPattern : ']' expected\n"); - ctxt->error = 1; - return; - } - ret = xmlStrndup(q, CUR_PTR - q); - PUSH(XSLT_OP_PREDICATE, ret, NULL, novar); - /* push the predicate lower than local test */ - SWAP(); - NEXT; - SKIP_BLANKS; - } - return; -error: - if (token != NULL) - xmlFree(token); - if (name != NULL) - xmlFree(name); -} - -/** - * xsltCompileRelativePathPattern: - * @comp: the compilation context - * @token: a posible precompiled name - * @novar: flag to prohibit xslt variables - * - * Compile the XSLT RelativePathPattern and generates a precompiled - * form suitable for fast matching. - * - * [4] RelativePathPattern ::= StepPattern - * | RelativePathPattern '/' StepPattern - * | RelativePathPattern '//' StepPattern - */ -static void -xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) { - xsltCompileStepPattern(ctxt, token, novar); - if (ctxt->error) - goto error; - SKIP_BLANKS; - while ((CUR != 0) && (CUR != '|')) { - if ((CUR == '/') && (NXT(1) == '/')) { - PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar); - NEXT; - NEXT; - SKIP_BLANKS; - xsltCompileStepPattern(ctxt, NULL, novar); - } else if (CUR == '/') { - PUSH(XSLT_OP_PARENT, NULL, NULL, novar); - NEXT; - SKIP_BLANKS; - if ((CUR != 0) && (CUR != '|')) { - xsltCompileRelativePathPattern(ctxt, NULL, novar); - } - } else { - ctxt->error = 1; - } - if (ctxt->error) - goto error; - SKIP_BLANKS; - } -error: - return; -} - -/** - * xsltCompileLocationPathPattern: - * @ctxt: the compilation context - * @novar: flag to prohibit xslt variables - * - * Compile the XSLT LocationPathPattern and generates a precompiled - * form suitable for fast matching. - * - * [2] LocationPathPattern ::= '/' RelativePathPattern? - * | IdKeyPattern (('/' | '//') RelativePathPattern)? - * | '//'? RelativePathPattern - */ -static void -xsltCompileLocationPathPattern(xsltParserContextPtr ctxt, int novar) { - SKIP_BLANKS; - if ((CUR == '/') && (NXT(1) == '/')) { - /* - * since we reverse the query - * a leading // can be safely ignored - */ - NEXT; - NEXT; - ctxt->comp->priority = 0.5; /* '//' means not 0 priority */ - xsltCompileRelativePathPattern(ctxt, NULL, novar); - } else if (CUR == '/') { - /* - * We need to find root as the parent - */ - NEXT; - SKIP_BLANKS; - PUSH(XSLT_OP_ROOT, NULL, NULL, novar); - if ((CUR != 0) && (CUR != '|')) { - PUSH(XSLT_OP_PARENT, NULL, NULL, novar); - xsltCompileRelativePathPattern(ctxt, NULL, novar); - } - } else if (CUR == '*') { - xsltCompileRelativePathPattern(ctxt, NULL, novar); - } else if (CUR == '@') { - xsltCompileRelativePathPattern(ctxt, NULL, novar); - } else { - xmlChar *name; - name = xsltScanName(ctxt); - if (name == NULL) { - xsltTransformError(NULL, NULL, NULL, - "xsltCompileLocationPathPattern : Name expected\n"); - ctxt->error = 1; - return; - } - SKIP_BLANKS; - if ((CUR == '(') && !xmlXPathIsNodeType(name)) { - xsltCompileIdKeyPattern(ctxt, name, 1, novar); - if ((CUR == '/') && (NXT(1) == '/')) { - PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar); - NEXT; - NEXT; - SKIP_BLANKS; - xsltCompileRelativePathPattern(ctxt, NULL, novar); - } else if (CUR == '/') { - PUSH(XSLT_OP_PARENT, NULL, NULL, novar); - NEXT; - SKIP_BLANKS; - xsltCompileRelativePathPattern(ctxt, NULL, novar); - } - return; - } - xsltCompileRelativePathPattern(ctxt, name, novar); - } -error: - return; -} - -/** - * xsltCompilePatternInternal: - * @pattern: an XSLT pattern - * @doc: the containing document - * @node: the containing element - * @style: the stylesheet - * @runtime: the transformation context, if done at run-time - * @novar: flag to prohibit xslt variables - * - * Compile the XSLT pattern and generates a list of precompiled form suitable - * for fast matching. - * - * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern - * - * Returns the generated pattern list or NULL in case of failure - */ - -static xsltCompMatchPtr -xsltCompilePatternInternal(const xmlChar *pattern, xmlDocPtr doc, - xmlNodePtr node, xsltStylesheetPtr style, - xsltTransformContextPtr runtime, int novar) { - xsltParserContextPtr ctxt = NULL; - xsltCompMatchPtr element, first = NULL, previous = NULL; - int current, start, end, level, j; - - if (pattern == NULL) { - xsltTransformError(NULL, NULL, node, - "xsltCompilePattern : NULL pattern\n"); - return(NULL); - } - - ctxt = xsltNewParserContext(style, runtime); - if (ctxt == NULL) - return(NULL); - ctxt->doc = doc; - ctxt->elem = node; - current = end = 0; - while (pattern[current] != 0) { - start = current; - while (IS_BLANK_CH(pattern[current])) - current++; - end = current; - level = 0; - while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) { - if (pattern[end] == '[') - level++; - else if (pattern[end] == ']') - level--; - else if (pattern[end] == '\'') { - end++; - while ((pattern[end] != 0) && (pattern[end] != '\'')) - end++; - } else if (pattern[end] == '"') { - end++; - while ((pattern[end] != 0) && (pattern[end] != '"')) - end++; - } - end++; - } - if (current == end) { - xsltTransformError(NULL, NULL, node, - "xsltCompilePattern : NULL pattern\n"); - goto error; - } - element = xsltNewCompMatch(); - if (element == NULL) { - goto error; - } - if (first == NULL) - first = element; - else if (previous != NULL) - previous->next = element; - previous = element; - - ctxt->comp = element; - ctxt->base = xmlStrndup(&pattern[start], end - start); - if (ctxt->base == NULL) - goto error; - ctxt->cur = &(ctxt->base)[current - start]; - element->pattern = ctxt->base; - element->nsList = xmlGetNsList(doc, node); - j = 0; - if (element->nsList != NULL) { - while (element->nsList[j] != NULL) - j++; - } - element->nsNr = j; - - -#ifdef WITH_XSLT_DEBUG_PATTERN - xsltGenericDebug(xsltGenericDebugContext, - "xsltCompilePattern : parsing '%s'\n", - element->pattern); -#endif - /* - Preset default priority to be zero. - This may be changed by xsltCompileLocationPathPattern. - */ - element->priority = 0; - xsltCompileLocationPathPattern(ctxt, novar); - if (ctxt->error) { - xsltTransformError(NULL, style, node, - "xsltCompilePattern : failed to compile '%s'\n", - element->pattern); - if (style != NULL) style->errors++; - goto error; - } - - /* - * Reverse for faster interpretation. - */ - xsltReverseCompMatch(element); - - /* - * Set-up the priority - */ - if (element->priority == 0) { /* if not yet determined */ - if (((element->steps[0].op == XSLT_OP_ELEM) || - (element->steps[0].op == XSLT_OP_ATTR) || - (element->steps[0].op == XSLT_OP_PI)) && - (element->steps[0].value != NULL) && - (element->steps[1].op == XSLT_OP_END)) { - ; /* previously preset */ - } else if ((element->steps[0].op == XSLT_OP_ATTR) && - (element->steps[0].value2 != NULL) && - (element->steps[1].op == XSLT_OP_END)) { - element->priority = -0.25; - } else if ((element->steps[0].op == XSLT_OP_NS) && - (element->steps[0].value != NULL) && - (element->steps[1].op == XSLT_OP_END)) { - element->priority = -0.25; - } else if ((element->steps[0].op == XSLT_OP_ATTR) && - (element->steps[0].value == NULL) && - (element->steps[0].value2 == NULL) && - (element->steps[1].op == XSLT_OP_END)) { - element->priority = -0.5; - } else if (((element->steps[0].op == XSLT_OP_PI) || - (element->steps[0].op == XSLT_OP_TEXT) || - (element->steps[0].op == XSLT_OP_ALL) || - (element->steps[0].op == XSLT_OP_NODE) || - (element->steps[0].op == XSLT_OP_COMMENT)) && - (element->steps[1].op == XSLT_OP_END)) { - element->priority = -0.5; - } else { - element->priority = 0.5; - } - } -#ifdef WITH_XSLT_DEBUG_PATTERN - xsltGenericDebug(xsltGenericDebugContext, - "xsltCompilePattern : parsed %s, default priority %f\n", - element->pattern, element->priority); -#endif - if (pattern[end] == '|') - end++; - current = end; - } - if (end == 0) { - xsltTransformError(NULL, style, node, - "xsltCompilePattern : NULL pattern\n"); - if (style != NULL) style->errors++; - goto error; - } - - xsltFreeParserContext(ctxt); - return(first); - -error: - if (ctxt != NULL) - xsltFreeParserContext(ctxt); - if (first != NULL) - xsltFreeCompMatchList(first); - return(NULL); -} - -/** - * xsltCompilePattern: - * @pattern: an XSLT pattern - * @doc: the containing document - * @node: the containing element - * @style: the stylesheet - * @runtime: the transformation context, if done at run-time - * - * Compile the XSLT pattern and generates a list of precompiled form suitable - * for fast matching. - * - * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern - * - * Returns the generated pattern list or NULL in case of failure - */ - -xsltCompMatchPtr -xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc, - xmlNodePtr node, xsltStylesheetPtr style, - xsltTransformContextPtr runtime) { - return (xsltCompilePatternInternal(pattern, doc, node, style, runtime, 0)); -} - -/************************************************************************ - * * - * Module interfaces * - * * - ************************************************************************/ - -/** - * xsltAddTemplate: - * @style: an XSLT stylesheet - * @cur: an XSLT template - * @mode: the mode name or NULL - * @modeURI: the mode URI or NULL - * - * Register the XSLT pattern associated to @cur - * - * Returns -1 in case of error, 0 otherwise - */ -int -xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur, - const xmlChar *mode, const xmlChar *modeURI) { - xsltCompMatchPtr pat, list, *top = NULL, next; - const xmlChar *name = NULL; - float priority; /* the priority */ - - if ((style == NULL) || (cur == NULL) || (cur->match == NULL)) - return(-1); - - priority = cur->priority; - pat = xsltCompilePatternInternal(cur->match, style->doc, cur->elem, - style, NULL, 1); - while (pat) { - next = pat->next; - pat->next = NULL; - name = NULL; - - pat->template = cur; - if (mode != NULL) - pat->mode = xmlDictLookup(style->dict, mode, -1); - if (modeURI != NULL) - pat->modeURI = xmlDictLookup(style->dict, modeURI, -1); - if (priority != XSLT_PAT_NO_PRIORITY) - pat->priority = priority; - - /* - * insert it in the hash table list corresponding to its lookup name - */ - switch (pat->steps[0].op) { - case XSLT_OP_ATTR: - if (pat->steps[0].value != NULL) - name = pat->steps[0].value; - else - top = (xsltCompMatchPtr *) &(style->attrMatch); - break; - case XSLT_OP_CHILD: - case XSLT_OP_PARENT: - case XSLT_OP_ANCESTOR: - top = (xsltCompMatchPtr *) &(style->elemMatch); - break; - case XSLT_OP_ROOT: - top = (xsltCompMatchPtr *) &(style->rootMatch); - break; - case XSLT_OP_KEY: - top = (xsltCompMatchPtr *) &(style->keyMatch); - break; - case XSLT_OP_ID: - /* TODO optimize ID !!! */ - case XSLT_OP_NS: - case XSLT_OP_ALL: - top = (xsltCompMatchPtr *) &(style->elemMatch); - break; - case XSLT_OP_END: - case XSLT_OP_PREDICATE: - xsltTransformError(NULL, style, NULL, - "xsltAddTemplate: invalid compiled pattern\n"); - xsltFreeCompMatch(pat); - return(-1); - /* - * TODO: some flags at the top level about type based patterns - * would be faster than inclusion in the hash table. - */ - case XSLT_OP_PI: - if (pat->steps[0].value != NULL) - name = pat->steps[0].value; - else - top = (xsltCompMatchPtr *) &(style->piMatch); - break; - case XSLT_OP_COMMENT: - top = (xsltCompMatchPtr *) &(style->commentMatch); - break; - case XSLT_OP_TEXT: - top = (xsltCompMatchPtr *) &(style->textMatch); - break; - case XSLT_OP_ELEM: - case XSLT_OP_NODE: - if (pat->steps[0].value != NULL) - name = pat->steps[0].value; - else - top = (xsltCompMatchPtr *) &(style->elemMatch); - break; - } - if (name != NULL) { - if (style->templatesHash == NULL) { - style->templatesHash = xmlHashCreate(1024); - if (style->templatesHash == NULL) { - xsltFreeCompMatch(pat); - return(-1); - } - xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat); - } else { - list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash, - name, mode, modeURI); - if (list == NULL) { - xmlHashAddEntry3(style->templatesHash, name, - mode, modeURI, pat); - } else { - /* - * Note '<=' since one must choose among the matching - * template rules that are left, the one that occurs - * last in the stylesheet - */ - if (list->priority <= pat->priority) { - pat->next = list; - xmlHashUpdateEntry3(style->templatesHash, name, - mode, modeURI, pat, NULL); - } else { - while (list->next != NULL) { - if (list->next->priority <= pat->priority) - break; - list = list->next; - } - pat->next = list->next; - list->next = pat; - } - } - } - } else if (top != NULL) { - list = *top; - if (list == NULL) { - *top = pat; - pat->next = NULL; - } else if (list->priority <= pat->priority) { - pat->next = list; - *top = pat; - } else { - while (list->next != NULL) { - if (list->next->priority <= pat->priority) - break; - list = list->next; - } - pat->next = list->next; - list->next = pat; - } - } else { - xsltTransformError(NULL, style, NULL, - "xsltAddTemplate: invalid compiled pattern\n"); - xsltFreeCompMatch(pat); - return(-1); - } -#ifdef WITH_XSLT_DEBUG_PATTERN - if (mode) - xsltGenericDebug(xsltGenericDebugContext, - "added pattern : '%s' mode '%s' priority %f\n", - pat->pattern, pat->mode, pat->priority); - else - xsltGenericDebug(xsltGenericDebugContext, - "added pattern : '%s' priority %f\n", - pat->pattern, pat->priority); -#endif - - pat = next; - } - return(0); -} - -#ifdef XSLT_REFACTORED_KEYCOMP -static int -xsltComputeAllKeys(xsltTransformContextPtr ctxt, - xsltDocumentPtr document) -{ - xsltStylesheetPtr style, style2; - xsltKeyDefPtr keyd, keyd2; - xsltKeyTablePtr table; - - if ((ctxt == NULL) || (document == NULL)) - return(-1); - - if (document->nbKeysComputed == ctxt->nbKeys) - return(0); - /* - * TODO: This could be further optimized - */ - style = ctxt->style; - while (style) { - keyd = (xsltKeyDefPtr) style->keys; - while (keyd != NULL) { - /* - * Check if keys with this QName have been already - * computed. - */ - table = (xsltKeyTablePtr) document->keys; - while (table) { - if (((keyd->nameURI != NULL) == (table->nameURI != NULL)) && - xmlStrEqual(keyd->name, table->name) && - xmlStrEqual(keyd->nameURI, table->nameURI)) - { - break; - } - table = table->next; - } - if (table == NULL) { - /* - * Keys with this QName have not been yet computed. - */ - style2 = ctxt->style; - while (style2 != NULL) { - keyd2 = (xsltKeyDefPtr) style2->keys; - while (keyd2 != NULL) { - if (((keyd2->nameURI != NULL) == - (keyd->nameURI != NULL)) && - xmlStrEqual(keyd2->name, keyd->name) && - xmlStrEqual(keyd2->nameURI, keyd->nameURI)) - { - xsltInitCtxtKey(ctxt, document, keyd2); - if (document->nbKeysComputed == ctxt->nbKeys) - return(0); - } - keyd2 = keyd2->next; - } - style2 = xsltNextImport(style2); - } - } - keyd = keyd->next; - } - style = xsltNextImport(style); - } - return(0); -} -#endif - -/** - * xsltGetTemplate: - * @ctxt: a XSLT process context - * @node: the node being processed - * @style: the current style - * - * Finds the template applying to this node, if @style is non-NULL - * it means one needs to look for the next imported template in scope. - * - * Returns the xsltTemplatePtr or NULL if not found - */ -xsltTemplatePtr -xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, - xsltStylesheetPtr style) { - xsltStylesheetPtr curstyle; - xsltTemplatePtr ret = NULL; - const xmlChar *name = NULL; - xsltCompMatchPtr list = NULL; - float priority; - int keyed = 0; - - if ((ctxt == NULL) || (node == NULL)) - return(NULL); - - if (style == NULL) { - curstyle = ctxt->style; - } else { - curstyle = xsltNextImport(style); - } - - while ((curstyle != NULL) && (curstyle != style)) { - priority = XSLT_PAT_NO_PRIORITY; - /* TODO : handle IDs/keys here ! */ - if (curstyle->templatesHash != NULL) { - /* - * Use the top name as selector - */ - switch (node->type) { - case XML_ELEMENT_NODE: - if (node->name[0] == ' ') - break; - case XML_ATTRIBUTE_NODE: - case XML_PI_NODE: - name = node->name; - break; - case XML_DOCUMENT_NODE: - case XML_HTML_DOCUMENT_NODE: - case XML_TEXT_NODE: - case XML_CDATA_SECTION_NODE: - case XML_COMMENT_NODE: - case XML_ENTITY_REF_NODE: - case XML_ENTITY_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_NAMESPACE_DECL: - case XML_XINCLUDE_START: - case XML_XINCLUDE_END: - break; - default: - return(NULL); - - } - } - if (name != NULL) { - /* - * find the list of applicable expressions based on the name - */ - list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash, - name, ctxt->mode, ctxt->modeURI); - } else - list = NULL; - while (list != NULL) { - if (xsltTestCompMatch(ctxt, list, node, - ctxt->mode, ctxt->modeURI)) { - ret = list->template; - priority = list->priority; - break; - } - list = list->next; - } - list = NULL; - - /* - * find alternate generic matches - */ - switch (node->type) { - case XML_ELEMENT_NODE: - if (node->name[0] == ' ') - list = curstyle->rootMatch; - else - list = curstyle->elemMatch; - if (node->psvi != NULL) keyed = 1; - break; - case XML_ATTRIBUTE_NODE: { - xmlAttrPtr attr; - - list = curstyle->attrMatch; - attr = (xmlAttrPtr) node; - if (attr->psvi != NULL) keyed = 1; - break; - } - case XML_PI_NODE: - list = curstyle->piMatch; - if (node->psvi != NULL) keyed = 1; - break; - case XML_DOCUMENT_NODE: - case XML_HTML_DOCUMENT_NODE: { - xmlDocPtr doc; - - list = curstyle->rootMatch; - doc = (xmlDocPtr) node; - if (doc->psvi != NULL) keyed = 1; - break; - } - case XML_TEXT_NODE: - case XML_CDATA_SECTION_NODE: - list = curstyle->textMatch; - if (node->psvi != NULL) keyed = 1; - break; - case XML_COMMENT_NODE: - list = curstyle->commentMatch; - if (node->psvi != NULL) keyed = 1; - break; - case XML_ENTITY_REF_NODE: - case XML_ENTITY_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_NAMESPACE_DECL: - case XML_XINCLUDE_START: - case XML_XINCLUDE_END: - break; - default: - break; - } - while ((list != NULL) && - ((ret == NULL) || (list->priority > priority))) { - if (xsltTestCompMatch(ctxt, list, node, - ctxt->mode, ctxt->modeURI)) { - ret = list->template; - priority = list->priority; - break; - } - list = list->next; - } - /* - * Some of the tests for elements can also apply to documents - */ - if ((node->type == XML_DOCUMENT_NODE) || - (node->type == XML_HTML_DOCUMENT_NODE) || - (node->type == XML_TEXT_NODE)) { - list = curstyle->elemMatch; - while ((list != NULL) && - ((ret == NULL) || (list->priority > priority))) { - if (xsltTestCompMatch(ctxt, list, node, - ctxt->mode, ctxt->modeURI)) { - ret = list->template; - priority = list->priority; - break; - } - list = list->next; - } - } else if ((node->type == XML_PI_NODE) || - (node->type == XML_COMMENT_NODE)) { - list = curstyle->elemMatch; - while ((list != NULL) && - ((ret == NULL) || (list->priority > priority))) { - if (xsltTestCompMatch(ctxt, list, node, - ctxt->mode, ctxt->modeURI)) { - ret = list->template; - priority = list->priority; - break; - } - list = list->next; - } - } - -#ifdef XSLT_REFACTORED_KEYCOMP -keyed_match: -#endif - if (keyed) { - list = curstyle->keyMatch; - while ((list != NULL) && - ((ret == NULL) || (list->priority > priority))) { - if (xsltTestCompMatch(ctxt, list, node, - ctxt->mode, ctxt->modeURI)) { - ret = list->template; - priority = list->priority; - break; - } - list = list->next; - } - } -#ifdef XSLT_REFACTORED_KEYCOMP - else if (ctxt->hasTemplKeyPatterns && - (ctxt->document->nbKeysComputed < ctxt->nbKeys)) - { - /* - * Compute all remaining keys for this document. - * - * REVISIT TODO: I think this could be further optimized. - */ - xsltComputeAllKeys(ctxt, ctxt->document); - - switch (node->type) { - case XML_ELEMENT_NODE: - if (node->psvi != NULL) keyed = 1; - break; - case XML_ATTRIBUTE_NODE: - if (((xmlAttrPtr) node)->psvi != NULL) keyed = 1; - break; - case XML_TEXT_NODE: - case XML_CDATA_SECTION_NODE: - case XML_COMMENT_NODE: - case XML_PI_NODE: - if (node->psvi != NULL) keyed = 1; - break; - case XML_DOCUMENT_NODE: - case XML_HTML_DOCUMENT_NODE: - if (((xmlDocPtr) node)->psvi != NULL) keyed = 1; - break; - default: - break; - } - if (keyed) - goto keyed_match; - } -#endif /* XSLT_REFACTORED_KEYCOMP */ - if (ret != NULL) - return(ret); - - /* - * Cycle on next curstylesheet import. - */ - curstyle = xsltNextImport(curstyle); - } - return(NULL); -} - -/** - * xsltCleanupTemplates: - * @style: an XSLT stylesheet - * - * Cleanup the state of the templates used by the stylesheet and - * the ones it imports. - */ -void -xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED) { -} - -/** - * xsltFreeTemplateHashes: - * @style: an XSLT stylesheet - * - * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism - */ -void -xsltFreeTemplateHashes(xsltStylesheetPtr style) { - if (style->templatesHash != NULL) - xmlHashFree((xmlHashTablePtr) style->templatesHash, - (xmlHashDeallocator) xsltFreeCompMatchList); - if (style->rootMatch != NULL) - xsltFreeCompMatchList(style->rootMatch); - if (style->keyMatch != NULL) - xsltFreeCompMatchList(style->keyMatch); - if (style->elemMatch != NULL) - xsltFreeCompMatchList(style->elemMatch); - if (style->attrMatch != NULL) - xsltFreeCompMatchList(style->attrMatch); - if (style->parentMatch != NULL) - xsltFreeCompMatchList(style->parentMatch); - if (style->textMatch != NULL) - xsltFreeCompMatchList(style->textMatch); - if (style->piMatch != NULL) - xsltFreeCompMatchList(style->piMatch); - if (style->commentMatch != NULL) - xsltFreeCompMatchList(style->commentMatch); -} - +/* + * pattern.c: Implemetation of the template match compilation and lookup + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +/* + * TODO: handle pathological cases like *[*[@a="b"]] + * TODO: detect [number] at compilation, optimize accordingly + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include + +#include +#include +#include +#include +#include +#include +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "imports.h" +#include "templates.h" +#include "keys.h" +#include "pattern.h" +#include "documents.h" + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_PATTERN +#endif + +/* + * Types are private: + */ + +typedef enum { + XSLT_OP_END=0, + XSLT_OP_ROOT, + XSLT_OP_ELEM, + XSLT_OP_CHILD, + XSLT_OP_ATTR, + XSLT_OP_PARENT, + XSLT_OP_ANCESTOR, + XSLT_OP_ID, + XSLT_OP_KEY, + XSLT_OP_NS, + XSLT_OP_ALL, + XSLT_OP_PI, + XSLT_OP_COMMENT, + XSLT_OP_TEXT, + XSLT_OP_NODE, + XSLT_OP_PREDICATE +} xsltOp; + +typedef struct _xsltStepState xsltStepState; +typedef xsltStepState *xsltStepStatePtr; +struct _xsltStepState { + int step; + xmlNodePtr node; +}; + +typedef struct _xsltStepStates xsltStepStates; +typedef xsltStepStates *xsltStepStatesPtr; +struct _xsltStepStates { + int nbstates; + int maxstates; + xsltStepStatePtr states; +}; + +typedef struct _xsltStepOp xsltStepOp; +typedef xsltStepOp *xsltStepOpPtr; +struct _xsltStepOp { + xsltOp op; + xmlChar *value; + xmlChar *value2; + xmlChar *value3; + xmlXPathCompExprPtr comp; + /* + * Optimisations for count + */ + int previousExtra; + int indexExtra; + int lenExtra; +}; + +struct _xsltCompMatch { + struct _xsltCompMatch *next; /* siblings in the name hash */ + float priority; /* the priority */ + const xmlChar *pattern; /* the pattern */ + const xmlChar *mode; /* the mode */ + const xmlChar *modeURI; /* the mode URI */ + xsltTemplatePtr template; /* the associated template */ + + int direct; + /* TODO fix the statically allocated size steps[] */ + int nbStep; + int maxStep; + xmlNsPtr *nsList; /* the namespaces in scope */ + int nsNr; /* the number of namespaces in scope */ + xsltStepOp steps[40]; /* ops for computation */ +}; + +typedef struct _xsltParserContext xsltParserContext; +typedef xsltParserContext *xsltParserContextPtr; +struct _xsltParserContext { + xsltStylesheetPtr style; /* the stylesheet */ + xsltTransformContextPtr ctxt; /* the transformation or NULL */ + const xmlChar *cur; /* the current char being parsed */ + const xmlChar *base; /* the full expression */ + xmlDocPtr doc; /* the source document */ + xmlNodePtr elem; /* the source element */ + int error; /* error code */ + xsltCompMatchPtr comp; /* the result */ +}; + +/************************************************************************ + * * + * Type functions * + * * + ************************************************************************/ + +/** + * xsltNewCompMatch: + * + * Create a new XSLT CompMatch + * + * Returns the newly allocated xsltCompMatchPtr or NULL in case of error + */ +static xsltCompMatchPtr +xsltNewCompMatch(void) { + xsltCompMatchPtr cur; + + cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewCompMatch : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltCompMatch)); + cur->maxStep = 40; + cur->nsNr = 0; + cur->nsList = NULL; + cur->direct = 0; + return(cur); +} + +/** + * xsltFreeCompMatch: + * @comp: an XSLT comp + * + * Free up the memory allocated by @comp + */ +static void +xsltFreeCompMatch(xsltCompMatchPtr comp) { + xsltStepOpPtr op; + int i; + + if (comp == NULL) + return; + if (comp->pattern != NULL) + xmlFree((xmlChar *)comp->pattern); + if (comp->nsList != NULL) + xmlFree(comp->nsList); + for (i = 0;i < comp->nbStep;i++) { + op = &comp->steps[i]; + if (op->value != NULL) + xmlFree(op->value); + if (op->value2 != NULL) + xmlFree(op->value2); + if (op->value3 != NULL) + xmlFree(op->value3); + if (op->comp != NULL) + xmlXPathFreeCompExpr(op->comp); + } + memset(comp, -1, sizeof(xsltCompMatch)); + xmlFree(comp); +} + +/** + * xsltFreeCompMatchList: + * @comp: an XSLT comp list + * + * Free up the memory allocated by all the elements of @comp + */ +void +xsltFreeCompMatchList(xsltCompMatchPtr comp) { + xsltCompMatchPtr cur; + + while (comp != NULL) { + cur = comp; + comp = comp->next; + xsltFreeCompMatch(cur); + } +} + +/** + * xsltNormalizeCompSteps: + * @payload: pointer to template hash table entry + * @data: pointer to the stylesheet + * @name: template match name + * + * This is a hashtable scanner function to normalize the compiled + * steps of an imported stylesheet. + */ +void xsltNormalizeCompSteps(void *payload, + void *data, const xmlChar *name ATTRIBUTE_UNUSED) { + xsltCompMatchPtr comp = payload; + xsltStylesheetPtr style = data; + int ix; + + for (ix = 0; ix < comp->nbStep; ix++) { + comp->steps[ix].previousExtra += style->extrasNr; + comp->steps[ix].indexExtra += style->extrasNr; + comp->steps[ix].lenExtra += style->extrasNr; + } +} + +/** + * xsltNewParserContext: + * @style: the stylesheet + * @ctxt: the transformation context, if done at run-time + * + * Create a new XSLT ParserContext + * + * Returns the newly allocated xsltParserContextPtr or NULL in case of error + */ +static xsltParserContextPtr +xsltNewParserContext(xsltStylesheetPtr style, xsltTransformContextPtr ctxt) { + xsltParserContextPtr cur; + + cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewParserContext : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltParserContext)); + cur->style = style; + cur->ctxt = ctxt; + return(cur); +} + +/** + * xsltFreeParserContext: + * @ctxt: an XSLT parser context + * + * Free up the memory allocated by @ctxt + */ +static void +xsltFreeParserContext(xsltParserContextPtr ctxt) { + if (ctxt == NULL) + return; + memset(ctxt, -1, sizeof(xsltParserContext)); + xmlFree(ctxt); +} + +/** + * xsltCompMatchAdd: + * @comp: the compiled match expression + * @op: an op + * @value: the first value + * @value2: the second value + * @novar: flag to set XML_XPATH_NOVAR + * + * Add an step to an XSLT Compiled Match + * + * Returns -1 in case of failure, 0 otherwise. + */ +static int +xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp, + xsltOp op, xmlChar * value, xmlChar * value2, int novar) +{ + if (comp->nbStep >= 40) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompMatchAdd: overflow\n"); + return (-1); + } + comp->steps[comp->nbStep].op = op; + comp->steps[comp->nbStep].value = value; + comp->steps[comp->nbStep].value2 = value2; + if (ctxt->ctxt != NULL) { + comp->steps[comp->nbStep].previousExtra = + xsltAllocateExtraCtxt(ctxt->ctxt); + comp->steps[comp->nbStep].indexExtra = + xsltAllocateExtraCtxt(ctxt->ctxt); + comp->steps[comp->nbStep].lenExtra = + xsltAllocateExtraCtxt(ctxt->ctxt); + } else { + comp->steps[comp->nbStep].previousExtra = + xsltAllocateExtra(ctxt->style); + comp->steps[comp->nbStep].indexExtra = + xsltAllocateExtra(ctxt->style); + comp->steps[comp->nbStep].lenExtra = + xsltAllocateExtra(ctxt->style); + } + if (op == XSLT_OP_PREDICATE) { + xmlXPathContextPtr xctxt; + + if (ctxt->style != NULL) + xctxt = xmlXPathNewContext(ctxt->style->doc); + else + xctxt = xmlXPathNewContext(NULL); +#ifdef XML_XPATH_NOVAR + if (novar != 0) + xctxt->flags = XML_XPATH_NOVAR; +#endif + if (ctxt->style != NULL) + xctxt->dict = ctxt->style->dict; + comp->steps[comp->nbStep].comp = xmlXPathCtxtCompile(xctxt, value); + xmlXPathFreeContext(xctxt); + if (comp->steps[comp->nbStep].comp == NULL) { + xsltTransformError(NULL, ctxt->style, ctxt->elem, + "Failed to compile predicate\n"); + if (ctxt->style != NULL) + ctxt->style->errors++; + } + } + comp->nbStep++; + return (0); +} + +/** + * xsltSwapTopCompMatch: + * @comp: the compiled match expression + * + * reverse the two top steps. + */ +static void +xsltSwapTopCompMatch(xsltCompMatchPtr comp) { + int i; + int j = comp->nbStep - 1; + + if (j > 0) { + register xmlChar *tmp; + register xsltOp op; + register xmlXPathCompExprPtr expr; + i = j - 1; + tmp = comp->steps[i].value; + comp->steps[i].value = comp->steps[j].value; + comp->steps[j].value = tmp; + tmp = comp->steps[i].value2; + comp->steps[i].value2 = comp->steps[j].value2; + comp->steps[j].value2 = tmp; + op = comp->steps[i].op; + comp->steps[i].op = comp->steps[j].op; + comp->steps[j].op = op; + expr = comp->steps[i].comp; + comp->steps[i].comp = comp->steps[j].comp; + comp->steps[j].comp = expr; + } +} + +/** + * xsltReverseCompMatch: + * @comp: the compiled match expression + * + * reverse all the stack of expressions + */ +static void +xsltReverseCompMatch(xsltCompMatchPtr comp) { + int i = 0; + int j = comp->nbStep - 1; + + while (j > i) { + register xmlChar *tmp; + register xsltOp op; + register xmlXPathCompExprPtr expr; + tmp = comp->steps[i].value; + comp->steps[i].value = comp->steps[j].value; + comp->steps[j].value = tmp; + tmp = comp->steps[i].value2; + comp->steps[i].value2 = comp->steps[j].value2; + comp->steps[j].value2 = tmp; + op = comp->steps[i].op; + comp->steps[i].op = comp->steps[j].op; + comp->steps[j].op = op; + expr = comp->steps[i].comp; + comp->steps[i].comp = comp->steps[j].comp; + comp->steps[j].comp = expr; + j--; + i++; + } + comp->steps[comp->nbStep++].op = XSLT_OP_END; + /* + * detect consecutive XSLT_OP_PREDICATE indicating a direct + * matching should be done. + */ + for (i = 0;i < comp->nbStep - 1;i++) { + if ((comp->steps[i].op == XSLT_OP_PREDICATE) && + (comp->steps[i + 1].op == XSLT_OP_PREDICATE)) { + + comp->direct = 1; + if (comp->pattern[0] != '/') { + xmlChar *query; + + query = xmlStrdup((const xmlChar *)"//"); + query = xmlStrcat(query, comp->pattern); + + xmlFree((xmlChar *) comp->pattern); + comp->pattern = query; + } + break; + } + } +} + +/************************************************************************ + * * + * The interpreter for the precompiled patterns * + * * + ************************************************************************/ + +static int +xsltPatPushState(xsltStepStates *states, int step, xmlNodePtr node) { + if ((states->states == NULL) || (states->maxstates <= 0)) { + states->maxstates = 4; + states->nbstates = 0; + states->states = xmlMalloc(4 * sizeof(xsltStepState)); + } + else if (states->maxstates <= states->nbstates) { + xsltStepState *tmp; + + tmp = (xsltStepStatePtr) xmlRealloc(states->states, + 2 * states->maxstates * sizeof(xsltStepState)); + if (tmp == NULL) + return(-1); + states->states = tmp; + states->maxstates *= 2; + } + states->states[states->nbstates].step = step; + states->states[states->nbstates++].node = node; +#if 0 + fprintf(stderr, "Push: %d, %s\n", step, node->name); +#endif + return(0); +} + +/** + * xsltTestCompMatchDirect: + * @ctxt: a XSLT process context + * @comp: the precompiled pattern + * @node: a node + * + * Test whether the node matches the pattern, do a direct evalutation + * and not a step by step evaluation. + * + * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure + */ +static int +xsltTestCompMatchDirect(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp, + xmlNodePtr node) { + xsltStepOpPtr sel = NULL; + xmlDocPtr prevdoc; + xmlDocPtr doc; + xmlXPathObjectPtr list; + int ix, j; + int nocache = 0; + int isRVT; + + doc = node->doc; + if (XSLT_IS_RES_TREE_FRAG(doc)) + isRVT = 1; + else + isRVT = 0; + sel = &comp->steps[0]; /* store extra in first step arbitrarily */ + + prevdoc = (xmlDocPtr) + XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); + ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival); + list = (xmlXPathObjectPtr) + XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra); + + if ((list == NULL) || (prevdoc != doc)) { + xmlXPathObjectPtr newlist; + xmlNodePtr parent = node->parent; + xmlDocPtr olddoc; + xmlNodePtr oldnode; + + oldnode = ctxt->xpathCtxt->node; + olddoc = ctxt->xpathCtxt->doc; + ctxt->xpathCtxt->node = node; + ctxt->xpathCtxt->doc = doc; + newlist = xmlXPathEval(comp->pattern, ctxt->xpathCtxt); + ctxt->xpathCtxt->node = oldnode; + ctxt->xpathCtxt->doc = olddoc; + if (newlist == NULL) + return(-1); + if (newlist->type != XPATH_NODESET) { + xmlXPathFreeObject(newlist); + return(-1); + } + ix = 0; + + if ((parent == NULL) || (node->doc == NULL) || isRVT) + nocache = 1; + + if (nocache == 0) { + if (list != NULL) + xmlXPathFreeObject(list); + list = newlist; + + XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) = + (void *) list; + XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = + (void *) doc; + XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = + 0; + XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) = + (xmlFreeFunc) xmlXPathFreeObject; + } else + list = newlist; + } + if ((list->nodesetval == NULL) || + (list->nodesetval->nodeNr <= 0)) { + if (nocache == 1) + xmlXPathFreeObject(list); + return(0); + } + /* TODO: store the index and use it for the scan */ + if (ix == 0) { + for (j = 0;j < list->nodesetval->nodeNr;j++) { + if (list->nodesetval->nodeTab[j] == node) { + if (nocache == 1) + xmlXPathFreeObject(list); + return(1); + } + } + } else { + } + if (nocache == 1) + xmlXPathFreeObject(list); + return(0); +} + +/** + * xsltTestCompMatch: + * @ctxt: a XSLT process context + * @comp: the precompiled pattern + * @node: a node + * @mode: the mode name or NULL + * @modeURI: the mode URI or NULL + * + * Test whether the node matches the pattern + * + * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure + */ +static int +xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp, + xmlNodePtr node, const xmlChar *mode, + const xmlChar *modeURI) { + int i; + xsltStepOpPtr step, sel = NULL; + xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */ + + if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) { + xsltTransformError(ctxt, NULL, node, + "xsltTestCompMatch: null arg\n"); + return(-1); + } + if (mode != NULL) { + if (comp->mode == NULL) + return(0); + /* + * both mode strings must be interned on the stylesheet dictionary + */ + if (comp->mode != mode) + return(0); + } else { + if (comp->mode != NULL) + return(0); + } + if (modeURI != NULL) { + if (comp->modeURI == NULL) + return(0); + /* + * both modeURI strings must be interned on the stylesheet dictionary + */ + if (comp->modeURI != modeURI) + return(0); + } else { + if (comp->modeURI != NULL) + return(0); + } + + i = 0; +restart: + for (;i < comp->nbStep;i++) { + step = &comp->steps[i]; + if (step->op != XSLT_OP_PREDICATE) + sel = step; + switch (step->op) { + case XSLT_OP_END: + goto found; + case XSLT_OP_ROOT: + if ((node->type == XML_DOCUMENT_NODE) || +#ifdef LIBXML_DOCB_ENABLED + (node->type == XML_DOCB_DOCUMENT_NODE) || +#endif + (node->type == XML_HTML_DOCUMENT_NODE)) + continue; + if ((node->type == XML_ELEMENT_NODE) && (node->name[0] == ' ')) + continue; + goto rollback; + case XSLT_OP_ELEM: + if (node->type != XML_ELEMENT_NODE) + goto rollback; + if (step->value == NULL) + continue; + if (step->value[0] != node->name[0]) + goto rollback; + if (!xmlStrEqual(step->value, node->name)) + goto rollback; + + /* Namespace test */ + if (node->ns == NULL) { + if (step->value2 != NULL) + goto rollback; + } else if (node->ns->href != NULL) { + if (step->value2 == NULL) + goto rollback; + if (!xmlStrEqual(step->value2, node->ns->href)) + goto rollback; + } + continue; + case XSLT_OP_CHILD: { + xmlNodePtr lst; + + if ((node->type != XML_ELEMENT_NODE) && + (node->type != XML_DOCUMENT_NODE) && +#ifdef LIBXML_DOCB_ENABLED + (node->type != XML_DOCB_DOCUMENT_NODE) && +#endif + (node->type != XML_HTML_DOCUMENT_NODE)) + goto rollback; + + lst = node->children; + + if (step->value != NULL) { + while (lst != NULL) { + if ((lst->type == XML_ELEMENT_NODE) && + (step->value[0] == lst->name[0]) && + (xmlStrEqual(step->value, lst->name))) + break; + lst = lst->next; + } + if (lst != NULL) + continue; + } + goto rollback; + } + case XSLT_OP_ATTR: + if (node->type != XML_ATTRIBUTE_NODE) + goto rollback; + if (step->value != NULL) { + if (step->value[0] != node->name[0]) + goto rollback; + if (!xmlStrEqual(step->value, node->name)) + goto rollback; + } + /* Namespace test */ + if (node->ns == NULL) { + if (step->value2 != NULL) + goto rollback; + } else if (step->value2 != NULL) { + if (!xmlStrEqual(step->value2, node->ns->href)) + goto rollback; + } + continue; + case XSLT_OP_PARENT: + if ((node->type == XML_DOCUMENT_NODE) || + (node->type == XML_HTML_DOCUMENT_NODE) || +#ifdef LIBXML_DOCB_ENABLED + (node->type == XML_DOCB_DOCUMENT_NODE) || +#endif + (node->type == XML_NAMESPACE_DECL)) + goto rollback; + node = node->parent; + if (node == NULL) + goto rollback; + if (step->value == NULL) + continue; + if (step->value[0] != node->name[0]) + goto rollback; + if (!xmlStrEqual(step->value, node->name)) + goto rollback; + /* Namespace test */ + if (node->ns == NULL) { + if (step->value2 != NULL) + goto rollback; + } else if (node->ns->href != NULL) { + if (step->value2 == NULL) + goto rollback; + if (!xmlStrEqual(step->value2, node->ns->href)) + goto rollback; + } + continue; + case XSLT_OP_ANCESTOR: + /* TODO: implement coalescing of ANCESTOR/NODE ops */ + if (step->value == NULL) { + step = &comp->steps[i+1]; + if (step->op == XSLT_OP_ROOT) + goto found; + /* added NS, ID and KEY as a result of bug 168208 */ + if ((step->op != XSLT_OP_ELEM) && + (step->op != XSLT_OP_ALL) && + (step->op != XSLT_OP_NS) && + (step->op != XSLT_OP_ID) && + (step->op != XSLT_OP_KEY)) + goto rollback; + } + if (node == NULL) + goto rollback; + if ((node->type == XML_DOCUMENT_NODE) || + (node->type == XML_HTML_DOCUMENT_NODE) || +#ifdef LIBXML_DOCB_ENABLED + (node->type == XML_DOCB_DOCUMENT_NODE) || +#endif + (node->type == XML_NAMESPACE_DECL)) + goto rollback; + node = node->parent; + if ((step->op != XSLT_OP_ELEM) && step->op != XSLT_OP_ALL) { + xsltPatPushState(&states, i, node); + continue; + } + i++; + if (step->value == NULL) { + xsltPatPushState(&states, i - 1, node); + continue; + } + while (node != NULL) { + if ((node->type == XML_ELEMENT_NODE) && + (step->value[0] == node->name[0]) && + (xmlStrEqual(step->value, node->name))) { + /* Namespace test */ + if (node->ns == NULL) { + if (step->value2 == NULL) + break; + } else if (node->ns->href != NULL) { + if ((step->value2 != NULL) && + (xmlStrEqual(step->value2, node->ns->href))) + break; + } + } + node = node->parent; + } + if (node == NULL) + goto rollback; + xsltPatPushState(&states, i - 1, node); + continue; + case XSLT_OP_ID: { + /* TODO Handle IDs decently, must be done differently */ + xmlAttrPtr id; + + if (node->type != XML_ELEMENT_NODE) + goto rollback; + + id = xmlGetID(node->doc, step->value); + if ((id == NULL) || (id->parent != node)) + goto rollback; + break; + } + case XSLT_OP_KEY: { + xmlNodeSetPtr list; + int indx; + + list = xsltGetKey(ctxt, step->value, + step->value3, step->value2); + if (list == NULL) + goto rollback; + for (indx = 0;indx < list->nodeNr;indx++) + if (list->nodeTab[indx] == node) + break; + if (indx >= list->nodeNr) + goto rollback; + break; + } + case XSLT_OP_NS: + if (node->type != XML_ELEMENT_NODE) + goto rollback; + if (node->ns == NULL) { + if (step->value != NULL) + goto rollback; + } else if (node->ns->href != NULL) { + if (step->value == NULL) + goto rollback; + if (!xmlStrEqual(step->value, node->ns->href)) + goto rollback; + } + break; + case XSLT_OP_ALL: + if (node->type != XML_ELEMENT_NODE) + goto rollback; + break; + case XSLT_OP_PREDICATE: { + xmlNodePtr oldNode; + xmlDocPtr doc; + int oldCS, oldCP; + int pos = 0, len = 0; + int isRVT; + + /* + * when there is cascading XSLT_OP_PREDICATE, then use a + * direct computation approach. It's not done directly + * at the beginning of the routine to filter out as much + * as possible this costly computation. + */ + if (comp->direct) { + if (states.states != NULL) { + /* Free the rollback states */ + xmlFree(states.states); + } + return(xsltTestCompMatchDirect(ctxt, comp, node)); + } + + doc = node->doc; + if (XSLT_IS_RES_TREE_FRAG(doc)) + isRVT = 1; + else + isRVT = 0; + + /* + * Depending on the last selection, one may need to + * recompute contextSize and proximityPosition. + */ + oldCS = ctxt->xpathCtxt->contextSize; + oldCP = ctxt->xpathCtxt->proximityPosition; + if ((sel != NULL) && + (sel->op == XSLT_OP_ELEM) && + (sel->value != NULL) && + (node->type == XML_ELEMENT_NODE) && + (node->parent != NULL)) { + xmlNodePtr previous; + int ix, nocache = 0; + + previous = (xmlNodePtr) + XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); + ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival); + if ((previous != NULL) && + (previous->parent == node->parent)) { + /* + * just walk back to adjust the index + */ + int indx = 0; + xmlNodePtr sibling = node; + + while (sibling != NULL) { + if (sibling == previous) + break; + if ((previous->type == XML_ELEMENT_NODE) && + (previous->name != NULL) && + (sibling->name != NULL) && + (previous->name[0] == sibling->name[0]) && + (xmlStrEqual(previous->name, sibling->name))) + { + if ((sel->value2 == NULL) || + ((sibling->ns != NULL) && + (xmlStrEqual(sel->value2, + sibling->ns->href)))) + indx++; + } + sibling = sibling->prev; + } + if (sibling == NULL) { + /* hum going backward in document order ... */ + indx = 0; + sibling = node; + while (sibling != NULL) { + if (sibling == previous) + break; + if ((previous->type == XML_ELEMENT_NODE) && + (previous->name != NULL) && + (sibling->name != NULL) && + (previous->name[0] == sibling->name[0]) && + (xmlStrEqual(previous->name, sibling->name))) + { + if ((sel->value2 == NULL) || + ((sibling->ns != NULL) && + (xmlStrEqual(sel->value2, + sibling->ns->href)))) + { + indx--; + } + } + sibling = sibling->next; + } + } + if (sibling != NULL) { + pos = ix + indx; + /* + * If the node is in a Value Tree we need to + * save len, but cannot cache the node! + * (bugs 153137 and 158840) + */ + if (node->doc != NULL) { + len = XSLT_RUNTIME_EXTRA(ctxt, + sel->lenExtra, ival); + if (!isRVT) { + XSLT_RUNTIME_EXTRA(ctxt, + sel->previousExtra, ptr) = node; + XSLT_RUNTIME_EXTRA(ctxt, + sel->indexExtra, ival) = pos; + } + } + ix = pos; + } else + pos = 0; + } else { + /* + * recompute the index + */ + xmlNodePtr siblings = node->parent->children; + xmlNodePtr parent = node->parent; + + while (siblings != NULL) { + if (siblings->type == XML_ELEMENT_NODE) { + if (siblings == node) { + len++; + pos = len; + } else if ((node->name != NULL) && + (siblings->name != NULL) && + (node->name[0] == siblings->name[0]) && + (xmlStrEqual(node->name, siblings->name))) { + if ((sel->value2 == NULL) || + ((siblings->ns != NULL) && + (xmlStrEqual(sel->value2, + siblings->ns->href)))) + len++; + } + } + siblings = siblings->next; + } + if ((parent == NULL) || (node->doc == NULL)) + nocache = 1; + else { + while (parent->parent != NULL) + parent = parent->parent; + if (((parent->type != XML_DOCUMENT_NODE) && + (parent->type != XML_HTML_DOCUMENT_NODE)) || + (parent != (xmlNodePtr) node->doc)) + nocache = 1; + } + } + if (pos != 0) { + ctxt->xpathCtxt->contextSize = len; + ctxt->xpathCtxt->proximityPosition = pos; + /* + * If the node is in a Value Tree we cannot + * cache it ! + */ + if ((!isRVT) && (node->doc != NULL) && + (nocache == 0)) { + XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = + node; + XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = + pos; + XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = + len; + } + } + } else if ((sel != NULL) && (sel->op == XSLT_OP_ALL) && + (node->type == XML_ELEMENT_NODE)) { + xmlNodePtr previous; + int ix, nocache = 0; + + previous = (xmlNodePtr) + XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); + ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival); + if ((previous != NULL) && + (previous->parent == node->parent)) { + /* + * just walk back to adjust the index + */ + int indx = 0; + xmlNodePtr sibling = node; + + while (sibling != NULL) { + if (sibling == previous) + break; + if (sibling->type == XML_ELEMENT_NODE) + indx++; + sibling = sibling->prev; + } + if (sibling == NULL) { + /* hum going backward in document order ... */ + indx = 0; + sibling = node; + while (sibling != NULL) { + if (sibling == previous) + break; + if (sibling->type == XML_ELEMENT_NODE) + indx--; + sibling = sibling->next; + } + } + if (sibling != NULL) { + pos = ix + indx; + /* + * If the node is in a Value Tree we cannot + * cache it ! + */ + if ((node->doc != NULL) && !isRVT) { + len = XSLT_RUNTIME_EXTRA(ctxt, + sel->lenExtra, ival); + XSLT_RUNTIME_EXTRA(ctxt, + sel->previousExtra, ptr) = node; + XSLT_RUNTIME_EXTRA(ctxt, + sel->indexExtra, ival) = pos; + } + } else + pos = 0; + } else { + /* + * recompute the index + */ + xmlNodePtr siblings = node->parent->children; + xmlNodePtr parent = node->parent; + + while (siblings != NULL) { + if (siblings->type == XML_ELEMENT_NODE) { + len++; + if (siblings == node) { + pos = len; + } + } + siblings = siblings->next; + } + if ((parent == NULL) || (node->doc == NULL)) + nocache = 1; + else { + while (parent->parent != NULL) + parent = parent->parent; + if (((parent->type != XML_DOCUMENT_NODE) && + (parent->type != XML_HTML_DOCUMENT_NODE)) || + (parent != (xmlNodePtr) node->doc)) + nocache = 1; + } + } + if (pos != 0) { + ctxt->xpathCtxt->contextSize = len; + ctxt->xpathCtxt->proximityPosition = pos; + /* + * If the node is in a Value Tree we cannot + * cache it ! + */ + if ((node->doc != NULL) && (nocache == 0) && !isRVT) { + XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = + node; + XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = + pos; + XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = + len; + } + } + } + oldNode = ctxt->node; + ctxt->node = node; + + if (step->value == NULL) + goto wrong_index; + if (step->comp == NULL) + goto wrong_index; + + if (!xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList, + comp->nsNr)) + goto wrong_index; + + if (pos != 0) { + ctxt->xpathCtxt->contextSize = oldCS; + ctxt->xpathCtxt->proximityPosition = oldCP; + } + ctxt->node = oldNode; + break; +wrong_index: + if (pos != 0) { + ctxt->xpathCtxt->contextSize = oldCS; + ctxt->xpathCtxt->proximityPosition = oldCP; + } + ctxt->node = oldNode; + goto rollback; + } + case XSLT_OP_PI: + if (node->type != XML_PI_NODE) + goto rollback; + if (step->value != NULL) { + if (!xmlStrEqual(step->value, node->name)) + goto rollback; + } + break; + case XSLT_OP_COMMENT: + if (node->type != XML_COMMENT_NODE) + goto rollback; + break; + case XSLT_OP_TEXT: + if ((node->type != XML_TEXT_NODE) && + (node->type != XML_CDATA_SECTION_NODE)) + goto rollback; + break; + case XSLT_OP_NODE: + switch (node->type) { + case XML_ELEMENT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_TEXT_NODE: + break; + default: + goto rollback; + } + break; + } + } +found: + if (states.states != NULL) { + /* Free the rollback states */ + xmlFree(states.states); + } + return(1); +rollback: + /* got an error try to rollback */ + if (states.states == NULL) + return(0); + if (states.nbstates <= 0) { + xmlFree(states.states); + return(0); + } + states.nbstates--; + i = states.states[states.nbstates].step; + node = states.states[states.nbstates].node; +#if 0 + fprintf(stderr, "Pop: %d, %s\n", i, node->name); +#endif + goto restart; +} + +/** + * xsltTestCompMatchList: + * @ctxt: a XSLT process context + * @node: a node + * @comp: the precompiled pattern list + * + * Test whether the node matches one of the patterns in the list + * + * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure + */ +int +xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node, + xsltCompMatchPtr comp) { + int ret; + + if ((ctxt == NULL) || (node == NULL)) + return(-1); + while (comp != NULL) { + ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL); + if (ret == 1) + return(1); + comp = comp->next; + } + return(0); +} + +/************************************************************************ + * * + * Dedicated parser for templates * + * * + ************************************************************************/ + +#define CUR (*ctxt->cur) +#define SKIP(val) ctxt->cur += (val) +#define NXT(val) ctxt->cur[(val)] +#define CUR_PTR ctxt->cur + +#define SKIP_BLANKS \ + while (IS_BLANK_CH(CUR)) NEXT + +#define CURRENT (*ctxt->cur) +#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) + + +#define PUSH(op, val, val2, novar) \ + if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto error; + +#define SWAP() \ + xsltSwapTopCompMatch(ctxt->comp); + +#define XSLT_ERROR(X) \ + { xsltError(ctxt, __FILE__, __LINE__, X); \ + ctxt->error = (X); return; } + +#define XSLT_ERROR0(X) \ + { xsltError(ctxt, __FILE__, __LINE__, X); \ + ctxt->error = (X); return(0); } + +/** + * xsltScanLiteral: + * @ctxt: the XPath Parser context + * + * Parse an XPath Litteral: + * + * [29] Literal ::= '"' [^"]* '"' + * | "'" [^']* "'" + * + * Returns the Literal parsed or NULL + */ + +static xmlChar * +xsltScanLiteral(xsltParserContextPtr ctxt) { + const xmlChar *q, *cur; + xmlChar *ret = NULL; + int val, len; + + SKIP_BLANKS; + if (CUR == '"') { + NEXT; + cur = q = CUR_PTR; + val = xmlStringCurrentChar(NULL, cur, &len); + while ((IS_CHAR(val)) && (val != '"')) { + cur += len; + val = xmlStringCurrentChar(NULL, cur, &len); + } + if (!IS_CHAR(val)) { + ctxt->error = 1; + return(NULL); + } else { + ret = xmlStrndup(q, cur - q); + } + cur += len; + CUR_PTR = cur; + } else if (CUR == '\'') { + NEXT; + cur = q = CUR_PTR; + val = xmlStringCurrentChar(NULL, cur, &len); + while ((IS_CHAR(val)) && (val != '\'')) { + cur += len; + val = xmlStringCurrentChar(NULL, cur, &len); + } + if (!IS_CHAR(val)) { + ctxt->error = 1; + return(NULL); + } else { + ret = xmlStrndup(q, cur - q); + } + cur += len; + CUR_PTR = cur; + } else { + /* XP_ERROR(XPATH_START_LITERAL_ERROR); */ + ctxt->error = 1; + return(NULL); + } + return(ret); +} + +/** + * xsltScanName: + * @ctxt: the XPath Parser context + * + * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | + * CombiningChar | Extender + * + * [5] Name ::= (Letter | '_' | ':') (NameChar)* + * + * [6] Names ::= Name (S Name)* + * + * Returns the Name parsed or NULL + */ + +static xmlChar * +xsltScanName(xsltParserContextPtr ctxt) { + const xmlChar *q, *cur; + xmlChar *ret = NULL; + int val, len; + + SKIP_BLANKS; + + cur = q = CUR_PTR; + val = xmlStringCurrentChar(NULL, cur, &len); + if (!IS_LETTER(val) && (val != '_') && (val != ':')) + return(NULL); + + while ((IS_LETTER(val)) || (IS_DIGIT(val)) || + (val == '.') || (val == '-') || + (val == '_') || + (IS_COMBINING(val)) || + (IS_EXTENDER(val))) { + cur += len; + val = xmlStringCurrentChar(NULL, cur, &len); + } + ret = xmlStrndup(q, cur - q); + CUR_PTR = cur; + return(ret); +} + +/** + * xsltScanNCName: + * @ctxt: the XPath Parser context + * + * Parses a non qualified name + * + * Returns the Name parsed or NULL + */ + +static xmlChar * +xsltScanNCName(xsltParserContextPtr ctxt) { + const xmlChar *q, *cur; + xmlChar *ret = NULL; + int val, len; + + SKIP_BLANKS; + + cur = q = CUR_PTR; + val = xmlStringCurrentChar(NULL, cur, &len); + if (!IS_LETTER(val) && (val != '_')) + return(NULL); + + while ((IS_LETTER(val)) || (IS_DIGIT(val)) || + (val == '.') || (val == '-') || + (val == '_') || + (IS_COMBINING(val)) || + (IS_EXTENDER(val))) { + cur += len; + val = xmlStringCurrentChar(NULL, cur, &len); + } + ret = xmlStrndup(q, cur - q); + CUR_PTR = cur; + return(ret); +} + +/** + * xsltScanQName: + * @ctxt: the XPath Parser context + * @prefix: the place to store the prefix + * + * Parse a qualified name + * + * Returns the Name parsed or NULL + */ + +static xmlChar * +xsltScanQName(xsltParserContextPtr ctxt, xmlChar **prefix) { + xmlChar *ret = NULL; + + *prefix = NULL; + ret = xsltScanNCName(ctxt); + if (CUR == ':') { + *prefix = ret; + NEXT; + ret = xsltScanNCName(ctxt); + } + return(ret); +} + +/* + * xsltCompileIdKeyPattern: + * @ctxt: the compilation context + * @name: a preparsed name + * @aid: whether id/key are allowed there + * @novar: flag to prohibit xslt var + * + * Compile the XSLT LocationIdKeyPattern + * [3] IdKeyPattern ::= 'id' '(' Literal ')' + * | 'key' '(' Literal ',' Literal ')' + * + * also handle NodeType and PI from: + * + * [7] NodeTest ::= NameTest + * | NodeType '(' ')' + * | 'processing-instruction' '(' Literal ')' + */ +static void +xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, + int aid, int novar) { + xmlChar *lit = NULL; + xmlChar *lit2 = NULL; + + if (CUR != '(') { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : ( expected\n"); + ctxt->error = 1; + return; + } + if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) { + NEXT; + SKIP_BLANKS; + lit = xsltScanLiteral(ctxt); + if (ctxt->error) + return; + SKIP_BLANKS; + if (CUR != ')') { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : ) expected\n"); + ctxt->error = 1; + return; + } + NEXT; + PUSH(XSLT_OP_ID, lit, NULL, novar); + } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) { + NEXT; + SKIP_BLANKS; + lit = xsltScanLiteral(ctxt); + if (ctxt->error) + return; + SKIP_BLANKS; + if (CUR != ',') { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : , expected\n"); + ctxt->error = 1; + return; + } + NEXT; + SKIP_BLANKS; + lit2 = xsltScanLiteral(ctxt); + if (ctxt->error) + return; + SKIP_BLANKS; + if (CUR != ')') { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : ) expected\n"); + ctxt->error = 1; + return; + } + NEXT; + /* URGENT TODO: support namespace in keys */ + PUSH(XSLT_OP_KEY, lit, lit2, novar); + } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) { + NEXT; + SKIP_BLANKS; + if (CUR != ')') { + lit = xsltScanLiteral(ctxt); + if (ctxt->error) + return; + SKIP_BLANKS; + if (CUR != ')') { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : ) expected\n"); + ctxt->error = 1; + return; + } + } + NEXT; + PUSH(XSLT_OP_PI, lit, NULL, novar); + } else if (xmlStrEqual(name, (const xmlChar *)"text")) { + NEXT; + SKIP_BLANKS; + if (CUR != ')') { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : ) expected\n"); + ctxt->error = 1; + return; + } + NEXT; + PUSH(XSLT_OP_TEXT, NULL, NULL, novar); + } else if (xmlStrEqual(name, (const xmlChar *)"comment")) { + NEXT; + SKIP_BLANKS; + if (CUR != ')') { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : ) expected\n"); + ctxt->error = 1; + return; + } + NEXT; + PUSH(XSLT_OP_COMMENT, NULL, NULL, novar); + } else if (xmlStrEqual(name, (const xmlChar *)"node")) { + NEXT; + SKIP_BLANKS; + if (CUR != ')') { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : ) expected\n"); + ctxt->error = 1; + return; + } + NEXT; + PUSH(XSLT_OP_NODE, NULL, NULL, novar); + } else if (aid) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n"); + ctxt->error = 1; + return; + } else { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : node type\n"); + ctxt->error = 1; + return; + } +error: + if (name != NULL) + xmlFree(name); +} + +/** + * xsltCompileStepPattern: + * @ctxt: the compilation context + * @token: a posible precompiled name + * @novar: flag to prohibit xslt variables from pattern + * + * Compile the XSLT StepPattern and generates a precompiled + * form suitable for fast matching. + * + * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate* + * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier + * | ('child' | 'attribute') '::' + * from XPath + * [7] NodeTest ::= NameTest + * | NodeType '(' ')' + * | 'processing-instruction' '(' Literal ')' + * [8] Predicate ::= '[' PredicateExpr ']' + * [9] PredicateExpr ::= Expr + * [13] AbbreviatedAxisSpecifier ::= '@'? + * [37] NameTest ::= '*' | NCName ':' '*' | QName + */ + +static void +xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) { + xmlChar *name = NULL; + const xmlChar *URI = NULL; + xmlChar *URL = NULL; + int level; + + SKIP_BLANKS; + if ((token == NULL) && (CUR == '@')) { + xmlChar *prefix = NULL; + + NEXT; + if (CUR == '*') { + NEXT; + PUSH(XSLT_OP_ATTR, NULL, NULL, novar); + goto parse_predicate; + } + token = xsltScanQName(ctxt, &prefix); + if (prefix != NULL) { + xmlNsPtr ns; + + ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix); + if (ns == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileStepPattern : no namespace bound to prefix %s\n", + prefix); + } else { + URL = xmlStrdup(ns->href); + } + xmlFree(prefix); + } + if (token == NULL) { + if (CUR == '*') { + NEXT; + PUSH(XSLT_OP_ATTR, NULL, URL, novar); + return; + } + xsltTransformError(NULL, NULL, NULL, + "xsltCompileStepPattern : Name expected\n"); + ctxt->error = 1; + goto error; + } + PUSH(XSLT_OP_ATTR, token, URL, novar); + goto parse_predicate; + } + if (token == NULL) + token = xsltScanName(ctxt); + if (token == NULL) { + if (CUR == '*') { + NEXT; + PUSH(XSLT_OP_ALL, token, NULL, novar); + goto parse_predicate; + } else { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileStepPattern : Name expected\n"); + ctxt->error = 1; + goto error; + } + } + + + SKIP_BLANKS; + if (CUR == '(') { + xsltCompileIdKeyPattern(ctxt, token, 0, novar); + if (ctxt->error) + goto error; + } else if (CUR == ':') { + NEXT; + if (CUR != ':') { + xmlChar *prefix = token; + xmlNsPtr ns; + + /* + * This is a namespace match + */ + token = xsltScanName(ctxt); + ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix); + if (ns == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileStepPattern : no namespace bound to prefix %s\n", + prefix); + ctxt->error = 1; + goto error; + } else { + URL = xmlStrdup(ns->href); + } + xmlFree(prefix); + if (token == NULL) { + if (CUR == '*') { + NEXT; + PUSH(XSLT_OP_NS, URL, NULL, novar); + } else { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileStepPattern : Name expected\n"); + ctxt->error = 1; + goto error; + } + } else { + PUSH(XSLT_OP_ELEM, token, URL, novar); + } + } else { + NEXT; + if (xmlStrEqual(token, (const xmlChar *) "child")) { + xmlFree(token); + token = xsltScanName(ctxt); + if (token == NULL) { + if (CUR == '*') { + NEXT; + PUSH(XSLT_OP_ALL, token, NULL, novar); + goto parse_predicate; + } else { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileStepPattern : QName expected\n"); + ctxt->error = 1; + goto error; + } + } + URI = xsltGetQNameURI(ctxt->elem, &token); + if (token == NULL) { + ctxt->error = 1; + goto error; + } else { + name = xmlStrdup(token); + if (URI != NULL) + URL = xmlStrdup(URI); + } + PUSH(XSLT_OP_CHILD, name, URL, novar); + } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) { + xmlFree(token); + token = xsltScanName(ctxt); + if (token == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileStepPattern : QName expected\n"); + ctxt->error = 1; + goto error; + } + URI = xsltGetQNameURI(ctxt->elem, &token); + if (token == NULL) { + ctxt->error = 1; + goto error; + } else { + name = xmlStrdup(token); + if (URI != NULL) + URL = xmlStrdup(URI); + } + PUSH(XSLT_OP_ATTR, name, URL, novar); + } else { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileStepPattern : 'child' or 'attribute' expected\n"); + ctxt->error = 1; + goto error; + } + xmlFree(token); + } + } else if (CUR == '*') { + NEXT; + PUSH(XSLT_OP_ALL, token, NULL, novar); + } else { + URI = xsltGetQNameURI(ctxt->elem, &token); + if (token == NULL) { + ctxt->error = 1; + goto error; + } + if (URI != NULL) + URL = xmlStrdup(URI); + PUSH(XSLT_OP_ELEM, token, URL, novar); + } +parse_predicate: + SKIP_BLANKS; + level = 0; + while (CUR == '[') { + const xmlChar *q; + xmlChar *ret = NULL; + + level++; + NEXT; + q = CUR_PTR; + while (CUR != 0) { + /* Skip over nested predicates */ + if (CUR == '[') + level++; + else if (CUR == ']') { + level--; + if (level == 0) + break; + } else if (CUR == '"') { + NEXT; + while ((CUR != 0) && (CUR != '"')) + NEXT; + } else if (CUR == '\'') { + NEXT; + while ((CUR != 0) && (CUR != '\'')) + NEXT; + } + NEXT; + } + if (CUR == 0) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileStepPattern : ']' expected\n"); + ctxt->error = 1; + return; + } + ret = xmlStrndup(q, CUR_PTR - q); + PUSH(XSLT_OP_PREDICATE, ret, NULL, novar); + /* push the predicate lower than local test */ + SWAP(); + NEXT; + SKIP_BLANKS; + } + return; +error: + if (token != NULL) + xmlFree(token); + if (name != NULL) + xmlFree(name); +} + +/** + * xsltCompileRelativePathPattern: + * @comp: the compilation context + * @token: a posible precompiled name + * @novar: flag to prohibit xslt variables + * + * Compile the XSLT RelativePathPattern and generates a precompiled + * form suitable for fast matching. + * + * [4] RelativePathPattern ::= StepPattern + * | RelativePathPattern '/' StepPattern + * | RelativePathPattern '//' StepPattern + */ +static void +xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) { + xsltCompileStepPattern(ctxt, token, novar); + if (ctxt->error) + goto error; + SKIP_BLANKS; + while ((CUR != 0) && (CUR != '|')) { + if ((CUR == '/') && (NXT(1) == '/')) { + PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar); + NEXT; + NEXT; + SKIP_BLANKS; + xsltCompileStepPattern(ctxt, NULL, novar); + } else if (CUR == '/') { + PUSH(XSLT_OP_PARENT, NULL, NULL, novar); + NEXT; + SKIP_BLANKS; + if ((CUR != 0) && (CUR != '|')) { + xsltCompileRelativePathPattern(ctxt, NULL, novar); + } + } else { + ctxt->error = 1; + } + if (ctxt->error) + goto error; + SKIP_BLANKS; + } +error: + return; +} + +/** + * xsltCompileLocationPathPattern: + * @ctxt: the compilation context + * @novar: flag to prohibit xslt variables + * + * Compile the XSLT LocationPathPattern and generates a precompiled + * form suitable for fast matching. + * + * [2] LocationPathPattern ::= '/' RelativePathPattern? + * | IdKeyPattern (('/' | '//') RelativePathPattern)? + * | '//'? RelativePathPattern + */ +static void +xsltCompileLocationPathPattern(xsltParserContextPtr ctxt, int novar) { + SKIP_BLANKS; + if ((CUR == '/') && (NXT(1) == '/')) { + /* + * since we reverse the query + * a leading // can be safely ignored + */ + NEXT; + NEXT; + ctxt->comp->priority = 0.5; /* '//' means not 0 priority */ + xsltCompileRelativePathPattern(ctxt, NULL, novar); + } else if (CUR == '/') { + /* + * We need to find root as the parent + */ + NEXT; + SKIP_BLANKS; + PUSH(XSLT_OP_ROOT, NULL, NULL, novar); + if ((CUR != 0) && (CUR != '|')) { + PUSH(XSLT_OP_PARENT, NULL, NULL, novar); + xsltCompileRelativePathPattern(ctxt, NULL, novar); + } + } else if (CUR == '*') { + xsltCompileRelativePathPattern(ctxt, NULL, novar); + } else if (CUR == '@') { + xsltCompileRelativePathPattern(ctxt, NULL, novar); + } else { + xmlChar *name; + name = xsltScanName(ctxt); + if (name == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileLocationPathPattern : Name expected\n"); + ctxt->error = 1; + return; + } + SKIP_BLANKS; + if ((CUR == '(') && !xmlXPathIsNodeType(name)) { + xsltCompileIdKeyPattern(ctxt, name, 1, novar); + if ((CUR == '/') && (NXT(1) == '/')) { + PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar); + NEXT; + NEXT; + SKIP_BLANKS; + xsltCompileRelativePathPattern(ctxt, NULL, novar); + } else if (CUR == '/') { + PUSH(XSLT_OP_PARENT, NULL, NULL, novar); + NEXT; + SKIP_BLANKS; + xsltCompileRelativePathPattern(ctxt, NULL, novar); + } + return; + } + xsltCompileRelativePathPattern(ctxt, name, novar); + } +error: + return; +} + +/** + * xsltCompilePatternInternal: + * @pattern: an XSLT pattern + * @doc: the containing document + * @node: the containing element + * @style: the stylesheet + * @runtime: the transformation context, if done at run-time + * @novar: flag to prohibit xslt variables + * + * Compile the XSLT pattern and generates a list of precompiled form suitable + * for fast matching. + * + * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern + * + * Returns the generated pattern list or NULL in case of failure + */ + +static xsltCompMatchPtr +xsltCompilePatternInternal(const xmlChar *pattern, xmlDocPtr doc, + xmlNodePtr node, xsltStylesheetPtr style, + xsltTransformContextPtr runtime, int novar) { + xsltParserContextPtr ctxt = NULL; + xsltCompMatchPtr element, first = NULL, previous = NULL; + int current, start, end, level, j; + + if (pattern == NULL) { + xsltTransformError(NULL, NULL, node, + "xsltCompilePattern : NULL pattern\n"); + return(NULL); + } + + ctxt = xsltNewParserContext(style, runtime); + if (ctxt == NULL) + return(NULL); + ctxt->doc = doc; + ctxt->elem = node; + current = end = 0; + while (pattern[current] != 0) { + start = current; + while (IS_BLANK_CH(pattern[current])) + current++; + end = current; + level = 0; + while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) { + if (pattern[end] == '[') + level++; + else if (pattern[end] == ']') + level--; + else if (pattern[end] == '\'') { + end++; + while ((pattern[end] != 0) && (pattern[end] != '\'')) + end++; + } else if (pattern[end] == '"') { + end++; + while ((pattern[end] != 0) && (pattern[end] != '"')) + end++; + } + end++; + } + if (current == end) { + xsltTransformError(NULL, NULL, node, + "xsltCompilePattern : NULL pattern\n"); + goto error; + } + element = xsltNewCompMatch(); + if (element == NULL) { + goto error; + } + if (first == NULL) + first = element; + else if (previous != NULL) + previous->next = element; + previous = element; + + ctxt->comp = element; + ctxt->base = xmlStrndup(&pattern[start], end - start); + if (ctxt->base == NULL) + goto error; + ctxt->cur = &(ctxt->base)[current - start]; + element->pattern = ctxt->base; + element->nsList = xmlGetNsList(doc, node); + j = 0; + if (element->nsList != NULL) { + while (element->nsList[j] != NULL) + j++; + } + element->nsNr = j; + + +#ifdef WITH_XSLT_DEBUG_PATTERN + xsltGenericDebug(xsltGenericDebugContext, + "xsltCompilePattern : parsing '%s'\n", + element->pattern); +#endif + /* + Preset default priority to be zero. + This may be changed by xsltCompileLocationPathPattern. + */ + element->priority = 0; + xsltCompileLocationPathPattern(ctxt, novar); + if (ctxt->error) { + xsltTransformError(NULL, style, node, + "xsltCompilePattern : failed to compile '%s'\n", + element->pattern); + if (style != NULL) style->errors++; + goto error; + } + + /* + * Reverse for faster interpretation. + */ + xsltReverseCompMatch(element); + + /* + * Set-up the priority + */ + if (element->priority == 0) { /* if not yet determined */ + if (((element->steps[0].op == XSLT_OP_ELEM) || + (element->steps[0].op == XSLT_OP_ATTR) || + (element->steps[0].op == XSLT_OP_PI)) && + (element->steps[0].value != NULL) && + (element->steps[1].op == XSLT_OP_END)) { + ; /* previously preset */ + } else if ((element->steps[0].op == XSLT_OP_ATTR) && + (element->steps[0].value2 != NULL) && + (element->steps[1].op == XSLT_OP_END)) { + element->priority = -0.25; + } else if ((element->steps[0].op == XSLT_OP_NS) && + (element->steps[0].value != NULL) && + (element->steps[1].op == XSLT_OP_END)) { + element->priority = -0.25; + } else if ((element->steps[0].op == XSLT_OP_ATTR) && + (element->steps[0].value == NULL) && + (element->steps[0].value2 == NULL) && + (element->steps[1].op == XSLT_OP_END)) { + element->priority = -0.5; + } else if (((element->steps[0].op == XSLT_OP_PI) || + (element->steps[0].op == XSLT_OP_TEXT) || + (element->steps[0].op == XSLT_OP_ALL) || + (element->steps[0].op == XSLT_OP_NODE) || + (element->steps[0].op == XSLT_OP_COMMENT)) && + (element->steps[1].op == XSLT_OP_END)) { + element->priority = -0.5; + } else { + element->priority = 0.5; + } + } +#ifdef WITH_XSLT_DEBUG_PATTERN + xsltGenericDebug(xsltGenericDebugContext, + "xsltCompilePattern : parsed %s, default priority %f\n", + element->pattern, element->priority); +#endif + if (pattern[end] == '|') + end++; + current = end; + } + if (end == 0) { + xsltTransformError(NULL, style, node, + "xsltCompilePattern : NULL pattern\n"); + if (style != NULL) style->errors++; + goto error; + } + + xsltFreeParserContext(ctxt); + return(first); + +error: + if (ctxt != NULL) + xsltFreeParserContext(ctxt); + if (first != NULL) + xsltFreeCompMatchList(first); + return(NULL); +} + +/** + * xsltCompilePattern: + * @pattern: an XSLT pattern + * @doc: the containing document + * @node: the containing element + * @style: the stylesheet + * @runtime: the transformation context, if done at run-time + * + * Compile the XSLT pattern and generates a list of precompiled form suitable + * for fast matching. + * + * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern + * + * Returns the generated pattern list or NULL in case of failure + */ + +xsltCompMatchPtr +xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc, + xmlNodePtr node, xsltStylesheetPtr style, + xsltTransformContextPtr runtime) { + return (xsltCompilePatternInternal(pattern, doc, node, style, runtime, 0)); +} + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +/** + * xsltAddTemplate: + * @style: an XSLT stylesheet + * @cur: an XSLT template + * @mode: the mode name or NULL + * @modeURI: the mode URI or NULL + * + * Register the XSLT pattern associated to @cur + * + * Returns -1 in case of error, 0 otherwise + */ +int +xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur, + const xmlChar *mode, const xmlChar *modeURI) { + xsltCompMatchPtr pat, list, *top = NULL, next; + const xmlChar *name = NULL; + float priority; /* the priority */ + + if ((style == NULL) || (cur == NULL) || (cur->match == NULL)) + return(-1); + + priority = cur->priority; + pat = xsltCompilePatternInternal(cur->match, style->doc, cur->elem, + style, NULL, 1); + while (pat) { + next = pat->next; + pat->next = NULL; + name = NULL; + + pat->template = cur; + if (mode != NULL) + pat->mode = xmlDictLookup(style->dict, mode, -1); + if (modeURI != NULL) + pat->modeURI = xmlDictLookup(style->dict, modeURI, -1); + if (priority != XSLT_PAT_NO_PRIORITY) + pat->priority = priority; + + /* + * insert it in the hash table list corresponding to its lookup name + */ + switch (pat->steps[0].op) { + case XSLT_OP_ATTR: + if (pat->steps[0].value != NULL) + name = pat->steps[0].value; + else + top = (xsltCompMatchPtr *) &(style->attrMatch); + break; + case XSLT_OP_CHILD: + case XSLT_OP_PARENT: + case XSLT_OP_ANCESTOR: + top = (xsltCompMatchPtr *) &(style->elemMatch); + break; + case XSLT_OP_ROOT: + top = (xsltCompMatchPtr *) &(style->rootMatch); + break; + case XSLT_OP_KEY: + top = (xsltCompMatchPtr *) &(style->keyMatch); + break; + case XSLT_OP_ID: + /* TODO optimize ID !!! */ + case XSLT_OP_NS: + case XSLT_OP_ALL: + top = (xsltCompMatchPtr *) &(style->elemMatch); + break; + case XSLT_OP_END: + case XSLT_OP_PREDICATE: + xsltTransformError(NULL, style, NULL, + "xsltAddTemplate: invalid compiled pattern\n"); + xsltFreeCompMatch(pat); + return(-1); + /* + * TODO: some flags at the top level about type based patterns + * would be faster than inclusion in the hash table. + */ + case XSLT_OP_PI: + if (pat->steps[0].value != NULL) + name = pat->steps[0].value; + else + top = (xsltCompMatchPtr *) &(style->piMatch); + break; + case XSLT_OP_COMMENT: + top = (xsltCompMatchPtr *) &(style->commentMatch); + break; + case XSLT_OP_TEXT: + top = (xsltCompMatchPtr *) &(style->textMatch); + break; + case XSLT_OP_ELEM: + case XSLT_OP_NODE: + if (pat->steps[0].value != NULL) + name = pat->steps[0].value; + else + top = (xsltCompMatchPtr *) &(style->elemMatch); + break; + } + if (name != NULL) { + if (style->templatesHash == NULL) { + style->templatesHash = xmlHashCreate(1024); + if (style->templatesHash == NULL) { + xsltFreeCompMatch(pat); + return(-1); + } + xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat); + } else { + list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash, + name, mode, modeURI); + if (list == NULL) { + xmlHashAddEntry3(style->templatesHash, name, + mode, modeURI, pat); + } else { + /* + * Note '<=' since one must choose among the matching + * template rules that are left, the one that occurs + * last in the stylesheet + */ + if (list->priority <= pat->priority) { + pat->next = list; + xmlHashUpdateEntry3(style->templatesHash, name, + mode, modeURI, pat, NULL); + } else { + while (list->next != NULL) { + if (list->next->priority <= pat->priority) + break; + list = list->next; + } + pat->next = list->next; + list->next = pat; + } + } + } + } else if (top != NULL) { + list = *top; + if (list == NULL) { + *top = pat; + pat->next = NULL; + } else if (list->priority <= pat->priority) { + pat->next = list; + *top = pat; + } else { + while (list->next != NULL) { + if (list->next->priority <= pat->priority) + break; + list = list->next; + } + pat->next = list->next; + list->next = pat; + } + } else { + xsltTransformError(NULL, style, NULL, + "xsltAddTemplate: invalid compiled pattern\n"); + xsltFreeCompMatch(pat); + return(-1); + } +#ifdef WITH_XSLT_DEBUG_PATTERN + if (mode) + xsltGenericDebug(xsltGenericDebugContext, + "added pattern : '%s' mode '%s' priority %f\n", + pat->pattern, pat->mode, pat->priority); + else + xsltGenericDebug(xsltGenericDebugContext, + "added pattern : '%s' priority %f\n", + pat->pattern, pat->priority); +#endif + + pat = next; + } + return(0); +} + +#ifdef XSLT_REFACTORED_KEYCOMP +static int +xsltComputeAllKeys(xsltTransformContextPtr ctxt, xmlNodePtr contextNode) +{ + xsltStylesheetPtr style, style2; + xsltKeyDefPtr keyd, keyd2; + xsltKeyTablePtr table; + + if ((ctxt == NULL) || (contextNode == NULL)) { + xsltTransformError(ctxt, NULL, ctxt->inst, + "Internal error in xsltComputeAllKeys(): " + "Bad arguments.\n"); + return(-1); + } + + if (ctxt->document == NULL) { + /* + * The document info will only be NULL if we have a RTF. + */ + if (contextNode->doc->_private != NULL) + goto doc_info_mismatch; + /* + * On-demand creation of the document info (needed for keys). + */ + ctxt->document = xsltNewDocument(ctxt, contextNode->doc); + if (ctxt->document == NULL) + return(-1); + } + + if (ctxt->document->nbKeysComputed == ctxt->nbKeys) + return(0); + /* + * TODO: This could be further optimized + */ + style = ctxt->style; + while (style) { + keyd = (xsltKeyDefPtr) style->keys; + while (keyd != NULL) { + /* + * Check if keys with this QName have been already + * computed. + */ + table = (xsltKeyTablePtr) ctxt->document->keys; + while (table) { + if (((keyd->nameURI != NULL) == (table->nameURI != NULL)) && + xmlStrEqual(keyd->name, table->name) && + xmlStrEqual(keyd->nameURI, table->nameURI)) + { + break; + } + table = table->next; + } + if (table == NULL) { + /* + * Keys with this QName have not been yet computed. + */ + style2 = ctxt->style; + while (style2 != NULL) { + keyd2 = (xsltKeyDefPtr) style2->keys; + while (keyd2 != NULL) { + if (((keyd2->nameURI != NULL) == + (keyd->nameURI != NULL)) && + xmlStrEqual(keyd2->name, keyd->name) && + xmlStrEqual(keyd2->nameURI, keyd->nameURI)) + { + xsltInitCtxtKey(ctxt, ctxt->document, keyd2); + if (ctxt->document->nbKeysComputed == ctxt->nbKeys) + return(0); + } + keyd2 = keyd2->next; + } + style2 = xsltNextImport(style2); + } + } + keyd = keyd->next; + } + style = xsltNextImport(style); + } + return(0); + +doc_info_mismatch: + xsltTransformError(ctxt, NULL, ctxt->inst, + "Internal error in xsltComputeAllKeys(): " + "The context's document info doesn't match the " + "document info of the current result tree.\n"); + ctxt->state = XSLT_STATE_STOPPED; + return(-1); +} +#endif + +/** + * xsltGetTemplate: + * @ctxt: a XSLT process context + * @node: the node being processed + * @style: the current style + * + * Finds the template applying to this node, if @style is non-NULL + * it means one needs to look for the next imported template in scope. + * + * Returns the xsltTemplatePtr or NULL if not found + */ +xsltTemplatePtr +xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, + xsltStylesheetPtr style) +{ + xsltStylesheetPtr curstyle; + xsltTemplatePtr ret = NULL; + const xmlChar *name = NULL; + xsltCompMatchPtr list = NULL; + float priority; + int keyed = 0; + + if ((ctxt == NULL) || (node == NULL)) + return(NULL); + + if (style == NULL) { + curstyle = ctxt->style; + } else { + curstyle = xsltNextImport(style); + } + + while ((curstyle != NULL) && (curstyle != style)) { + priority = XSLT_PAT_NO_PRIORITY; + /* TODO : handle IDs/keys here ! */ + if (curstyle->templatesHash != NULL) { + /* + * Use the top name as selector + */ + switch (node->type) { + case XML_ELEMENT_NODE: + if (node->name[0] == ' ') + break; + case XML_ATTRIBUTE_NODE: + case XML_PI_NODE: + name = node->name; + break; + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_COMMENT_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_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_NAMESPACE_DECL: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + break; + default: + return(NULL); + + } + } + if (name != NULL) { + /* + * find the list of applicable expressions based on the name + */ + list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash, + name, ctxt->mode, ctxt->modeURI); + } else + list = NULL; + while (list != NULL) { + if (xsltTestCompMatch(ctxt, list, node, + ctxt->mode, ctxt->modeURI)) { + ret = list->template; + priority = list->priority; + break; + } + list = list->next; + } + list = NULL; + + /* + * find alternate generic matches + */ + switch (node->type) { + case XML_ELEMENT_NODE: + if (node->name[0] == ' ') + list = curstyle->rootMatch; + else + list = curstyle->elemMatch; + if (node->psvi != NULL) keyed = 1; + break; + case XML_ATTRIBUTE_NODE: { + xmlAttrPtr attr; + + list = curstyle->attrMatch; + attr = (xmlAttrPtr) node; + if (attr->psvi != NULL) keyed = 1; + break; + } + case XML_PI_NODE: + list = curstyle->piMatch; + if (node->psvi != NULL) keyed = 1; + break; + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: { + xmlDocPtr doc; + + list = curstyle->rootMatch; + doc = (xmlDocPtr) node; + if (doc->psvi != NULL) keyed = 1; + break; + } + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + list = curstyle->textMatch; + if (node->psvi != NULL) keyed = 1; + break; + case XML_COMMENT_NODE: + list = curstyle->commentMatch; + if (node->psvi != NULL) keyed = 1; + break; + case XML_ENTITY_REF_NODE: + case XML_ENTITY_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_NAMESPACE_DECL: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + break; + default: + break; + } + while ((list != NULL) && + ((ret == NULL) || (list->priority > priority))) { + if (xsltTestCompMatch(ctxt, list, node, + ctxt->mode, ctxt->modeURI)) { + ret = list->template; + priority = list->priority; + break; + } + list = list->next; + } + /* + * Some of the tests for elements can also apply to documents + */ + if ((node->type == XML_DOCUMENT_NODE) || + (node->type == XML_HTML_DOCUMENT_NODE) || + (node->type == XML_TEXT_NODE)) { + list = curstyle->elemMatch; + while ((list != NULL) && + ((ret == NULL) || (list->priority > priority))) { + if (xsltTestCompMatch(ctxt, list, node, + ctxt->mode, ctxt->modeURI)) { + ret = list->template; + priority = list->priority; + break; + } + list = list->next; + } + } else if ((node->type == XML_PI_NODE) || + (node->type == XML_COMMENT_NODE)) { + list = curstyle->elemMatch; + while ((list != NULL) && + ((ret == NULL) || (list->priority > priority))) { + if (xsltTestCompMatch(ctxt, list, node, + ctxt->mode, ctxt->modeURI)) { + ret = list->template; + priority = list->priority; + break; + } + list = list->next; + } + } + +#ifdef XSLT_REFACTORED_KEYCOMP +keyed_match: +#endif + if (keyed) { + list = curstyle->keyMatch; + while ((list != NULL) && + ((ret == NULL) || (list->priority > priority))) { + if (xsltTestCompMatch(ctxt, list, node, + ctxt->mode, ctxt->modeURI)) { + ret = list->template; + priority = list->priority; + break; + } + list = list->next; + } + } +#ifdef XSLT_REFACTORED_KEYCOMP + else if (ctxt->hasTemplKeyPatterns && + ((ctxt->document == NULL) || + (ctxt->document->nbKeysComputed < ctxt->nbKeys))) + { + /* + * Compute all remaining keys for this document. + * + * REVISIT TODO: I think this could be further optimized. + */ + if (xsltComputeAllKeys(ctxt, node) == -1) + goto error; + + switch (node->type) { + case XML_ELEMENT_NODE: + if (node->psvi != NULL) keyed = 1; + break; + case XML_ATTRIBUTE_NODE: + if (((xmlAttrPtr) node)->psvi != NULL) keyed = 1; + break; + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_COMMENT_NODE: + case XML_PI_NODE: + if (node->psvi != NULL) keyed = 1; + break; + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + if (((xmlDocPtr) node)->psvi != NULL) keyed = 1; + break; + default: + break; + } + if (keyed) + goto keyed_match; + } +#endif /* XSLT_REFACTORED_KEYCOMP */ + if (ret != NULL) + return(ret); + + /* + * Cycle on next curstylesheet import. + */ + curstyle = xsltNextImport(curstyle); + } + +error: + return(NULL); +} + +/** + * xsltCleanupTemplates: + * @style: an XSLT stylesheet + * + * Cleanup the state of the templates used by the stylesheet and + * the ones it imports. + */ +void +xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED) { +} + +/** + * xsltFreeTemplateHashes: + * @style: an XSLT stylesheet + * + * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism + */ +void +xsltFreeTemplateHashes(xsltStylesheetPtr style) { + if (style->templatesHash != NULL) + xmlHashFree((xmlHashTablePtr) style->templatesHash, + (xmlHashDeallocator) xsltFreeCompMatchList); + if (style->rootMatch != NULL) + xsltFreeCompMatchList(style->rootMatch); + if (style->keyMatch != NULL) + xsltFreeCompMatchList(style->keyMatch); + if (style->elemMatch != NULL) + xsltFreeCompMatchList(style->elemMatch); + if (style->attrMatch != NULL) + xsltFreeCompMatchList(style->attrMatch); + if (style->parentMatch != NULL) + xsltFreeCompMatchList(style->parentMatch); + if (style->textMatch != NULL) + xsltFreeCompMatchList(style->textMatch); + if (style->piMatch != NULL) + xsltFreeCompMatchList(style->piMatch); + if (style->commentMatch != NULL) + xsltFreeCompMatchList(style->commentMatch); +} + diff --git a/libxslt/preproc.c b/libxslt/preproc.c index 344de8ce..f85543e2 100644 --- a/libxslt/preproc.c +++ b/libxslt/preproc.c @@ -1,2357 +1,2380 @@ -/* - * preproc.c: Preprocessing of style operations - * - * 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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include "xslt.h" -#include "xsltutils.h" -#include "xsltInternals.h" -#include "transform.h" -#include "templates.h" -#include "variables.h" -#include "numbersInternals.h" -#include "preproc.h" -#include "extra.h" -#include "imports.h" -#include "extensions.h" - -#ifdef WITH_XSLT_DEBUG -#define WITH_XSLT_DEBUG_PREPROC -#endif - -const xmlChar *xsltExtMarker = (const xmlChar *) "Extension Element"; - -/************************************************************************ - * * - * Grammar checks * - * * - ************************************************************************/ - -#ifdef XSLT_REFACTORED - /* - * Grammar checks are now performed in xslt.c. - */ -#else -/** - * xsltCheckTopLevelElement: - * @style: the XSLT stylesheet - * @inst: the XSLT instruction - * @err: raise an error or not - * - * Check that the instruction is instanciated as a top level element. - * - * Returns -1 in case of error, 0 if failed and 1 in case of success - */ -static int -xsltCheckTopLevelElement(xsltStylesheetPtr style, xmlNodePtr inst, int err) { - xmlNodePtr parent; - if ((style == NULL) || (inst == NULL) || (inst->ns == NULL)) - return(-1); - - parent = inst->parent; - if (parent == NULL) { - if (err) { - xsltTransformError(NULL, style, inst, - "internal problem: element has no parent\n"); - style->errors++; - } - return(0); - } - if ((parent->ns == NULL) || - ((parent->ns != inst->ns) && - (!xmlStrEqual(parent->ns->href, inst->ns->href))) || - ((!xmlStrEqual(parent->name, BAD_CAST "stylesheet")) && - (!xmlStrEqual(parent->name, BAD_CAST "transform")))) { - if (err) { - xsltTransformError(NULL, style, inst, - "element %s only allowed as child of stylesheet\n", - inst->name); - style->errors++; - } - return(0); - } - return(1); -} - -/** - * xsltCheckInstructionElement: - * @style: the XSLT stylesheet - * @inst: the XSLT instruction - * - * Check that the instruction is instanciated as an instruction element. - */ -static void -xsltCheckInstructionElement(xsltStylesheetPtr style, xmlNodePtr inst) { - xmlNodePtr parent; - int has_ext; - - if ((style == NULL) || (inst == NULL) || (inst->ns == NULL) || - (style->literal_result)) - return; - - has_ext = (style->extInfos != NULL); - - parent = inst->parent; - if (parent == NULL) { - xsltTransformError(NULL, style, inst, - "internal problem: element has no parent\n"); - style->errors++; - return; - } - while ((parent != NULL) && (parent->type != XML_DOCUMENT_NODE)) { - if (((parent->ns == inst->ns) || - ((parent->ns != NULL) && - (xmlStrEqual(parent->ns->href, inst->ns->href)))) && - ((xmlStrEqual(parent->name, BAD_CAST "template")) || - (xmlStrEqual(parent->name, BAD_CAST "param")) || - (xmlStrEqual(parent->name, BAD_CAST "attribute")) || - (xmlStrEqual(parent->name, BAD_CAST "variable")))) { - return; - } - - /* - * if we are within an extension element all bets are off - * about the semantic there e.g. xsl:param within func:function - */ - if ((has_ext) && (parent->ns != NULL) && - (xmlHashLookup(style->extInfos, parent->ns->href) != NULL)) - return; - - parent = parent->parent; - } - xsltTransformError(NULL, style, inst, - "element %s only allowed within a template, variable or param\n", - inst->name); - style->errors++; -} - -/** - * xsltCheckParentElement: - * @style: the XSLT stylesheet - * @inst: the XSLT instruction - * @allow1: allowed parent1 - * @allow2: allowed parent2 - * - * Check that the instruction is instanciated as the childre of one of the - * possible parents. - */ -static void -xsltCheckParentElement(xsltStylesheetPtr style, xmlNodePtr inst, - const xmlChar *allow1, const xmlChar *allow2) { - xmlNodePtr parent; - - if ((style == NULL) || (inst == NULL) || (inst->ns == NULL) || - (style->literal_result)) - return; - - parent = inst->parent; - if (parent == NULL) { - xsltTransformError(NULL, style, inst, - "internal problem: element has no parent\n"); - style->errors++; - return; - } - if (((parent->ns == inst->ns) || - ((parent->ns != NULL) && - (xmlStrEqual(parent->ns->href, inst->ns->href)))) && - ((xmlStrEqual(parent->name, allow1)) || - (xmlStrEqual(parent->name, allow2)))) { - return; - } - - if (style->extInfos != NULL) { - while ((parent != NULL) && (parent->type != XML_DOCUMENT_NODE)) { - /* - * if we are within an extension element all bets are off - * about the semantic there e.g. xsl:param within func:function - */ - if ((parent->ns != NULL) && - (xmlHashLookup(style->extInfos, parent->ns->href) != NULL)) - return; - - parent = parent->parent; - } - } - xsltTransformError(NULL, style, inst, - "element %s is not allowed within that context\n", - inst->name); - style->errors++; -} -#endif - -/************************************************************************ - * * - * handling of precomputed data * - * * - ************************************************************************/ - -/** - * xsltNewStylePreComp: - * @style: the XSLT stylesheet - * @type: the construct type - * - * Create a new XSLT Style precomputed block - * - * Returns the newly allocated specialized structure - * or NULL in case of error - */ -static xsltStylePreCompPtr -xsltNewStylePreComp(xsltStylesheetPtr style, xsltStyleType type) { - xsltStylePreCompPtr cur; -#ifdef XSLT_REFACTORED - size_t size; -#endif - - if (style == NULL) - return(NULL); - -#ifdef XSLT_REFACTORED - /* - * URGENT TODO: Use specialized factory functions in order - * to avoid this ugliness. - */ - switch (type) { - case XSLT_FUNC_COPY: - size = sizeof(xsltStyleItemCopy); break; - case XSLT_FUNC_SORT: - size = sizeof(xsltStyleItemSort); break; - case XSLT_FUNC_TEXT: - size = sizeof(xsltStyleItemText); break; - case XSLT_FUNC_ELEMENT: - size = sizeof(xsltStyleItemElement); break; - case XSLT_FUNC_ATTRIBUTE: - size = sizeof(xsltStyleItemAttribute); break; - case XSLT_FUNC_COMMENT: - size = sizeof(xsltStyleItemComment); break; - case XSLT_FUNC_PI: - size = sizeof(xsltStyleItemPI); break; - case XSLT_FUNC_COPYOF: - size = sizeof(xsltStyleItemCopyOf); break; - case XSLT_FUNC_VALUEOF: - size = sizeof(xsltStyleItemValueOf); break;; - case XSLT_FUNC_NUMBER: - size = sizeof(xsltStyleItemNumber); break; - case XSLT_FUNC_APPLYIMPORTS: - size = sizeof(xsltStyleItemApplyImports); break; - case XSLT_FUNC_CALLTEMPLATE: - size = sizeof(xsltStyleItemCallTemplate); break; - case XSLT_FUNC_APPLYTEMPLATES: - size = sizeof(xsltStyleItemApplyTemplates); break; - case XSLT_FUNC_CHOOSE: - size = sizeof(xsltStyleItemChoose); break; - case XSLT_FUNC_IF: - size = sizeof(xsltStyleItemIf); break; - case XSLT_FUNC_FOREACH: - size = sizeof(xsltStyleItemForEach); break; - case XSLT_FUNC_DOCUMENT: - size = sizeof(xsltStyleItemDocument); break; - case XSLT_FUNC_WITHPARAM: - size = sizeof(xsltStyleItemWithParam); break; - case XSLT_FUNC_PARAM: - size = sizeof(xsltStyleItemParam); break; - case XSLT_FUNC_VARIABLE: - size = sizeof(xsltStyleItemVariable); break; - case XSLT_FUNC_WHEN: - size = sizeof(xsltStyleItemWhen); break; - case XSLT_FUNC_OTHERWISE: - size = sizeof(xsltStyleItemOtherwise); break; - default: - xsltTransformError(NULL, style, NULL, - "xsltNewStylePreComp : invalid type %d\n", type); - style->errors++; - return(NULL); - } - /* - * Create the structure. - */ - cur = (xsltStylePreCompPtr) xmlMalloc(size); - if (cur == NULL) { - xsltTransformError(NULL, style, NULL, - "xsltNewStylePreComp : malloc failed\n"); - style->errors++; - return(NULL); - } - memset(cur, 0, size); - -#else /* XSLT_REFACTORED */ - /* - * Old behaviour. - */ - cur = (xsltStylePreCompPtr) xmlMalloc(sizeof(xsltStylePreComp)); - if (cur == NULL) { - xsltTransformError(NULL, style, NULL, - "xsltNewStylePreComp : malloc failed\n"); - style->errors++; - return(NULL); - } - memset(cur, 0, sizeof(xsltStylePreComp)); -#endif /* XSLT_REFACTORED */ - - /* - * URGENT TODO: Better to move this to spezialized factory functions. - */ - cur->type = type; - switch (cur->type) { - case XSLT_FUNC_COPY: - cur->func = (xsltTransformFunction) xsltCopy;break; - case XSLT_FUNC_SORT: - cur->func = (xsltTransformFunction) xsltSort;break; - case XSLT_FUNC_TEXT: - cur->func = (xsltTransformFunction) xsltText;break; - case XSLT_FUNC_ELEMENT: - cur->func = (xsltTransformFunction) xsltElement;break; - case XSLT_FUNC_ATTRIBUTE: - cur->func = (xsltTransformFunction) xsltAttribute;break; - case XSLT_FUNC_COMMENT: - cur->func = (xsltTransformFunction) xsltComment;break; - case XSLT_FUNC_PI: - cur->func = (xsltTransformFunction) xsltProcessingInstruction; - break; - case XSLT_FUNC_COPYOF: - cur->func = (xsltTransformFunction) xsltCopyOf;break; - case XSLT_FUNC_VALUEOF: - cur->func = (xsltTransformFunction) xsltValueOf;break; - case XSLT_FUNC_NUMBER: - cur->func = (xsltTransformFunction) xsltNumber;break; - case XSLT_FUNC_APPLYIMPORTS: - cur->func = (xsltTransformFunction) xsltApplyImports;break; - case XSLT_FUNC_CALLTEMPLATE: - cur->func = (xsltTransformFunction) xsltCallTemplate;break; - case XSLT_FUNC_APPLYTEMPLATES: - cur->func = (xsltTransformFunction) xsltApplyTemplates;break; - case XSLT_FUNC_CHOOSE: - cur->func = (xsltTransformFunction) xsltChoose;break; - case XSLT_FUNC_IF: - cur->func = (xsltTransformFunction) xsltIf;break; - case XSLT_FUNC_FOREACH: - cur->func = (xsltTransformFunction) xsltForEach;break; - case XSLT_FUNC_DOCUMENT: - cur->func = (xsltTransformFunction) xsltDocumentElem;break; - case XSLT_FUNC_WITHPARAM: - case XSLT_FUNC_PARAM: - case XSLT_FUNC_VARIABLE: - case XSLT_FUNC_WHEN: - break; - default: - if (cur->func == NULL) { - xsltTransformError(NULL, style, NULL, - "xsltNewStylePreComp : no function for type %d\n", type); - style->errors++; - } - } - cur->next = style->preComps; - style->preComps = (xsltElemPreCompPtr) cur; - - return(cur); -} - -/** - * xsltFreeStylePreComp: - * @comp: an XSLT Style precomputed block - * - * Free up the memory allocated by @comp - */ -static void -xsltFreeStylePreComp(xsltStylePreCompPtr comp) { - if (comp == NULL) - return; -#ifdef XSLT_REFACTORED - /* - * URGENT TODO: Implement destructors. - */ - switch (comp->type) { - case XSLT_FUNC_LITERAL_RESULT_ELEMENT: - break; - case XSLT_FUNC_COPY: - break; - case XSLT_FUNC_SORT: { - xsltStyleItemSortPtr item = (xsltStyleItemSortPtr) comp; - if (item->comp != NULL) - xmlXPathFreeCompExpr(item->comp); - } - break; - case XSLT_FUNC_TEXT: - break; - case XSLT_FUNC_ELEMENT: - break; - case XSLT_FUNC_ATTRIBUTE: - break; - case XSLT_FUNC_COMMENT: - break; - case XSLT_FUNC_PI: - break; - case XSLT_FUNC_COPYOF: { - xsltStyleItemCopyOfPtr item = (xsltStyleItemCopyOfPtr) comp; - if (item->comp != NULL) - xmlXPathFreeCompExpr(item->comp); - } - break; - case XSLT_FUNC_VALUEOF: { - xsltStyleItemValueOfPtr item = (xsltStyleItemValueOfPtr) comp; - if (item->comp != NULL) - xmlXPathFreeCompExpr(item->comp); - } - break; - case XSLT_FUNC_NUMBER: - break; - case XSLT_FUNC_APPLYIMPORTS: - break; - case XSLT_FUNC_CALLTEMPLATE: - break; - case XSLT_FUNC_APPLYTEMPLATES: { - xsltStyleItemApplyTemplatesPtr item = - (xsltStyleItemApplyTemplatesPtr) comp; - if (item->comp != NULL) - xmlXPathFreeCompExpr(item->comp); - } - break; - case XSLT_FUNC_CHOOSE: - break; - case XSLT_FUNC_IF: { - xsltStyleItemIfPtr item = (xsltStyleItemIfPtr) comp; - if (item->comp != NULL) - xmlXPathFreeCompExpr(item->comp); - } - break; - case XSLT_FUNC_FOREACH: { - xsltStyleItemForEachPtr item = - (xsltStyleItemForEachPtr) comp; - if (item->comp != NULL) - xmlXPathFreeCompExpr(item->comp); - } - break; - case XSLT_FUNC_DOCUMENT: - break; - case XSLT_FUNC_WITHPARAM: { - xsltStyleItemWithParamPtr item = - (xsltStyleItemWithParamPtr) comp; - if (item->comp != NULL) - xmlXPathFreeCompExpr(item->comp); - } - break; - case XSLT_FUNC_PARAM: { - xsltStyleItemParamPtr item = - (xsltStyleItemParamPtr) comp; - if (item->comp != NULL) - xmlXPathFreeCompExpr(item->comp); - } - break; - case XSLT_FUNC_VARIABLE: { - xsltStyleItemVariablePtr item = - (xsltStyleItemVariablePtr) comp; - if (item->comp != NULL) - xmlXPathFreeCompExpr(item->comp); - } - break; - case XSLT_FUNC_WHEN: { - xsltStyleItemWhenPtr item = - (xsltStyleItemWhenPtr) comp; - if (item->comp != NULL) - xmlXPathFreeCompExpr(item->comp); - } - break; - case XSLT_FUNC_OTHERWISE: - case XSLT_FUNC_FALLBACK: - case XSLT_FUNC_MESSAGE: - case XSLT_FUNC_INCLUDE: - case XSLT_FUNC_ATTRSET: - - break; - default: - /* TODO: Raise error. */ - break; - } -#else - if (comp->comp != NULL) - xmlXPathFreeCompExpr(comp->comp); - if (comp->nsList != NULL) - xmlFree(comp->nsList); -#endif - - xmlFree(comp); -} - - -/************************************************************************ - * * - * XSLT-1.1 extensions * - * * - ************************************************************************/ - -/** - * xsltDocumentComp: - * @style: the XSLT stylesheet - * @inst: the instruction in the stylesheet - * @function: unused - * - * Pre process an XSLT-1.1 document element - * - * Returns a precompiled data structure for the element - */ -xsltElemPreCompPtr -xsltDocumentComp(xsltStylesheetPtr style, xmlNodePtr inst, - xsltTransformFunction function ATTRIBUTE_UNUSED) { -#ifdef XSLT_REFACTORED - xsltStyleItemDocumentPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - const xmlChar *filename = NULL; - - /* - * As of 2006-03-30, this function is currently defined in Libxslt - * to be used for: - * (in libxslt/extra.c) - * "output" in XSLT_SAXON_NAMESPACE - * "write" XSLT_XALAN_NAMESPACE - * "document" XSLT_XT_NAMESPACE - * "document" XSLT_NAMESPACE (from the abandoned old working - * draft of XSLT 1.1) - * (in libexslt/common.c) - * "document" in EXSLT_COMMON_NAMESPACE - */ -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemDocumentPtr) - xsltNewStylePreComp(style, XSLT_FUNC_DOCUMENT); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_DOCUMENT); -#endif - - if (comp == NULL) - return (NULL); - comp->inst = inst; - comp->ver11 = 0; - - if (xmlStrEqual(inst->name, (const xmlChar *) "output")) { -#ifdef WITH_XSLT_DEBUG_EXTRA - xsltGenericDebug(xsltGenericDebugContext, - "Found saxon:output extension\n"); -#endif - /* - * The element "output" is in the namespace XSLT_SAXON_NAMESPACE - * (http://icl.com/saxon) - * The @file is in no namespace; it is an AVT. - * (http://www.computerwizards.com/saxon/doc/extensions.html#saxon:output) - * - * TODO: Do we need not to check the namespace here? - */ - filename = xsltEvalStaticAttrValueTemplate(style, inst, - (const xmlChar *)"file", - NULL, &comp->has_filename); - } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) { -#ifdef WITH_XSLT_DEBUG_EXTRA - xsltGenericDebug(xsltGenericDebugContext, - "Found xalan:write extension\n"); -#endif - /* the filename need to be interpreted */ - /* - * TODO: Is "filename need to be interpreted" meant to be a todo? - * Where will be the filename of xalan:write be processed? - * - * TODO: Do we need not to check the namespace here? - * The extension ns is "http://xml.apache.org/xalan/redirect". - * See http://xml.apache.org/xalan-j/extensionslib.html. - */ - } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) { - if (inst->ns != NULL) { - if (xmlStrEqual(inst->ns->href, XSLT_NAMESPACE)) { - /* - * Mark the instruction as being of - * XSLT version 1.1 (abandoned). - */ - comp->ver11 = 1; -#ifdef WITH_XSLT_DEBUG_EXTRA - xsltGenericDebug(xsltGenericDebugContext, - "Found xslt11:document construct\n"); -#endif - } else { - if (xmlStrEqual(inst->ns->href, - (const xmlChar *)"http://exslt.org/common")) { - /* EXSLT. */ -#ifdef WITH_XSLT_DEBUG_EXTRA - xsltGenericDebug(xsltGenericDebugContext, - "Found exslt:document extension\n"); -#endif - } else if (xmlStrEqual(inst->ns->href, XSLT_XT_NAMESPACE)) { - /* James Clark's XT. */ -#ifdef WITH_XSLT_DEBUG_EXTRA - xsltGenericDebug(xsltGenericDebugContext, - "Found xt:document extension\n"); -#endif - } - } - } - /* - * The element "document" is used in conjunction with the - * following namespaces: - * - * 1) XSLT_NAMESPACE (http://www.w3.org/1999/XSL/Transform version 1.1) - * - * - * TODO: is @href is an AVT? - * - * In all cases @href is in no namespace. - */ - filename = xsltEvalStaticAttrValueTemplate(style, inst, - (const xmlChar *)"href", NULL, &comp->has_filename); - } - if (!comp->has_filename) { - goto error; - } - comp->filename = filename; - -error: - return ((xsltElemPreCompPtr) comp); -} - -/************************************************************************ - * * - * Most of the XSLT-1.0 transformations * - * * - ************************************************************************/ - -/** - * xsltSortComp: - * @style: the XSLT stylesheet - * @inst: the xslt sort node - * - * Process the xslt sort node on the source node - */ -static void -xsltSortComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemSortPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemSortPtr) xsltNewStylePreComp(style, XSLT_FUNC_SORT); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_SORT); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; - - comp->stype = xsltEvalStaticAttrValueTemplate(style, inst, - (const xmlChar *)"data-type", - NULL, &comp->has_stype); - if (comp->stype != NULL) { - if (xmlStrEqual(comp->stype, (const xmlChar *) "text")) - comp->number = 0; - else if (xmlStrEqual(comp->stype, (const xmlChar *) "number")) - comp->number = 1; - else { - xsltTransformError(NULL, style, inst, - "xsltSortComp: no support for data-type = %s\n", comp->stype); - comp->number = 0; /* use default */ - if (style != NULL) style->warnings++; - } - } - comp->order = xsltEvalStaticAttrValueTemplate(style, inst, - (const xmlChar *)"order", - NULL, &comp->has_order); - if (comp->order != NULL) { - if (xmlStrEqual(comp->order, (const xmlChar *) "ascending")) - comp->descending = 0; - else if (xmlStrEqual(comp->order, (const xmlChar *) "descending")) - comp->descending = 1; - else { - xsltTransformError(NULL, style, inst, - "xsltSortComp: invalid value %s for order\n", comp->order); - comp->descending = 0; /* use default */ - if (style != NULL) style->warnings++; - } - } - comp->case_order = xsltEvalStaticAttrValueTemplate(style, inst, - (const xmlChar *)"case-order", - NULL, &comp->has_use); - if (comp->case_order != NULL) { - if (xmlStrEqual(comp->case_order, (const xmlChar *) "upper-first")) - comp->lower_first = 0; - else if (xmlStrEqual(comp->case_order, (const xmlChar *) "lower-first")) - comp->lower_first = 1; - else { - xsltTransformError(NULL, style, inst, - "xsltSortComp: invalid value %s for order\n", comp->order); - comp->lower_first = 0; /* use default */ - if (style != NULL) style->warnings++; - } - } - - comp->lang = xsltEvalStaticAttrValueTemplate(style, inst, - (const xmlChar *)"lang", - NULL, &comp->has_lang); - - comp->select = xsltGetCNsProp(style, inst,(const xmlChar *)"select", XSLT_NAMESPACE); - if (comp->select == NULL) { - /* - * The default value of the select attribute is ., which will - * cause the string-value of the current node to be used as - * the sort key. - */ - comp->select = xmlDictLookup(style->dict, BAD_CAST ".", 1); - } - comp->comp = xsltXPathCompile(style, comp->select); - if (comp->comp == NULL) { - xsltTransformError(NULL, style, inst, - "xsltSortComp: could not compile select expression '%s'\n", - comp->select); - if (style != NULL) style->errors++; - } - if (inst->children != NULL) { - xsltTransformError(NULL, style, inst, - "xsl:sort : is not empty\n"); - if (style != NULL) style->errors++; - } -} - -/** - * xsltCopyComp: - * @style: the XSLT stylesheet - * @inst: the xslt copy node - * - * Process the xslt copy node on the source node - */ -static void -xsltCopyComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemCopyPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - - if ((style == NULL) || (inst == NULL)) - return; -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemCopyPtr) xsltNewStylePreComp(style, XSLT_FUNC_COPY); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_COPY); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; - - - comp->use = xsltGetCNsProp(style, inst, (const xmlChar *)"use-attribute-sets", - XSLT_NAMESPACE); - if (comp->use == NULL) - comp->has_use = 0; - else - comp->has_use = 1; -} - -#ifdef XSLT_REFACTORED - /* Enable if ever needed for xsl:text. */ -#else -/** - * xsltTextComp: - * @style: an XSLT compiled stylesheet - * @inst: the xslt text node - * - * TODO: This function is obsolete, since xsl:text won't - * be compiled, but removed from the tree. - * - * Process the xslt text node on the source node - */ -static void -xsltTextComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemTextPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - const xmlChar *prop; - - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemTextPtr) xsltNewStylePreComp(style, XSLT_FUNC_TEXT); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_TEXT); -#endif - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; - comp->noescape = 0; - - prop = xsltGetCNsProp(style, inst, - (const xmlChar *)"disable-output-escaping", - XSLT_NAMESPACE); - if (prop != NULL) { - if (xmlStrEqual(prop, (const xmlChar *)"yes")) { - comp->noescape = 1; - } else if (!xmlStrEqual(prop, - (const xmlChar *)"no")){ - xsltTransformError(NULL, style, inst, - "xsl:text: disable-output-escaping allows only yes or no\n"); - if (style != NULL) style->warnings++; - } - } -} -#endif /* else of XSLT_REFACTORED */ - -/** - * xsltElementComp: - * @style: an XSLT compiled stylesheet - * @inst: the xslt element node - * - * Process the xslt element node on the source node - */ -static void -xsltElementComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemElementPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - - /* - * - * - * - */ - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemElementPtr) xsltNewStylePreComp(style, XSLT_FUNC_ELEMENT); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_ELEMENT); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; - - /* - * Attribute "name". - */ - /* - * TODO: Precompile the AVT. See bug #344894. - */ - comp->name = xsltEvalStaticAttrValueTemplate(style, inst, - (const xmlChar *)"name", NULL, &comp->has_name); - if (! comp->has_name) { - xsltTransformError(NULL, style, inst, - "xsl:element: The attribute 'name' is missing.\n"); - style->errors++; - goto error; - } - /* - * Attribute "namespace". - */ - /* - * TODO: Precompile the AVT. See bug #344894. - */ - comp->ns = xsltEvalStaticAttrValueTemplate(style, inst, - (const xmlChar *)"namespace", NULL, &comp->has_ns); - - if (comp->name != NULL) { - if (xmlValidateQName(comp->name, 0)) { - xsltTransformError(NULL, style, inst, - "xsl:element: The value '%s' of the attribute 'name' is " - "not a valid QName.\n", comp->name); - style->errors++; - } else { - const xmlChar *prefix = NULL, *name; - - name = xsltSplitQName(style->dict, comp->name, &prefix); - if (comp->has_ns == 0) { - 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) { - comp->ns = xmlDictLookup(style->dict, ns->href, -1); - comp->has_ns = 1; -#ifdef XSLT_REFACTORED - comp->nsPrefix = prefix; - comp->name = name; -#endif - } else if (prefix != NULL) { - xsltTransformError(NULL, style, inst, - "xsl:element: The prefixed QName '%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", comp->name); - style->errors++; - } - } - if ((prefix != NULL) && - (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3))) - { - /* - * Mark is to be skipped. - */ - comp->has_name = 0; - } - } - } - /* - * Attribute "use-attribute-sets", - */ - comp->use = xsltEvalStaticAttrValueTemplate(style, inst, - (const xmlChar *)"use-attribute-sets", - NULL, &comp->has_use); - -error: - return; -} - -/** - * xsltAttributeComp: - * @style: an XSLT compiled stylesheet - * @inst: the xslt attribute node - * - * Process the xslt attribute node on the source node - */ -static void -xsltAttributeComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemAttributePtr comp; -#else - xsltStylePreCompPtr comp; -#endif - - /* - * - * - * - */ - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemAttributePtr) xsltNewStylePreComp(style, - XSLT_FUNC_ATTRIBUTE); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_ATTRIBUTE); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; - - /* - * Attribute "name". - */ - /* - * TODO: Precompile the AVT. See bug #344894. - */ - comp->name = xsltEvalStaticAttrValueTemplate(style, inst, - (const xmlChar *)"name", - NULL, &comp->has_name); - if (! comp->has_name) { - xsltTransformError(NULL, style, inst, - "xsl:attribute: The attribute 'name' is missing.\n"); - style->errors++; - return; - } - /* - * Attribute "namespace". - */ - /* - * TODO: Precompile the AVT. See bug #344894. - */ - comp->ns = xsltEvalStaticAttrValueTemplate(style, inst, - (const xmlChar *)"namespace", - NULL, &comp->has_ns); - - if (comp->name != NULL) { - if (xmlValidateQName(comp->name, 0)) { - xsltTransformError(NULL, style, inst, - "xsl:attribute: The value '%s' of the attribute 'name' is " - "not a valid QName.\n", comp->name); - style->errors++; - } else { - const xmlChar *prefix = NULL, *name; - - name = xsltSplitQName(style->dict, comp->name, &prefix); - if (prefix != NULL) { - if (comp->has_ns == 0) { - 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) { - comp->ns = xmlDictLookup(style->dict, ns->href, -1); - comp->has_ns = 1; -#ifdef XSLT_REFACTORED - comp->nsPrefix = prefix; - comp->name = name; -#endif - } else { - xsltTransformError(NULL, style, inst, - "xsl:attribute: The prefixed QName '%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", comp->name); - style->errors++; - } - } - if (!xmlStrncasecmp(prefix, (xmlChar *) "xmlns", 5)) { - /* - * SPEC XSLT 1.0: - * "It is an error if the string that results from - * instantiating the attribute value template is not a - * QName or is the string xmlns. An XSLT processor may - * signal the error; if it does not signal the error, - * it must recover by not adding the attribute to the - * result tree." - * - * Reject a prefix of "xmlns". Mark to be skipped. - */ - comp->has_name = 0; - -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltAttribute: xmlns prefix forbidden\n"); -#endif - return; - } - - } - } - } -} - -/** - * xsltCommentComp: - * @style: an XSLT compiled stylesheet - * @inst: the xslt comment node - * - * Process the xslt comment node on the source node - */ -static void -xsltCommentComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemCommentPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemCommentPtr) xsltNewStylePreComp(style, XSLT_FUNC_COMMENT); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_COMMENT); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; -} - -/** - * xsltProcessingInstructionComp: - * @style: an XSLT compiled stylesheet - * @inst: the xslt processing-instruction node - * - * Process the xslt processing-instruction node on the source node - */ -static void -xsltProcessingInstructionComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemPIPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemPIPtr) xsltNewStylePreComp(style, XSLT_FUNC_PI); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_PI); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; - - comp->name = xsltEvalStaticAttrValueTemplate(style, inst, - (const xmlChar *)"name", - XSLT_NAMESPACE, &comp->has_name); -} - -/** - * xsltCopyOfComp: - * @style: an XSLT compiled stylesheet - * @inst: the xslt copy-of node - * - * Process the xslt copy-of node on the source node - */ -static void -xsltCopyOfComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemCopyOfPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemCopyOfPtr) xsltNewStylePreComp(style, XSLT_FUNC_COPYOF); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_COPYOF); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; - - comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", - XSLT_NAMESPACE); - if (comp->select == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:copy-of : select is missing\n"); - if (style != NULL) style->errors++; - return; - } - comp->comp = xsltXPathCompile(style, comp->select); - if (comp->comp == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:copy-of : could not compile select expression '%s'\n", - comp->select); - if (style != NULL) style->errors++; - } -} - -/** - * xsltValueOfComp: - * @style: an XSLT compiled stylesheet - * @inst: the xslt value-of node - * - * Process the xslt value-of node on the source node - */ -static void -xsltValueOfComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemValueOfPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - const xmlChar *prop; - - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemValueOfPtr) xsltNewStylePreComp(style, XSLT_FUNC_VALUEOF); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_VALUEOF); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; - - prop = xsltGetCNsProp(style, inst, - (const xmlChar *)"disable-output-escaping", - XSLT_NAMESPACE); - if (prop != NULL) { - if (xmlStrEqual(prop, (const xmlChar *)"yes")) { - comp->noescape = 1; - } else if (!xmlStrEqual(prop, - (const xmlChar *)"no")){ - xsltTransformError(NULL, style, inst, -"xsl:value-of : disable-output-escaping allows only yes or no\n"); - if (style != NULL) style->warnings++; - } - } - comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", - XSLT_NAMESPACE); - if (comp->select == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:value-of : select is missing\n"); - if (style != NULL) style->errors++; - return; - } - comp->comp = xsltXPathCompile(style, comp->select); - if (comp->comp == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:value-of : could not compile select expression '%s'\n", - comp->select); - if (style != NULL) style->errors++; - } -} - -/** - * xsltWithParamComp: - * @style: an XSLT compiled stylesheet - * @inst: the xslt with-param node - * - * Process the xslt with-param node on the source node - * Allowed parents: xsl:call-template, xsl:apply-templates. - * - * - * - */ -static void -xsltWithParamComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemWithParamPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - const xmlChar *prop; - - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemWithParamPtr) xsltNewStylePreComp(style, XSLT_FUNC_WITHPARAM); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_WITHPARAM); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; - - /* - * The full namespace resolution can be done statically - */ - prop = xsltGetCNsProp(style, inst, (const xmlChar *)"name", XSLT_NAMESPACE); - if (prop == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:with-param : name is missing\n"); - if (style != NULL) style->errors++; - } else { - const xmlChar *URI; - - URI = xsltGetQNameURI2(style, inst, &prop); - if (prop == NULL) { - if (style != NULL) style->errors++; - } else { - comp->name = prop; - comp->has_name = 1; - if (URI != NULL) { - comp->ns = xmlStrdup(URI); - comp->has_ns = 1; - } else { - comp->has_ns = 0; - } - } - } - - comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", - XSLT_NAMESPACE); - if (comp->select != NULL) { - comp->comp = xsltXPathCompile(style, comp->select); - if (comp->comp == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:param : could not compile select expression '%s'\n", - comp->select); - if (style != NULL) style->errors++; - } - if (inst->children != NULL) { - xsltTransformError(NULL, style, inst, - "xsl:param : content should be empty since select is present \n"); - if (style != NULL) style->warnings++; - } - } -} - -/** - * xsltNumberComp: - * @style: an XSLT compiled stylesheet - * @cur: the xslt number node - * - * Process the xslt number node on the source node - */ -static void -xsltNumberComp(xsltStylesheetPtr style, xmlNodePtr cur) { -#ifdef XSLT_REFACTORED - xsltStyleItemNumberPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - const xmlChar *prop; - - if ((style == NULL) || (cur == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemNumberPtr) xsltNewStylePreComp(style, XSLT_FUNC_NUMBER); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_NUMBER); -#endif - - if (comp == NULL) - return; - cur->psvi = comp; - - if ((style == NULL) || (cur == NULL)) - return; - - comp->numdata.doc = cur->doc; - comp->numdata.node = cur; - comp->numdata.value = xsltGetCNsProp(style, cur, (const xmlChar *)"value", - XSLT_NAMESPACE); - - prop = xsltEvalStaticAttrValueTemplate(style, cur, - (const xmlChar *)"format", - XSLT_NAMESPACE, &comp->numdata.has_format); - if (comp->numdata.has_format == 0) { - comp->numdata.format = xmlDictLookup(style->dict, BAD_CAST "" , 0); - } else { - comp->numdata.format = prop; - } - - comp->numdata.count = xsltGetCNsProp(style, cur, (const xmlChar *)"count", - XSLT_NAMESPACE); - comp->numdata.from = xsltGetCNsProp(style, cur, (const xmlChar *)"from", - XSLT_NAMESPACE); - - prop = xsltGetCNsProp(style, cur, (const xmlChar *)"level", XSLT_NAMESPACE); - if (prop != NULL) { - if (xmlStrEqual(prop, BAD_CAST("single")) || - xmlStrEqual(prop, BAD_CAST("multiple")) || - xmlStrEqual(prop, BAD_CAST("any"))) { - comp->numdata.level = prop; - } else { - xsltTransformError(NULL, style, cur, - "xsl:number : invalid value %s for level\n", prop); - if (style != NULL) style->warnings++; - xmlFree((void *)(prop)); - } - } - - prop = xsltGetCNsProp(style, cur, (const xmlChar *)"lang", XSLT_NAMESPACE); - if (prop != NULL) { - XSLT_TODO; /* xsl:number lang attribute */ - xmlFree((void *)prop); - } - - prop = xsltGetCNsProp(style, cur, (const xmlChar *)"letter-value", XSLT_NAMESPACE); - if (prop != NULL) { - if (xmlStrEqual(prop, BAD_CAST("alphabetic"))) { - xsltTransformError(NULL, style, cur, - "xsl:number : letter-value 'alphabetic' not implemented\n"); - if (style != NULL) style->warnings++; - XSLT_TODO; /* xsl:number letter-value attribute alphabetic */ - } else if (xmlStrEqual(prop, BAD_CAST("traditional"))) { - xsltTransformError(NULL, style, cur, - "xsl:number : letter-value 'traditional' not implemented\n"); - if (style != NULL) style->warnings++; - XSLT_TODO; /* xsl:number letter-value attribute traditional */ - } else { - xsltTransformError(NULL, style, cur, - "xsl:number : invalid value %s for letter-value\n", prop); - if (style != NULL) style->warnings++; - } - } - - prop = xsltGetCNsProp(style, cur, (const xmlChar *)"grouping-separator", - XSLT_NAMESPACE); - if (prop != NULL) { - comp->numdata.groupingCharacterLen = xmlStrlen(prop); - comp->numdata.groupingCharacter = - xsltGetUTF8Char(prop, &(comp->numdata.groupingCharacterLen)); - } - - prop = xsltGetCNsProp(style, cur, (const xmlChar *)"grouping-size", XSLT_NAMESPACE); - if (prop != NULL) { - sscanf((char *)prop, "%d", &comp->numdata.digitsPerGroup); - } else { - comp->numdata.groupingCharacter = 0; - } - - /* Set default values */ - if (comp->numdata.value == NULL) { - if (comp->numdata.level == NULL) { - comp->numdata.level = xmlDictLookup(style->dict, - BAD_CAST"single", 6); - } - } - -} - -/** - * xsltApplyImportsComp: - * @style: an XSLT compiled stylesheet - * @inst: the xslt apply-imports node - * - * Process the xslt apply-imports node on the source node - */ -static void -xsltApplyImportsComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemApplyImportsPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemApplyImportsPtr) xsltNewStylePreComp(style, XSLT_FUNC_APPLYIMPORTS); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_APPLYIMPORTS); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; -} - -/** - * xsltCallTemplateComp: - * @style: an XSLT compiled stylesheet - * @inst: the xslt call-template node - * - * Process the xslt call-template node on the source node - */ -static void -xsltCallTemplateComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemCallTemplatePtr comp; -#else - xsltStylePreCompPtr comp; -#endif - const xmlChar *prop; - - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemCallTemplatePtr) - xsltNewStylePreComp(style, XSLT_FUNC_CALLTEMPLATE); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_CALLTEMPLATE); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; - - /* - * The full template resolution can be done statically - */ - prop = xsltGetCNsProp(style, inst, (const xmlChar *)"name", XSLT_NAMESPACE); - if (prop == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:call-template : name is missing\n"); - if (style != NULL) style->errors++; - } else { - const xmlChar *URI; - - URI = xsltGetQNameURI2(style, inst, &prop); - if (prop == NULL) { - if (style != NULL) style->errors++; - } else { - comp->name = prop; - comp->has_name = 1; - if (URI != NULL) { - comp->ns = URI; - comp->has_ns = 1; - } else { - comp->has_ns = 0; - } - } - comp->templ = NULL; - } -} - -/** - * xsltApplyTemplatesComp: - * @style: an XSLT compiled stylesheet - * @inst: the apply-templates node - * - * Process the apply-templates node on the source node - */ -static void -xsltApplyTemplatesComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemApplyTemplatesPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - const xmlChar *prop; - - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemApplyTemplatesPtr) - xsltNewStylePreComp(style, XSLT_FUNC_APPLYTEMPLATES); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_APPLYTEMPLATES); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; - - /* - * Get mode if any - */ - prop = xsltGetCNsProp(style, inst, (const xmlChar *)"mode", XSLT_NAMESPACE); - if (prop != NULL) { - const xmlChar *URI; - - URI = xsltGetQNameURI2(style, inst, &prop); - if (prop == NULL) { - if (style != NULL) style->errors++; - } else { - comp->mode = xmlDictLookup(style->dict, prop, -1); - if (URI != NULL) { - comp->modeURI = xmlDictLookup(style->dict, URI, -1); - } else { - comp->modeURI = NULL; - } - } - } - comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", - XSLT_NAMESPACE); - if (comp->select != NULL) { - comp->comp = xsltXPathCompile(style, comp->select); - if (comp->comp == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:apply-templates : could not compile select expression '%s'\n", - comp->select); - if (style != NULL) style->errors++; - } - } - - /* TODO: handle (or skip) the xsl:sort and xsl:with-param */ -} - -/** - * xsltChooseComp: - * @style: an XSLT compiled stylesheet - * @inst: the xslt choose node - * - * Process the xslt choose node on the source node - */ -static void -xsltChooseComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemChoosePtr comp; -#else - xsltStylePreCompPtr comp; -#endif - - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemChoosePtr) - xsltNewStylePreComp(style, XSLT_FUNC_CHOOSE); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_CHOOSE); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; -} - -/** - * xsltIfComp: - * @style: an XSLT compiled stylesheet - * @inst: the xslt if node - * - * Process the xslt if node on the source node - */ -static void -xsltIfComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemIfPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemIfPtr) - xsltNewStylePreComp(style, XSLT_FUNC_IF); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_IF); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; - - comp->test = xsltGetCNsProp(style, inst, (const xmlChar *)"test", XSLT_NAMESPACE); - if (comp->test == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:if : test is not defined\n"); - if (style != NULL) style->errors++; - return; - } - comp->comp = xsltXPathCompile(style, comp->test); - if (comp->comp == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:if : could not compile test expression '%s'\n", - comp->test); - if (style != NULL) style->errors++; - } -} - -/** - * xsltWhenComp: - * @style: an XSLT compiled stylesheet - * @inst: the xslt if node - * - * Process the xslt if node on the source node - */ -static void -xsltWhenComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemWhenPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemWhenPtr) - xsltNewStylePreComp(style, XSLT_FUNC_WHEN); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_WHEN); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; - - comp->test = xsltGetCNsProp(style, inst, (const xmlChar *)"test", XSLT_NAMESPACE); - if (comp->test == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:when : test is not defined\n"); - if (style != NULL) style->errors++; - return; - } - comp->comp = xsltXPathCompile(style, comp->test); - if (comp->comp == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:when : could not compile test expression '%s'\n", - comp->test); - if (style != NULL) style->errors++; - } -} - -/** - * xsltForEachComp: - * @style: an XSLT compiled stylesheet - * @inst: the xslt for-each node - * - * Process the xslt for-each node on the source node - */ -static void -xsltForEachComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemForEachPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemForEachPtr) - xsltNewStylePreComp(style, XSLT_FUNC_FOREACH); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_FOREACH); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; - - comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", - XSLT_NAMESPACE); - if (comp->select == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:for-each : select is missing\n"); - if (style != NULL) style->errors++; - } else { - comp->comp = xsltXPathCompile(style, comp->select); - if (comp->comp == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:for-each : could not compile select expression '%s'\n", - comp->select); - if (style != NULL) style->errors++; - } - } - /* TODO: handle and skip the xsl:sort */ -} - -/** - * xsltVariableComp: - * @style: an XSLT compiled stylesheet - * @inst: the xslt variable node - * - * Process the xslt variable node on the source node - */ -static void -xsltVariableComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemVariablePtr comp; -#else - xsltStylePreCompPtr comp; -#endif - const xmlChar *prop; - - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemVariablePtr) - xsltNewStylePreComp(style, XSLT_FUNC_VARIABLE); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_VARIABLE); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; - - /* - * The full template resolution can be done statically - */ - prop = xsltGetCNsProp(style, inst, (const xmlChar *)"name", XSLT_NAMESPACE); - if (prop == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:variable : name is missing\n"); - if (style != NULL) style->errors++; - } else { - const xmlChar *URI; - - URI = xsltGetQNameURI2(style, inst, &prop); - if (prop == NULL) { - if (style != NULL) style->errors++; - } else { - comp->name = prop; - comp->has_name = 1; - if (URI != NULL) { - comp->ns = xmlDictLookup(style->dict, URI, -1); - comp->has_ns = 1; - } else { - comp->has_ns = 0; - } - } - } - - comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", - XSLT_NAMESPACE); - if (comp->select != NULL) { - comp->comp = xsltXPathCompile(style, comp->select); - if (comp->comp == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:variable : could not compile select expression '%s'\n", - comp->select); - if (style != NULL) style->errors++; - } - if (inst->children != NULL) { - xsltTransformError(NULL, style, inst, - "xsl:variable : content should be empty since select is present \n"); - if (style != NULL) style->warnings++; - } - } -} - -/** - * xsltParamComp: - * @style: an XSLT compiled stylesheet - * @inst: the xslt param node - * - * Process the xslt param node on the source node - */ -static void -xsltParamComp(xsltStylesheetPtr style, xmlNodePtr inst) { -#ifdef XSLT_REFACTORED - xsltStyleItemParamPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - const xmlChar *prop; - - if ((style == NULL) || (inst == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemParamPtr) - xsltNewStylePreComp(style, XSLT_FUNC_PARAM); -#else - comp = xsltNewStylePreComp(style, XSLT_FUNC_PARAM); -#endif - - if (comp == NULL) - return; - inst->psvi = comp; - comp->inst = inst; - - /* - * The full template resolution can be done statically - */ - prop = xsltGetCNsProp(style, inst, (const xmlChar *)"name", XSLT_NAMESPACE); - if (prop == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:param : name is missing\n"); - if (style != NULL) style->errors++; - } else { - const xmlChar *URI; - - URI = xsltGetQNameURI2(style, inst, &prop); - if (prop == NULL) { - if (style != NULL) style->errors++; - } else { - comp->name = prop; - comp->has_name = 1; - if (URI != NULL) { - comp->ns = xmlStrdup(URI); - comp->has_ns = 1; - } else { - comp->has_ns = 0; - } - } - } - - comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", - XSLT_NAMESPACE); - if (comp->select != NULL) { - comp->comp = xsltXPathCompile(style, comp->select); - if (comp->comp == NULL) { - xsltTransformError(NULL, style, inst, - "xsl:param : could not compile select expression '%s'\n", - comp->select); - if (style != NULL) style->errors++; - } - if (inst->children != NULL) { - xsltTransformError(NULL, style, inst, - "xsl:param : content should be empty since select is present \n"); - if (style != NULL) style->warnings++; - } - } -} - -/************************************************************************ - * * - * Generic interface * - * * - ************************************************************************/ - -/** - * xsltFreeStylePreComps: - * @style: an XSLT transformation context - * - * Free up the memory allocated by all precomputed blocks - */ -void -xsltFreeStylePreComps(xsltStylesheetPtr style) { - xsltElemPreCompPtr cur, next; - - if (style == NULL) - return; - - cur = style->preComps; - while (cur != NULL) { - next = cur->next; - if (cur->type == XSLT_FUNC_EXTENSION) - cur->free(cur); - else - xsltFreeStylePreComp((xsltStylePreCompPtr) cur); - cur = next; - } -} - -#ifdef XSLT_REFACTORED - -/** - * xsltStylePreCompute: - * @style: the XSLT stylesheet - * @node: the element in the XSLT namespace - * - * Precompute an XSLT element. - * This expects the type of the element to be already - * set in style->compCtxt->inode->type; - */ -void -xsltStylePreCompute(xsltStylesheetPtr style, xmlNodePtr node) { - /* - * The xsltXSLTElemMarker marker was set beforehand by - * the parsing mechanism for all elements in the XSLT namespace. - */ - if (style == NULL) { - if (node != NULL) - node->psvi = NULL; - return; - } - if (node == NULL) - return; - if (! IS_XSLT_ELEM_FAST(node)) - return; - - node->psvi = NULL; - if (XSLT_CCTXT(style)->inode->type != 0) { - switch (XSLT_CCTXT(style)->inode->type) { - case XSLT_FUNC_APPLYTEMPLATES: - xsltApplyTemplatesComp(style, node); - break; - case XSLT_FUNC_WITHPARAM: - xsltWithParamComp(style, node); - break; - case XSLT_FUNC_VALUEOF: - xsltValueOfComp(style, node); - break; - case XSLT_FUNC_COPY: - xsltCopyComp(style, node); - break; - case XSLT_FUNC_COPYOF: - xsltCopyOfComp(style, node); - break; - case XSLT_FUNC_IF: - xsltIfComp(style, node); - break; - case XSLT_FUNC_CHOOSE: - xsltChooseComp(style, node); - break; - case XSLT_FUNC_WHEN: - xsltWhenComp(style, node); - break; - case XSLT_FUNC_OTHERWISE: - /* NOP yet */ - return; - case XSLT_FUNC_FOREACH: - xsltForEachComp(style, node); - break; - case XSLT_FUNC_APPLYIMPORTS: - xsltApplyImportsComp(style, node); - break; - case XSLT_FUNC_ATTRIBUTE: - xsltAttributeComp(style, node); - break; - case XSLT_FUNC_ELEMENT: - xsltElementComp(style, node); - break; - case XSLT_FUNC_SORT: - xsltSortComp(style, node); - break; - case XSLT_FUNC_COMMENT: - xsltCommentComp(style, node); - break; - case XSLT_FUNC_NUMBER: - xsltNumberComp(style, node); - break; - case XSLT_FUNC_PI: - xsltProcessingInstructionComp(style, node); - break; - case XSLT_FUNC_CALLTEMPLATE: - xsltCallTemplateComp(style, node); - break; - case XSLT_FUNC_PARAM: - xsltParamComp(style, node); - break; - case XSLT_FUNC_VARIABLE: - xsltVariableComp(style, node); - break; - case XSLT_FUNC_FALLBACK: - /* NOP yet */ - return; - case XSLT_FUNC_DOCUMENT: - /* The extra one */ - node->psvi = (void *) xsltDocumentComp(style, node, - (xsltTransformFunction) xsltDocumentElem); - break; - case XSLT_FUNC_MESSAGE: - /* NOP yet */ - return; - default: - /* - * NOTE that xsl:text, xsl:template, xsl:stylesheet, - * xsl:transform, xsl:import, xsl:include are not expected - * to be handed over to this function. - */ - xsltTransformError(NULL, style, node, - "Internal error: (xsltStylePreCompute) cannot handle " - "the XSLT element '%s'.\n", node->name); - style->errors++; - return; - } - } else { - /* - * Fallback to string comparison. - */ - if (IS_XSLT_NAME(node, "apply-templates")) { - xsltApplyTemplatesComp(style, node); - } else if (IS_XSLT_NAME(node, "with-param")) { - xsltWithParamComp(style, node); - } else if (IS_XSLT_NAME(node, "value-of")) { - xsltValueOfComp(style, node); - } else if (IS_XSLT_NAME(node, "copy")) { - xsltCopyComp(style, node); - } else if (IS_XSLT_NAME(node, "copy-of")) { - xsltCopyOfComp(style, node); - } else if (IS_XSLT_NAME(node, "if")) { - xsltIfComp(style, node); - } else if (IS_XSLT_NAME(node, "choose")) { - xsltChooseComp(style, node); - } else if (IS_XSLT_NAME(node, "when")) { - xsltWhenComp(style, node); - } else if (IS_XSLT_NAME(node, "otherwise")) { - /* NOP yet */ - return; - } else if (IS_XSLT_NAME(node, "for-each")) { - xsltForEachComp(style, node); - } else if (IS_XSLT_NAME(node, "apply-imports")) { - xsltApplyImportsComp(style, node); - } else if (IS_XSLT_NAME(node, "attribute")) { - xsltAttributeComp(style, node); - } else if (IS_XSLT_NAME(node, "element")) { - xsltElementComp(style, node); - } else if (IS_XSLT_NAME(node, "sort")) { - xsltSortComp(style, node); - } else if (IS_XSLT_NAME(node, "comment")) { - xsltCommentComp(style, node); - } else if (IS_XSLT_NAME(node, "number")) { - xsltNumberComp(style, node); - } else if (IS_XSLT_NAME(node, "processing-instruction")) { - xsltProcessingInstructionComp(style, node); - } else if (IS_XSLT_NAME(node, "call-template")) { - xsltCallTemplateComp(style, node); - } else if (IS_XSLT_NAME(node, "param")) { - xsltParamComp(style, node); - } else if (IS_XSLT_NAME(node, "variable")) { - xsltVariableComp(style, node); - } else if (IS_XSLT_NAME(node, "fallback")) { - /* NOP yet */ - return; - } else if (IS_XSLT_NAME(node, "document")) { - /* The extra one */ - node->psvi = (void *) xsltDocumentComp(style, node, - (xsltTransformFunction) xsltDocumentElem); - } else if (IS_XSLT_NAME(node, "output")) { - /* Top-level */ - return; - } else if (IS_XSLT_NAME(node, "preserve-space")) { - /* Top-level */ - return; - } else if (IS_XSLT_NAME(node, "strip-space")) { - /* Top-level */ - return; - } else if (IS_XSLT_NAME(node, "key")) { - /* Top-level */ - return; - } else if (IS_XSLT_NAME(node, "message")) { - return; - } else if (IS_XSLT_NAME(node, "attribute-set")) { - /* Top-level */ - return; - } else if (IS_XSLT_NAME(node, "namespace-alias")) { - /* Top-level */ - return; - } else if (IS_XSLT_NAME(node, "decimal-format")) { - /* Top-level */ - return; - } else if (IS_XSLT_NAME(node, "include")) { - /* Top-level */ - } else { - /* - * NOTE that xsl:text, xsl:template, xsl:stylesheet, - * xsl:transform, xsl:import, xsl:include are not expected - * to be handed over to this function. - */ - xsltTransformError(NULL, style, node, - "Internal error: (xsltStylePreCompute) cannot handle " - "the XSLT element '%s'.\n", node->name); - style->errors++; - return; - } - } - /* - * Assign the current list of in-scope namespaces to the - * item. This is needed for XPath expressions. - */ - if (node->psvi != NULL) { - ((xsltStylePreCompPtr) node->psvi)->inScopeNs = - XSLT_CCTXT(style)->inode->inScopeNs; - } -} - -#else - -/** - * xsltStylePreCompute: - * @style: the XSLT stylesheet - * @inst: the instruction in the stylesheet - * - * Precompute an XSLT stylesheet element - */ -void -xsltStylePreCompute(xsltStylesheetPtr style, xmlNodePtr inst) { - /* - * URGENT TODO: Normally inst->psvi Should never be reserved here, - * BUT: since if we include the same stylesheet from - * multiple imports, then the stylesheet will be parsed - * again. We simply must not try to compute the stylesheet again. - * TODO: Get to the point where we don't need to query the - * namespace- and local-name of the node, but can evaluate this - * using cctxt->style->inode->category; - */ - if (inst->psvi != NULL) - return; - - if (IS_XSLT_ELEM(inst)) { - xsltStylePreCompPtr cur; - - if (IS_XSLT_NAME(inst, "apply-templates")) { - xsltCheckInstructionElement(style, inst); - xsltApplyTemplatesComp(style, inst); - } else if (IS_XSLT_NAME(inst, "with-param")) { - xsltCheckParentElement(style, inst, BAD_CAST "apply-templates", - BAD_CAST "call-template"); - xsltWithParamComp(style, inst); - } else if (IS_XSLT_NAME(inst, "value-of")) { - xsltCheckInstructionElement(style, inst); - xsltValueOfComp(style, inst); - } else if (IS_XSLT_NAME(inst, "copy")) { - xsltCheckInstructionElement(style, inst); - xsltCopyComp(style, inst); - } else if (IS_XSLT_NAME(inst, "copy-of")) { - xsltCheckInstructionElement(style, inst); - xsltCopyOfComp(style, inst); - } else if (IS_XSLT_NAME(inst, "if")) { - xsltCheckInstructionElement(style, inst); - xsltIfComp(style, inst); - } else if (IS_XSLT_NAME(inst, "when")) { - xsltCheckParentElement(style, inst, BAD_CAST "choose", NULL); - xsltWhenComp(style, inst); - } else if (IS_XSLT_NAME(inst, "choose")) { - xsltCheckInstructionElement(style, inst); - xsltChooseComp(style, inst); - } else if (IS_XSLT_NAME(inst, "for-each")) { - xsltCheckInstructionElement(style, inst); - xsltForEachComp(style, inst); - } else if (IS_XSLT_NAME(inst, "apply-imports")) { - xsltCheckInstructionElement(style, inst); - xsltApplyImportsComp(style, inst); - } else if (IS_XSLT_NAME(inst, "attribute")) { - xmlNodePtr parent = inst->parent; - - if ((parent == NULL) || (parent->ns == NULL) || - ((parent->ns != inst->ns) && - (!xmlStrEqual(parent->ns->href, inst->ns->href))) || - (!xmlStrEqual(parent->name, BAD_CAST "attribute-set"))) { - xsltCheckInstructionElement(style, inst); - } - xsltAttributeComp(style, inst); - } else if (IS_XSLT_NAME(inst, "element")) { - xsltCheckInstructionElement(style, inst); - xsltElementComp(style, inst); - } else if (IS_XSLT_NAME(inst, "text")) { - xsltCheckInstructionElement(style, inst); - xsltTextComp(style, inst); - } else if (IS_XSLT_NAME(inst, "sort")) { - xsltCheckParentElement(style, inst, BAD_CAST "apply-templates", - BAD_CAST "for-each"); - xsltSortComp(style, inst); - } else if (IS_XSLT_NAME(inst, "comment")) { - xsltCheckInstructionElement(style, inst); - xsltCommentComp(style, inst); - } else if (IS_XSLT_NAME(inst, "number")) { - xsltCheckInstructionElement(style, inst); - xsltNumberComp(style, inst); - } else if (IS_XSLT_NAME(inst, "processing-instruction")) { - xsltCheckInstructionElement(style, inst); - xsltProcessingInstructionComp(style, inst); - } else if (IS_XSLT_NAME(inst, "call-template")) { - xsltCheckInstructionElement(style, inst); - xsltCallTemplateComp(style, inst); - } else if (IS_XSLT_NAME(inst, "param")) { - if (xsltCheckTopLevelElement(style, inst, 0) == 0) - xsltCheckInstructionElement(style, inst); - xsltParamComp(style, inst); - } else if (IS_XSLT_NAME(inst, "variable")) { - if (xsltCheckTopLevelElement(style, inst, 0) == 0) - xsltCheckInstructionElement(style, inst); - xsltVariableComp(style, inst); - } else if (IS_XSLT_NAME(inst, "otherwise")) { - xsltCheckParentElement(style, inst, BAD_CAST "choose", NULL); - xsltCheckInstructionElement(style, inst); - return; - } else if (IS_XSLT_NAME(inst, "template")) { - xsltCheckTopLevelElement(style, inst, 1); - return; - } else if (IS_XSLT_NAME(inst, "output")) { - xsltCheckTopLevelElement(style, inst, 1); - return; - } else if (IS_XSLT_NAME(inst, "preserve-space")) { - xsltCheckTopLevelElement(style, inst, 1); - return; - } else if (IS_XSLT_NAME(inst, "strip-space")) { - xsltCheckTopLevelElement(style, inst, 1); - return; - } else if ((IS_XSLT_NAME(inst, "stylesheet")) || - (IS_XSLT_NAME(inst, "transform"))) { - xmlNodePtr parent = inst->parent; - - if ((parent == NULL) || (parent->type != XML_DOCUMENT_NODE)) { - xsltTransformError(NULL, style, inst, - "element %s only allowed only as root element\n", - inst->name); - style->errors++; - } - return; - } else if (IS_XSLT_NAME(inst, "key")) { - xsltCheckTopLevelElement(style, inst, 1); - return; - } else if (IS_XSLT_NAME(inst, "message")) { - xsltCheckInstructionElement(style, inst); - return; - } else if (IS_XSLT_NAME(inst, "attribute-set")) { - xsltCheckTopLevelElement(style, inst, 1); - return; - } else if (IS_XSLT_NAME(inst, "namespace-alias")) { - xsltCheckTopLevelElement(style, inst, 1); - return; - } else if (IS_XSLT_NAME(inst, "include")) { - xsltCheckTopLevelElement(style, inst, 1); - return; - } else if (IS_XSLT_NAME(inst, "import")) { - xsltCheckTopLevelElement(style, inst, 1); - return; - } else if (IS_XSLT_NAME(inst, "decimal-format")) { - xsltCheckTopLevelElement(style, inst, 1); - return; - } else if (IS_XSLT_NAME(inst, "fallback")) { - xsltCheckInstructionElement(style, inst); - return; - } else if (IS_XSLT_NAME(inst, "document")) { - xsltCheckInstructionElement(style, inst); - inst->psvi = (void *) xsltDocumentComp(style, inst, - (xsltTransformFunction) xsltDocumentElem); - } else { - xsltTransformError(NULL, style, inst, - "xsltStylePreCompute: unknown xsl:%s\n", inst->name); - if (style != NULL) style->warnings++; - } - - cur = (xsltStylePreCompPtr) inst->psvi; - /* - * A ns-list is build for every XSLT item in the - * node-tree. This is needed for XPath expressions. - */ - if (cur != NULL) { - int i = 0; - - cur->nsList = xmlGetNsList(inst->doc, inst); - if (cur->nsList != NULL) { - while (cur->nsList[i] != NULL) - i++; - } - cur->nsNr = i; - } - } else { - inst->psvi = - (void *) xsltPreComputeExtModuleElement(style, inst); - - /* - * Unknown element, maybe registered at the context - * level. Mark it for later recognition. - */ - if (inst->psvi == NULL) - inst->psvi = (void *) xsltExtMarker; - } -} -#endif /* XSLT_REFACTORED */ +/* + * preproc.c: Preprocessing of style operations + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include "xslt.h" +#include "xsltutils.h" +#include "xsltInternals.h" +#include "transform.h" +#include "templates.h" +#include "variables.h" +#include "numbersInternals.h" +#include "preproc.h" +#include "extra.h" +#include "imports.h" +#include "extensions.h" + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_PREPROC +#endif + +const xmlChar *xsltExtMarker = (const xmlChar *) "Extension Element"; + +/************************************************************************ + * * + * Grammar checks * + * * + ************************************************************************/ + +#ifdef XSLT_REFACTORED + /* + * Grammar checks are now performed in xslt.c. + */ +#else +/** + * xsltCheckTopLevelElement: + * @style: the XSLT stylesheet + * @inst: the XSLT instruction + * @err: raise an error or not + * + * Check that the instruction is instanciated as a top level element. + * + * Returns -1 in case of error, 0 if failed and 1 in case of success + */ +static int +xsltCheckTopLevelElement(xsltStylesheetPtr style, xmlNodePtr inst, int err) { + xmlNodePtr parent; + if ((style == NULL) || (inst == NULL) || (inst->ns == NULL)) + return(-1); + + parent = inst->parent; + if (parent == NULL) { + if (err) { + xsltTransformError(NULL, style, inst, + "internal problem: element has no parent\n"); + style->errors++; + } + return(0); + } + if ((parent->ns == NULL) || + ((parent->ns != inst->ns) && + (!xmlStrEqual(parent->ns->href, inst->ns->href))) || + ((!xmlStrEqual(parent->name, BAD_CAST "stylesheet")) && + (!xmlStrEqual(parent->name, BAD_CAST "transform")))) { + if (err) { + xsltTransformError(NULL, style, inst, + "element %s only allowed as child of stylesheet\n", + inst->name); + style->errors++; + } + return(0); + } + return(1); +} + +/** + * xsltCheckInstructionElement: + * @style: the XSLT stylesheet + * @inst: the XSLT instruction + * + * Check that the instruction is instanciated as an instruction element. + */ +static void +xsltCheckInstructionElement(xsltStylesheetPtr style, xmlNodePtr inst) { + xmlNodePtr parent; + int has_ext; + + if ((style == NULL) || (inst == NULL) || (inst->ns == NULL) || + (style->literal_result)) + return; + + has_ext = (style->extInfos != NULL); + + parent = inst->parent; + if (parent == NULL) { + xsltTransformError(NULL, style, inst, + "internal problem: element has no parent\n"); + style->errors++; + return; + } + while ((parent != NULL) && (parent->type != XML_DOCUMENT_NODE)) { + if (((parent->ns == inst->ns) || + ((parent->ns != NULL) && + (xmlStrEqual(parent->ns->href, inst->ns->href)))) && + ((xmlStrEqual(parent->name, BAD_CAST "template")) || + (xmlStrEqual(parent->name, BAD_CAST "param")) || + (xmlStrEqual(parent->name, BAD_CAST "attribute")) || + (xmlStrEqual(parent->name, BAD_CAST "variable")))) { + return; + } + + /* + * if we are within an extension element all bets are off + * about the semantic there e.g. xsl:param within func:function + */ + if ((has_ext) && (parent->ns != NULL) && + (xmlHashLookup(style->extInfos, parent->ns->href) != NULL)) + return; + + parent = parent->parent; + } + xsltTransformError(NULL, style, inst, + "element %s only allowed within a template, variable or param\n", + inst->name); + style->errors++; +} + +/** + * xsltCheckParentElement: + * @style: the XSLT stylesheet + * @inst: the XSLT instruction + * @allow1: allowed parent1 + * @allow2: allowed parent2 + * + * Check that the instruction is instanciated as the childre of one of the + * possible parents. + */ +static void +xsltCheckParentElement(xsltStylesheetPtr style, xmlNodePtr inst, + const xmlChar *allow1, const xmlChar *allow2) { + xmlNodePtr parent; + + if ((style == NULL) || (inst == NULL) || (inst->ns == NULL) || + (style->literal_result)) + return; + + parent = inst->parent; + if (parent == NULL) { + xsltTransformError(NULL, style, inst, + "internal problem: element has no parent\n"); + style->errors++; + return; + } + if (((parent->ns == inst->ns) || + ((parent->ns != NULL) && + (xmlStrEqual(parent->ns->href, inst->ns->href)))) && + ((xmlStrEqual(parent->name, allow1)) || + (xmlStrEqual(parent->name, allow2)))) { + return; + } + + if (style->extInfos != NULL) { + while ((parent != NULL) && (parent->type != XML_DOCUMENT_NODE)) { + /* + * if we are within an extension element all bets are off + * about the semantic there e.g. xsl:param within func:function + */ + if ((parent->ns != NULL) && + (xmlHashLookup(style->extInfos, parent->ns->href) != NULL)) + return; + + parent = parent->parent; + } + } + xsltTransformError(NULL, style, inst, + "element %s is not allowed within that context\n", + inst->name); + style->errors++; +} +#endif + +/************************************************************************ + * * + * handling of precomputed data * + * * + ************************************************************************/ + +/** + * xsltNewStylePreComp: + * @style: the XSLT stylesheet + * @type: the construct type + * + * Create a new XSLT Style precomputed block + * + * Returns the newly allocated specialized structure + * or NULL in case of error + */ +static xsltStylePreCompPtr +xsltNewStylePreComp(xsltStylesheetPtr style, xsltStyleType type) { + xsltStylePreCompPtr cur; +#ifdef XSLT_REFACTORED + size_t size; +#endif + + if (style == NULL) + return(NULL); + +#ifdef XSLT_REFACTORED + /* + * URGENT TODO: Use specialized factory functions in order + * to avoid this ugliness. + */ + switch (type) { + case XSLT_FUNC_COPY: + size = sizeof(xsltStyleItemCopy); break; + case XSLT_FUNC_SORT: + size = sizeof(xsltStyleItemSort); break; + case XSLT_FUNC_TEXT: + size = sizeof(xsltStyleItemText); break; + case XSLT_FUNC_ELEMENT: + size = sizeof(xsltStyleItemElement); break; + case XSLT_FUNC_ATTRIBUTE: + size = sizeof(xsltStyleItemAttribute); break; + case XSLT_FUNC_COMMENT: + size = sizeof(xsltStyleItemComment); break; + case XSLT_FUNC_PI: + size = sizeof(xsltStyleItemPI); break; + case XSLT_FUNC_COPYOF: + size = sizeof(xsltStyleItemCopyOf); break; + case XSLT_FUNC_VALUEOF: + size = sizeof(xsltStyleItemValueOf); break;; + case XSLT_FUNC_NUMBER: + size = sizeof(xsltStyleItemNumber); break; + case XSLT_FUNC_APPLYIMPORTS: + size = sizeof(xsltStyleItemApplyImports); break; + case XSLT_FUNC_CALLTEMPLATE: + size = sizeof(xsltStyleItemCallTemplate); break; + case XSLT_FUNC_APPLYTEMPLATES: + size = sizeof(xsltStyleItemApplyTemplates); break; + case XSLT_FUNC_CHOOSE: + size = sizeof(xsltStyleItemChoose); break; + case XSLT_FUNC_IF: + size = sizeof(xsltStyleItemIf); break; + case XSLT_FUNC_FOREACH: + size = sizeof(xsltStyleItemForEach); break; + case XSLT_FUNC_DOCUMENT: + size = sizeof(xsltStyleItemDocument); break; + case XSLT_FUNC_WITHPARAM: + size = sizeof(xsltStyleItemWithParam); break; + case XSLT_FUNC_PARAM: + size = sizeof(xsltStyleItemParam); break; + case XSLT_FUNC_VARIABLE: + size = sizeof(xsltStyleItemVariable); break; + case XSLT_FUNC_WHEN: + size = sizeof(xsltStyleItemWhen); break; + case XSLT_FUNC_OTHERWISE: + size = sizeof(xsltStyleItemOtherwise); break; + default: + xsltTransformError(NULL, style, NULL, + "xsltNewStylePreComp : invalid type %d\n", type); + style->errors++; + return(NULL); + } + /* + * Create the structure. + */ + cur = (xsltStylePreCompPtr) xmlMalloc(size); + if (cur == NULL) { + xsltTransformError(NULL, style, NULL, + "xsltNewStylePreComp : malloc failed\n"); + style->errors++; + return(NULL); + } + memset(cur, 0, size); + +#else /* XSLT_REFACTORED */ + /* + * Old behaviour. + */ + cur = (xsltStylePreCompPtr) xmlMalloc(sizeof(xsltStylePreComp)); + if (cur == NULL) { + xsltTransformError(NULL, style, NULL, + "xsltNewStylePreComp : malloc failed\n"); + style->errors++; + return(NULL); + } + memset(cur, 0, sizeof(xsltStylePreComp)); +#endif /* XSLT_REFACTORED */ + + /* + * URGENT TODO: Better to move this to spezialized factory functions. + */ + cur->type = type; + switch (cur->type) { + case XSLT_FUNC_COPY: + cur->func = (xsltTransformFunction) xsltCopy;break; + case XSLT_FUNC_SORT: + cur->func = (xsltTransformFunction) xsltSort;break; + case XSLT_FUNC_TEXT: + cur->func = (xsltTransformFunction) xsltText;break; + case XSLT_FUNC_ELEMENT: + cur->func = (xsltTransformFunction) xsltElement;break; + case XSLT_FUNC_ATTRIBUTE: + cur->func = (xsltTransformFunction) xsltAttribute;break; + case XSLT_FUNC_COMMENT: + cur->func = (xsltTransformFunction) xsltComment;break; + case XSLT_FUNC_PI: + cur->func = (xsltTransformFunction) xsltProcessingInstruction; + break; + case XSLT_FUNC_COPYOF: + cur->func = (xsltTransformFunction) xsltCopyOf;break; + case XSLT_FUNC_VALUEOF: + cur->func = (xsltTransformFunction) xsltValueOf;break; + case XSLT_FUNC_NUMBER: + cur->func = (xsltTransformFunction) xsltNumber;break; + case XSLT_FUNC_APPLYIMPORTS: + cur->func = (xsltTransformFunction) xsltApplyImports;break; + case XSLT_FUNC_CALLTEMPLATE: + cur->func = (xsltTransformFunction) xsltCallTemplate;break; + case XSLT_FUNC_APPLYTEMPLATES: + cur->func = (xsltTransformFunction) xsltApplyTemplates;break; + case XSLT_FUNC_CHOOSE: + cur->func = (xsltTransformFunction) xsltChoose;break; + case XSLT_FUNC_IF: + cur->func = (xsltTransformFunction) xsltIf;break; + case XSLT_FUNC_FOREACH: + cur->func = (xsltTransformFunction) xsltForEach;break; + case XSLT_FUNC_DOCUMENT: + cur->func = (xsltTransformFunction) xsltDocumentElem;break; + case XSLT_FUNC_WITHPARAM: + case XSLT_FUNC_PARAM: + case XSLT_FUNC_VARIABLE: + case XSLT_FUNC_WHEN: + break; + default: + if (cur->func == NULL) { + xsltTransformError(NULL, style, NULL, + "xsltNewStylePreComp : no function for type %d\n", type); + style->errors++; + } + } + cur->next = style->preComps; + style->preComps = (xsltElemPreCompPtr) cur; + + return(cur); +} + +/** + * xsltFreeStylePreComp: + * @comp: an XSLT Style precomputed block + * + * Free up the memory allocated by @comp + */ +static void +xsltFreeStylePreComp(xsltStylePreCompPtr comp) { + if (comp == NULL) + return; +#ifdef XSLT_REFACTORED + /* + * URGENT TODO: Implement destructors. + */ + switch (comp->type) { + case XSLT_FUNC_LITERAL_RESULT_ELEMENT: + break; + case XSLT_FUNC_COPY: + break; + case XSLT_FUNC_SORT: { + xsltStyleItemSortPtr item = (xsltStyleItemSortPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_TEXT: + break; + case XSLT_FUNC_ELEMENT: + break; + case XSLT_FUNC_ATTRIBUTE: + break; + case XSLT_FUNC_COMMENT: + break; + case XSLT_FUNC_PI: + break; + case XSLT_FUNC_COPYOF: { + xsltStyleItemCopyOfPtr item = (xsltStyleItemCopyOfPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_VALUEOF: { + xsltStyleItemValueOfPtr item = (xsltStyleItemValueOfPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_NUMBER: + break; + case XSLT_FUNC_APPLYIMPORTS: + break; + case XSLT_FUNC_CALLTEMPLATE: + break; + case XSLT_FUNC_APPLYTEMPLATES: { + xsltStyleItemApplyTemplatesPtr item = + (xsltStyleItemApplyTemplatesPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_CHOOSE: + break; + case XSLT_FUNC_IF: { + xsltStyleItemIfPtr item = (xsltStyleItemIfPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_FOREACH: { + xsltStyleItemForEachPtr item = + (xsltStyleItemForEachPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_DOCUMENT: + break; + case XSLT_FUNC_WITHPARAM: { + xsltStyleItemWithParamPtr item = + (xsltStyleItemWithParamPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_PARAM: { + xsltStyleItemParamPtr item = + (xsltStyleItemParamPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_VARIABLE: { + xsltStyleItemVariablePtr item = + (xsltStyleItemVariablePtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_WHEN: { + xsltStyleItemWhenPtr item = + (xsltStyleItemWhenPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_OTHERWISE: + case XSLT_FUNC_FALLBACK: + case XSLT_FUNC_MESSAGE: + case XSLT_FUNC_INCLUDE: + case XSLT_FUNC_ATTRSET: + + break; + default: + /* TODO: Raise error. */ + break; + } +#else + if (comp->comp != NULL) + xmlXPathFreeCompExpr(comp->comp); + if (comp->nsList != NULL) + xmlFree(comp->nsList); +#endif + + xmlFree(comp); +} + + +/************************************************************************ + * * + * XSLT-1.1 extensions * + * * + ************************************************************************/ + +/** + * xsltDocumentComp: + * @style: the XSLT stylesheet + * @inst: the instruction in the stylesheet + * @function: unused + * + * Pre process an XSLT-1.1 document element + * + * Returns a precompiled data structure for the element + */ +xsltElemPreCompPtr +xsltDocumentComp(xsltStylesheetPtr style, xmlNodePtr inst, + xsltTransformFunction function ATTRIBUTE_UNUSED) { +#ifdef XSLT_REFACTORED + xsltStyleItemDocumentPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + const xmlChar *filename = NULL; + + /* + * As of 2006-03-30, this function is currently defined in Libxslt + * to be used for: + * (in libxslt/extra.c) + * "output" in XSLT_SAXON_NAMESPACE + * "write" XSLT_XALAN_NAMESPACE + * "document" XSLT_XT_NAMESPACE + * "document" XSLT_NAMESPACE (from the abandoned old working + * draft of XSLT 1.1) + * (in libexslt/common.c) + * "document" in EXSLT_COMMON_NAMESPACE + */ +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemDocumentPtr) + xsltNewStylePreComp(style, XSLT_FUNC_DOCUMENT); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_DOCUMENT); +#endif + + if (comp == NULL) + return (NULL); + comp->inst = inst; + comp->ver11 = 0; + + if (xmlStrEqual(inst->name, (const xmlChar *) "output")) { +#ifdef WITH_XSLT_DEBUG_EXTRA + xsltGenericDebug(xsltGenericDebugContext, + "Found saxon:output extension\n"); +#endif + /* + * The element "output" is in the namespace XSLT_SAXON_NAMESPACE + * (http://icl.com/saxon) + * The @file is in no namespace; it is an AVT. + * (http://www.computerwizards.com/saxon/doc/extensions.html#saxon:output) + * + * TODO: Do we need not to check the namespace here? + */ + filename = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"file", + NULL, &comp->has_filename); + } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) { +#ifdef WITH_XSLT_DEBUG_EXTRA + xsltGenericDebug(xsltGenericDebugContext, + "Found xalan:write extension\n"); +#endif + /* the filename need to be interpreted */ + /* + * TODO: Is "filename need to be interpreted" meant to be a todo? + * Where will be the filename of xalan:write be processed? + * + * TODO: Do we need not to check the namespace here? + * The extension ns is "http://xml.apache.org/xalan/redirect". + * See http://xml.apache.org/xalan-j/extensionslib.html. + */ + } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) { + if (inst->ns != NULL) { + if (xmlStrEqual(inst->ns->href, XSLT_NAMESPACE)) { + /* + * Mark the instruction as being of + * XSLT version 1.1 (abandoned). + */ + comp->ver11 = 1; +#ifdef WITH_XSLT_DEBUG_EXTRA + xsltGenericDebug(xsltGenericDebugContext, + "Found xslt11:document construct\n"); +#endif + } else { + if (xmlStrEqual(inst->ns->href, + (const xmlChar *)"http://exslt.org/common")) { + /* EXSLT. */ +#ifdef WITH_XSLT_DEBUG_EXTRA + xsltGenericDebug(xsltGenericDebugContext, + "Found exslt:document extension\n"); +#endif + } else if (xmlStrEqual(inst->ns->href, XSLT_XT_NAMESPACE)) { + /* James Clark's XT. */ +#ifdef WITH_XSLT_DEBUG_EXTRA + xsltGenericDebug(xsltGenericDebugContext, + "Found xt:document extension\n"); +#endif + } + } + } + /* + * The element "document" is used in conjunction with the + * following namespaces: + * + * 1) XSLT_NAMESPACE (http://www.w3.org/1999/XSL/Transform version 1.1) + * + * + * TODO: is @href is an AVT? + * + * In all cases @href is in no namespace. + */ + filename = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"href", NULL, &comp->has_filename); + } + if (!comp->has_filename) { + goto error; + } + comp->filename = filename; + +error: + return ((xsltElemPreCompPtr) comp); +} + +/************************************************************************ + * * + * Most of the XSLT-1.0 transformations * + * * + ************************************************************************/ + +/** + * xsltSortComp: + * @style: the XSLT stylesheet + * @inst: the xslt sort node + * + * Process the xslt sort node on the source node + */ +static void +xsltSortComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemSortPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemSortPtr) xsltNewStylePreComp(style, XSLT_FUNC_SORT); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_SORT); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + comp->stype = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"data-type", + NULL, &comp->has_stype); + if (comp->stype != NULL) { + if (xmlStrEqual(comp->stype, (const xmlChar *) "text")) + comp->number = 0; + else if (xmlStrEqual(comp->stype, (const xmlChar *) "number")) + comp->number = 1; + else { + xsltTransformError(NULL, style, inst, + "xsltSortComp: no support for data-type = %s\n", comp->stype); + comp->number = 0; /* use default */ + if (style != NULL) style->warnings++; + } + } + comp->order = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"order", + NULL, &comp->has_order); + if (comp->order != NULL) { + if (xmlStrEqual(comp->order, (const xmlChar *) "ascending")) + comp->descending = 0; + else if (xmlStrEqual(comp->order, (const xmlChar *) "descending")) + comp->descending = 1; + else { + xsltTransformError(NULL, style, inst, + "xsltSortComp: invalid value %s for order\n", comp->order); + comp->descending = 0; /* use default */ + if (style != NULL) style->warnings++; + } + } + comp->case_order = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"case-order", + NULL, &comp->has_use); + if (comp->case_order != NULL) { + if (xmlStrEqual(comp->case_order, (const xmlChar *) "upper-first")) + comp->lower_first = 0; + else if (xmlStrEqual(comp->case_order, (const xmlChar *) "lower-first")) + comp->lower_first = 1; + else { + xsltTransformError(NULL, style, inst, + "xsltSortComp: invalid value %s for order\n", comp->order); + comp->lower_first = 0; /* use default */ + if (style != NULL) style->warnings++; + } + } + + comp->lang = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"lang", + NULL, &comp->has_lang); + + comp->select = xsltGetCNsProp(style, inst,(const xmlChar *)"select", XSLT_NAMESPACE); + if (comp->select == NULL) { + /* + * The default value of the select attribute is ., which will + * cause the string-value of the current node to be used as + * the sort key. + */ + comp->select = xmlDictLookup(style->dict, BAD_CAST ".", 1); + } + comp->comp = xsltXPathCompile(style, comp->select); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsltSortComp: could not compile select expression '%s'\n", + comp->select); + if (style != NULL) style->errors++; + } + if (inst->children != NULL) { + xsltTransformError(NULL, style, inst, + "xsl:sort : is not empty\n"); + if (style != NULL) style->errors++; + } +} + +/** + * xsltCopyComp: + * @style: the XSLT stylesheet + * @inst: the xslt copy node + * + * Process the xslt copy node on the source node + */ +static void +xsltCopyComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemCopyPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL)) + return; +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemCopyPtr) xsltNewStylePreComp(style, XSLT_FUNC_COPY); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_COPY); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + + comp->use = xsltGetCNsProp(style, inst, (const xmlChar *)"use-attribute-sets", + XSLT_NAMESPACE); + if (comp->use == NULL) + comp->has_use = 0; + else + comp->has_use = 1; +} + +#ifdef XSLT_REFACTORED + /* Enable if ever needed for xsl:text. */ +#else +/** + * xsltTextComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt text node + * + * TODO: This function is obsolete, since xsl:text won't + * be compiled, but removed from the tree. + * + * Process the xslt text node on the source node + */ +static void +xsltTextComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemTextPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + const xmlChar *prop; + + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemTextPtr) xsltNewStylePreComp(style, XSLT_FUNC_TEXT); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_TEXT); +#endif + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + comp->noescape = 0; + + prop = xsltGetCNsProp(style, inst, + (const xmlChar *)"disable-output-escaping", + XSLT_NAMESPACE); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *)"yes")) { + comp->noescape = 1; + } else if (!xmlStrEqual(prop, + (const xmlChar *)"no")){ + xsltTransformError(NULL, style, inst, + "xsl:text: disable-output-escaping allows only yes or no\n"); + if (style != NULL) style->warnings++; + } + } +} +#endif /* else of XSLT_REFACTORED */ + +/** + * xsltElementComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt element node + * + * Process the xslt element node on the source node + */ +static void +xsltElementComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemElementPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + /* + * + * + * + */ + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemElementPtr) xsltNewStylePreComp(style, XSLT_FUNC_ELEMENT); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_ELEMENT); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + /* + * Attribute "name". + */ + /* + * TODO: Precompile the AVT. See bug #344894. + */ + comp->name = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"name", NULL, &comp->has_name); + if (! comp->has_name) { + xsltTransformError(NULL, style, inst, + "xsl:element: The attribute 'name' is missing.\n"); + style->errors++; + goto error; + } + /* + * Attribute "namespace". + */ + /* + * TODO: Precompile the AVT. See bug #344894. + */ + comp->ns = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"namespace", NULL, &comp->has_ns); + + if (comp->name != NULL) { + if (xmlValidateQName(comp->name, 0)) { + xsltTransformError(NULL, style, inst, + "xsl:element: The value '%s' of the attribute 'name' is " + "not a valid QName.\n", comp->name); + style->errors++; + } else { + const xmlChar *prefix = NULL, *name; + + name = xsltSplitQName(style->dict, comp->name, &prefix); + if (comp->has_ns == 0) { + 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) { + comp->ns = xmlDictLookup(style->dict, ns->href, -1); + comp->has_ns = 1; +#ifdef XSLT_REFACTORED + comp->nsPrefix = prefix; + comp->name = name; +#endif + } else if (prefix != NULL) { + xsltTransformError(NULL, style, inst, + "xsl:element: The prefixed QName '%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", comp->name); + style->errors++; + } + } + if ((prefix != NULL) && + (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3))) + { + /* + * Mark is to be skipped. + */ + comp->has_name = 0; + } + } + } + /* + * Attribute "use-attribute-sets", + */ + comp->use = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"use-attribute-sets", + NULL, &comp->has_use); + +error: + return; +} + +/** + * xsltAttributeComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt attribute node + * + * Process the xslt attribute node on the source node + */ +static void +xsltAttributeComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemAttributePtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + /* + * + * + * + */ + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemAttributePtr) xsltNewStylePreComp(style, + XSLT_FUNC_ATTRIBUTE); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_ATTRIBUTE); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + /* + * Attribute "name". + */ + /* + * TODO: Precompile the AVT. See bug #344894. + */ + comp->name = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"name", + NULL, &comp->has_name); + if (! comp->has_name) { + xsltTransformError(NULL, style, inst, + "XSLT-attribute: The attribute 'name' is missing.\n"); + style->errors++; + return; + } + /* + * Attribute "namespace". + */ + /* + * TODO: Precompile the AVT. See bug #344894. + */ + comp->ns = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"namespace", + NULL, &comp->has_ns); + + if (comp->name != NULL) { + if (xmlValidateQName(comp->name, 0)) { + xsltTransformError(NULL, style, inst, + "xsl:attribute: The value '%s' of the attribute 'name' is " + "not a valid QName.\n", comp->name); + style->errors++; + } else { + const xmlChar *prefix = NULL, *name; + + name = xsltSplitQName(style->dict, comp->name, &prefix); + if (prefix != NULL) { + if (comp->has_ns == 0) { + 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) { + comp->ns = xmlDictLookup(style->dict, ns->href, -1); + comp->has_ns = 1; +#ifdef XSLT_REFACTORED + comp->nsPrefix = prefix; + comp->name = name; +#endif + } else { + xsltTransformError(NULL, style, inst, + "xsl:attribute: The prefixed QName '%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", comp->name); + style->errors++; + } + } + if (!xmlStrncasecmp(prefix, (xmlChar *) "xmlns", 5)) { + /* + * SPEC XSLT 1.0: + * "It is an error if the string that results from + * instantiating the attribute value template is not a + * QName or is the string xmlns. An XSLT processor may + * signal the error; if it does not signal the error, + * it must recover by not adding the attribute to the + * result tree." + * + * Reject a prefix of "xmlns". Mark to be skipped. + */ + comp->has_name = 0; + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltAttribute: xmlns prefix forbidden\n"); +#endif + return; + } + + } + } + } +} + +/** + * xsltCommentComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt comment node + * + * Process the xslt comment node on the source node + */ +static void +xsltCommentComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemCommentPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemCommentPtr) xsltNewStylePreComp(style, XSLT_FUNC_COMMENT); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_COMMENT); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; +} + +/** + * xsltProcessingInstructionComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt processing-instruction node + * + * Process the xslt processing-instruction node on the source node + */ +static void +xsltProcessingInstructionComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemPIPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemPIPtr) xsltNewStylePreComp(style, XSLT_FUNC_PI); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_PI); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + comp->name = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"name", + XSLT_NAMESPACE, &comp->has_name); +} + +/** + * xsltCopyOfComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt copy-of node + * + * Process the xslt copy-of node on the source node + */ +static void +xsltCopyOfComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemCopyOfPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemCopyOfPtr) xsltNewStylePreComp(style, XSLT_FUNC_COPYOF); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_COPYOF); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", + XSLT_NAMESPACE); + if (comp->select == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:copy-of : select is missing\n"); + if (style != NULL) style->errors++; + return; + } + comp->comp = xsltXPathCompile(style, comp->select); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:copy-of : could not compile select expression '%s'\n", + comp->select); + if (style != NULL) style->errors++; + } +} + +/** + * xsltValueOfComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt value-of node + * + * Process the xslt value-of node on the source node + */ +static void +xsltValueOfComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemValueOfPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + const xmlChar *prop; + + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemValueOfPtr) xsltNewStylePreComp(style, XSLT_FUNC_VALUEOF); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_VALUEOF); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + prop = xsltGetCNsProp(style, inst, + (const xmlChar *)"disable-output-escaping", + XSLT_NAMESPACE); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *)"yes")) { + comp->noescape = 1; + } else if (!xmlStrEqual(prop, + (const xmlChar *)"no")){ + xsltTransformError(NULL, style, inst, +"xsl:value-of : disable-output-escaping allows only yes or no\n"); + if (style != NULL) style->warnings++; + } + } + comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", + XSLT_NAMESPACE); + if (comp->select == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:value-of : select is missing\n"); + if (style != NULL) style->errors++; + return; + } + comp->comp = xsltXPathCompile(style, comp->select); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:value-of : could not compile select expression '%s'\n", + comp->select); + if (style != NULL) style->errors++; + } +} + +/** + * xsltWithParamComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt with-param node + * + * Process the xslt with-param node on the source node + * Allowed parents: xsl:call-template, xsl:apply-templates. + * + * + * + */ +static void +xsltWithParamComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemWithParamPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + const xmlChar *prop; + + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemWithParamPtr) xsltNewStylePreComp(style, XSLT_FUNC_WITHPARAM); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_WITHPARAM); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + /* + * The full namespace resolution can be done statically + */ + prop = xsltGetCNsProp(style, inst, (const xmlChar *)"name", XSLT_NAMESPACE); + if (prop == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:with-param : name is missing\n"); + if (style != NULL) style->errors++; + } else { + const xmlChar *URI; + + /* + * @prop will be in the string dict afterwards, @URI not. + */ + URI = xsltGetQNameURI2(style, inst, &prop); + if (prop == NULL) { + if (style != NULL) style->errors++; + } else { + comp->name = prop; + comp->has_name = 1; + if (URI != NULL) { + /* + * Fixes bug #308441: Put the ns-name in the dict + * in order to pointer compare names during XPath's + * variable lookup. + */ + comp->ns = xmlDictLookup(style->dict, URI, -1); + comp->has_ns = 1; + } else { + comp->has_ns = 0; + } + } + } + + comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", + XSLT_NAMESPACE); + if (comp->select != NULL) { + comp->comp = xsltXPathCompile(style, comp->select); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:param : could not compile select expression '%s'\n", + comp->select); + if (style != NULL) style->errors++; + } + if (inst->children != NULL) { + xsltTransformError(NULL, style, inst, + "xsl:param : content should be empty since select is present \n"); + if (style != NULL) style->warnings++; + } + } +} + +/** + * xsltNumberComp: + * @style: an XSLT compiled stylesheet + * @cur: the xslt number node + * + * Process the xslt number node on the source node + */ +static void +xsltNumberComp(xsltStylesheetPtr style, xmlNodePtr cur) { +#ifdef XSLT_REFACTORED + xsltStyleItemNumberPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + const xmlChar *prop; + + if ((style == NULL) || (cur == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemNumberPtr) xsltNewStylePreComp(style, XSLT_FUNC_NUMBER); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_NUMBER); +#endif + + if (comp == NULL) + return; + cur->psvi = comp; + + if ((style == NULL) || (cur == NULL)) + return; + + comp->numdata.doc = cur->doc; + comp->numdata.node = cur; + comp->numdata.value = xsltGetCNsProp(style, cur, (const xmlChar *)"value", + XSLT_NAMESPACE); + + prop = xsltEvalStaticAttrValueTemplate(style, cur, + (const xmlChar *)"format", + XSLT_NAMESPACE, &comp->numdata.has_format); + if (comp->numdata.has_format == 0) { + comp->numdata.format = xmlDictLookup(style->dict, BAD_CAST "" , 0); + } else { + comp->numdata.format = prop; + } + + comp->numdata.count = xsltGetCNsProp(style, cur, (const xmlChar *)"count", + XSLT_NAMESPACE); + comp->numdata.from = xsltGetCNsProp(style, cur, (const xmlChar *)"from", + XSLT_NAMESPACE); + + prop = xsltGetCNsProp(style, cur, (const xmlChar *)"level", XSLT_NAMESPACE); + if (prop != NULL) { + if (xmlStrEqual(prop, BAD_CAST("single")) || + xmlStrEqual(prop, BAD_CAST("multiple")) || + xmlStrEqual(prop, BAD_CAST("any"))) { + comp->numdata.level = prop; + } else { + xsltTransformError(NULL, style, cur, + "xsl:number : invalid value %s for level\n", prop); + if (style != NULL) style->warnings++; + xmlFree((void *)(prop)); + } + } + + prop = xsltGetCNsProp(style, cur, (const xmlChar *)"lang", XSLT_NAMESPACE); + if (prop != NULL) { + XSLT_TODO; /* xsl:number lang attribute */ + xmlFree((void *)prop); + } + + prop = xsltGetCNsProp(style, cur, (const xmlChar *)"letter-value", XSLT_NAMESPACE); + if (prop != NULL) { + if (xmlStrEqual(prop, BAD_CAST("alphabetic"))) { + xsltTransformError(NULL, style, cur, + "xsl:number : letter-value 'alphabetic' not implemented\n"); + if (style != NULL) style->warnings++; + XSLT_TODO; /* xsl:number letter-value attribute alphabetic */ + } else if (xmlStrEqual(prop, BAD_CAST("traditional"))) { + xsltTransformError(NULL, style, cur, + "xsl:number : letter-value 'traditional' not implemented\n"); + if (style != NULL) style->warnings++; + XSLT_TODO; /* xsl:number letter-value attribute traditional */ + } else { + xsltTransformError(NULL, style, cur, + "xsl:number : invalid value %s for letter-value\n", prop); + if (style != NULL) style->warnings++; + } + } + + prop = xsltGetCNsProp(style, cur, (const xmlChar *)"grouping-separator", + XSLT_NAMESPACE); + if (prop != NULL) { + comp->numdata.groupingCharacterLen = xmlStrlen(prop); + comp->numdata.groupingCharacter = + xsltGetUTF8Char(prop, &(comp->numdata.groupingCharacterLen)); + } + + prop = xsltGetCNsProp(style, cur, (const xmlChar *)"grouping-size", XSLT_NAMESPACE); + if (prop != NULL) { + sscanf((char *)prop, "%d", &comp->numdata.digitsPerGroup); + } else { + comp->numdata.groupingCharacter = 0; + } + + /* Set default values */ + if (comp->numdata.value == NULL) { + if (comp->numdata.level == NULL) { + comp->numdata.level = xmlDictLookup(style->dict, + BAD_CAST"single", 6); + } + } + +} + +/** + * xsltApplyImportsComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt apply-imports node + * + * Process the xslt apply-imports node on the source node + */ +static void +xsltApplyImportsComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemApplyImportsPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemApplyImportsPtr) xsltNewStylePreComp(style, XSLT_FUNC_APPLYIMPORTS); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_APPLYIMPORTS); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; +} + +/** + * xsltCallTemplateComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt call-template node + * + * Process the xslt call-template node on the source node + */ +static void +xsltCallTemplateComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemCallTemplatePtr comp; +#else + xsltStylePreCompPtr comp; +#endif + const xmlChar *prop; + + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemCallTemplatePtr) + xsltNewStylePreComp(style, XSLT_FUNC_CALLTEMPLATE); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_CALLTEMPLATE); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + /* + * The full template resolution can be done statically + */ + prop = xsltGetCNsProp(style, inst, (const xmlChar *)"name", XSLT_NAMESPACE); + if (prop == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:call-template : name is missing\n"); + if (style != NULL) style->errors++; + } else { + const xmlChar *URI; + + URI = xsltGetQNameURI2(style, inst, &prop); + if (prop == NULL) { + if (style != NULL) style->errors++; + } else { + comp->name = prop; + comp->has_name = 1; + if (URI != NULL) { + comp->ns = xmlDictLookup(style->dict, URI, -1); + comp->has_ns = 1; + } else { + comp->has_ns = 0; + } + } + comp->templ = NULL; + } +} + +/** + * xsltApplyTemplatesComp: + * @style: an XSLT compiled stylesheet + * @inst: the apply-templates node + * + * Process the apply-templates node on the source node + */ +static void +xsltApplyTemplatesComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemApplyTemplatesPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + const xmlChar *prop; + + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemApplyTemplatesPtr) + xsltNewStylePreComp(style, XSLT_FUNC_APPLYTEMPLATES); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_APPLYTEMPLATES); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + /* + * Get mode if any + */ + prop = xsltGetCNsProp(style, inst, (const xmlChar *)"mode", XSLT_NAMESPACE); + if (prop != NULL) { + const xmlChar *URI; + + URI = xsltGetQNameURI2(style, inst, &prop); + if (prop == NULL) { + if (style != NULL) style->errors++; + } else { + comp->mode = xmlDictLookup(style->dict, prop, -1); + if (URI != NULL) { + comp->modeURI = xmlDictLookup(style->dict, URI, -1); + } else { + comp->modeURI = NULL; + } + } + } + comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", + XSLT_NAMESPACE); + if (comp->select != NULL) { + comp->comp = xsltXPathCompile(style, comp->select); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:apply-templates : could not compile select expression '%s'\n", + comp->select); + if (style != NULL) style->errors++; + } + } + + /* TODO: handle (or skip) the xsl:sort and xsl:with-param */ +} + +/** + * xsltChooseComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt choose node + * + * Process the xslt choose node on the source node + */ +static void +xsltChooseComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemChoosePtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemChoosePtr) + xsltNewStylePreComp(style, XSLT_FUNC_CHOOSE); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_CHOOSE); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; +} + +/** + * xsltIfComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt if node + * + * Process the xslt if node on the source node + */ +static void +xsltIfComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemIfPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemIfPtr) + xsltNewStylePreComp(style, XSLT_FUNC_IF); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_IF); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + comp->test = xsltGetCNsProp(style, inst, (const xmlChar *)"test", XSLT_NAMESPACE); + if (comp->test == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:if : test is not defined\n"); + if (style != NULL) style->errors++; + return; + } + comp->comp = xsltXPathCompile(style, comp->test); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:if : could not compile test expression '%s'\n", + comp->test); + if (style != NULL) style->errors++; + } +} + +/** + * xsltWhenComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt if node + * + * Process the xslt if node on the source node + */ +static void +xsltWhenComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemWhenPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemWhenPtr) + xsltNewStylePreComp(style, XSLT_FUNC_WHEN); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_WHEN); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + comp->test = xsltGetCNsProp(style, inst, (const xmlChar *)"test", XSLT_NAMESPACE); + if (comp->test == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:when : test is not defined\n"); + if (style != NULL) style->errors++; + return; + } + comp->comp = xsltXPathCompile(style, comp->test); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:when : could not compile test expression '%s'\n", + comp->test); + if (style != NULL) style->errors++; + } +} + +/** + * xsltForEachComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt for-each node + * + * Process the xslt for-each node on the source node + */ +static void +xsltForEachComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemForEachPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemForEachPtr) + xsltNewStylePreComp(style, XSLT_FUNC_FOREACH); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_FOREACH); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", + XSLT_NAMESPACE); + if (comp->select == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:for-each : select is missing\n"); + if (style != NULL) style->errors++; + } else { + comp->comp = xsltXPathCompile(style, comp->select); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:for-each : could not compile select expression '%s'\n", + comp->select); + if (style != NULL) style->errors++; + } + } + /* TODO: handle and skip the xsl:sort */ +} + +/** + * xsltVariableComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt variable node + * + * Process the xslt variable node on the source node + */ +static void +xsltVariableComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemVariablePtr comp; +#else + xsltStylePreCompPtr comp; +#endif + const xmlChar *prop; + const xmlChar *URI; + + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemVariablePtr) + xsltNewStylePreComp(style, XSLT_FUNC_VARIABLE); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_VARIABLE); +#endif + + if (comp == NULL) + return; + + inst->psvi = comp; + comp->inst = inst; + /* + * The full template resolution can be done statically + */ + + /* + * Attribute "name". + */ + prop = xsltGetCNsProp(style, inst, (const xmlChar *)"name", XSLT_NAMESPACE); + if (prop == NULL) { + xsltTransformError(NULL, style, inst, + "XSLT-variable: The attribute 'name' is missing.\n"); + style->errors++; + goto error; + } + if (xmlValidateQName(prop, 0)) { + xsltTransformError(NULL, style, inst, + "XSLT-variable: The value '%s' of the attribute 'name' is " + "not a valid QName.\n", prop); + style->errors++; + goto error; + } + URI = xsltGetQNameURI2(style, inst, &prop); + if (prop == NULL) { + goto error; + } + comp->name = prop; + comp->has_name = 1; + + if (URI != NULL) { + comp->ns = xmlDictLookup(style->dict, URI, -1); + comp->has_ns = 1; + } else { + comp->has_ns = 0; + } + /* + * Attribute "select". + */ + comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", + XSLT_NAMESPACE); + if (comp->select != NULL) { + comp->comp = xsltXPathCompile(style, comp->select); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "XSLT-variable: Failed to compile the XPath expression '%s'.\n", + comp->select); + style->errors++; + } + if (inst->children != NULL) { + xsltTransformError(NULL, style, inst, + "XSLT-variable: The must be no child nodes, since the " + "attribute 'select' was specified.\n"); + style->errors++; + } + } +error: + return; +} + +/** + * xsltParamComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt param node + * + * Process the xslt param node on the source node + */ +static void +xsltParamComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemParamPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + const xmlChar *prop; + + if ((style == NULL) || (inst == NULL)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemParamPtr) + xsltNewStylePreComp(style, XSLT_FUNC_PARAM); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_PARAM); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + /* + * The full template resolution can be done statically + */ + prop = xsltGetCNsProp(style, inst, (const xmlChar *)"name", XSLT_NAMESPACE); + if (prop == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:param : name is missing\n"); + if (style != NULL) style->errors++; + } else { + const xmlChar *URI; + + URI = xsltGetQNameURI2(style, inst, &prop); + if (prop == NULL) { + if (style != NULL) style->errors++; + } else { + comp->name = prop; + comp->has_name = 1; + if (URI != NULL) { + comp->ns = xmlDictLookup(style->dict, URI, -1); + comp->has_ns = 1; + } else { + comp->has_ns = 0; + } + } + } + + comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", + XSLT_NAMESPACE); + if (comp->select != NULL) { + comp->comp = xsltXPathCompile(style, comp->select); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:param : could not compile select expression '%s'\n", + comp->select); + if (style != NULL) style->errors++; + } + if (inst->children != NULL) { + xsltTransformError(NULL, style, inst, + "xsl:param : content should be empty since select is present \n"); + if (style != NULL) style->warnings++; + } + } +} + +/************************************************************************ + * * + * Generic interface * + * * + ************************************************************************/ + +/** + * xsltFreeStylePreComps: + * @style: an XSLT transformation context + * + * Free up the memory allocated by all precomputed blocks + */ +void +xsltFreeStylePreComps(xsltStylesheetPtr style) { + xsltElemPreCompPtr cur, next; + + if (style == NULL) + return; + + cur = style->preComps; + while (cur != NULL) { + next = cur->next; + if (cur->type == XSLT_FUNC_EXTENSION) + cur->free(cur); + else + xsltFreeStylePreComp((xsltStylePreCompPtr) cur); + cur = next; + } +} + +#ifdef XSLT_REFACTORED + +/** + * xsltStylePreCompute: + * @style: the XSLT stylesheet + * @node: the element in the XSLT namespace + * + * Precompute an XSLT element. + * This expects the type of the element to be already + * set in style->compCtxt->inode->type; + */ +void +xsltStylePreCompute(xsltStylesheetPtr style, xmlNodePtr node) { + /* + * The xsltXSLTElemMarker marker was set beforehand by + * the parsing mechanism for all elements in the XSLT namespace. + */ + if (style == NULL) { + if (node != NULL) + node->psvi = NULL; + return; + } + if (node == NULL) + return; + if (! IS_XSLT_ELEM_FAST(node)) + return; + + node->psvi = NULL; + if (XSLT_CCTXT(style)->inode->type != 0) { + switch (XSLT_CCTXT(style)->inode->type) { + case XSLT_FUNC_APPLYTEMPLATES: + xsltApplyTemplatesComp(style, node); + break; + case XSLT_FUNC_WITHPARAM: + xsltWithParamComp(style, node); + break; + case XSLT_FUNC_VALUEOF: + xsltValueOfComp(style, node); + break; + case XSLT_FUNC_COPY: + xsltCopyComp(style, node); + break; + case XSLT_FUNC_COPYOF: + xsltCopyOfComp(style, node); + break; + case XSLT_FUNC_IF: + xsltIfComp(style, node); + break; + case XSLT_FUNC_CHOOSE: + xsltChooseComp(style, node); + break; + case XSLT_FUNC_WHEN: + xsltWhenComp(style, node); + break; + case XSLT_FUNC_OTHERWISE: + /* NOP yet */ + return; + case XSLT_FUNC_FOREACH: + xsltForEachComp(style, node); + break; + case XSLT_FUNC_APPLYIMPORTS: + xsltApplyImportsComp(style, node); + break; + case XSLT_FUNC_ATTRIBUTE: + xsltAttributeComp(style, node); + break; + case XSLT_FUNC_ELEMENT: + xsltElementComp(style, node); + break; + case XSLT_FUNC_SORT: + xsltSortComp(style, node); + break; + case XSLT_FUNC_COMMENT: + xsltCommentComp(style, node); + break; + case XSLT_FUNC_NUMBER: + xsltNumberComp(style, node); + break; + case XSLT_FUNC_PI: + xsltProcessingInstructionComp(style, node); + break; + case XSLT_FUNC_CALLTEMPLATE: + xsltCallTemplateComp(style, node); + break; + case XSLT_FUNC_PARAM: + xsltParamComp(style, node); + break; + case XSLT_FUNC_VARIABLE: + xsltVariableComp(style, node); + break; + case XSLT_FUNC_FALLBACK: + /* NOP yet */ + return; + case XSLT_FUNC_DOCUMENT: + /* The extra one */ + node->psvi = (void *) xsltDocumentComp(style, node, + (xsltTransformFunction) xsltDocumentElem); + break; + case XSLT_FUNC_MESSAGE: + /* NOP yet */ + return; + default: + /* + * NOTE that xsl:text, xsl:template, xsl:stylesheet, + * xsl:transform, xsl:import, xsl:include are not expected + * to be handed over to this function. + */ + xsltTransformError(NULL, style, node, + "Internal error: (xsltStylePreCompute) cannot handle " + "the XSLT element '%s'.\n", node->name); + style->errors++; + return; + } + } else { + /* + * Fallback to string comparison. + */ + if (IS_XSLT_NAME(node, "apply-templates")) { + xsltApplyTemplatesComp(style, node); + } else if (IS_XSLT_NAME(node, "with-param")) { + xsltWithParamComp(style, node); + } else if (IS_XSLT_NAME(node, "value-of")) { + xsltValueOfComp(style, node); + } else if (IS_XSLT_NAME(node, "copy")) { + xsltCopyComp(style, node); + } else if (IS_XSLT_NAME(node, "copy-of")) { + xsltCopyOfComp(style, node); + } else if (IS_XSLT_NAME(node, "if")) { + xsltIfComp(style, node); + } else if (IS_XSLT_NAME(node, "choose")) { + xsltChooseComp(style, node); + } else if (IS_XSLT_NAME(node, "when")) { + xsltWhenComp(style, node); + } else if (IS_XSLT_NAME(node, "otherwise")) { + /* NOP yet */ + return; + } else if (IS_XSLT_NAME(node, "for-each")) { + xsltForEachComp(style, node); + } else if (IS_XSLT_NAME(node, "apply-imports")) { + xsltApplyImportsComp(style, node); + } else if (IS_XSLT_NAME(node, "attribute")) { + xsltAttributeComp(style, node); + } else if (IS_XSLT_NAME(node, "element")) { + xsltElementComp(style, node); + } else if (IS_XSLT_NAME(node, "sort")) { + xsltSortComp(style, node); + } else if (IS_XSLT_NAME(node, "comment")) { + xsltCommentComp(style, node); + } else if (IS_XSLT_NAME(node, "number")) { + xsltNumberComp(style, node); + } else if (IS_XSLT_NAME(node, "processing-instruction")) { + xsltProcessingInstructionComp(style, node); + } else if (IS_XSLT_NAME(node, "call-template")) { + xsltCallTemplateComp(style, node); + } else if (IS_XSLT_NAME(node, "param")) { + xsltParamComp(style, node); + } else if (IS_XSLT_NAME(node, "variable")) { + xsltVariableComp(style, node); + } else if (IS_XSLT_NAME(node, "fallback")) { + /* NOP yet */ + return; + } else if (IS_XSLT_NAME(node, "document")) { + /* The extra one */ + node->psvi = (void *) xsltDocumentComp(style, node, + (xsltTransformFunction) xsltDocumentElem); + } else if (IS_XSLT_NAME(node, "output")) { + /* Top-level */ + return; + } else if (IS_XSLT_NAME(node, "preserve-space")) { + /* Top-level */ + return; + } else if (IS_XSLT_NAME(node, "strip-space")) { + /* Top-level */ + return; + } else if (IS_XSLT_NAME(node, "key")) { + /* Top-level */ + return; + } else if (IS_XSLT_NAME(node, "message")) { + return; + } else if (IS_XSLT_NAME(node, "attribute-set")) { + /* Top-level */ + return; + } else if (IS_XSLT_NAME(node, "namespace-alias")) { + /* Top-level */ + return; + } else if (IS_XSLT_NAME(node, "decimal-format")) { + /* Top-level */ + return; + } else if (IS_XSLT_NAME(node, "include")) { + /* Top-level */ + } else { + /* + * NOTE that xsl:text, xsl:template, xsl:stylesheet, + * xsl:transform, xsl:import, xsl:include are not expected + * to be handed over to this function. + */ + xsltTransformError(NULL, style, node, + "Internal error: (xsltStylePreCompute) cannot handle " + "the XSLT element '%s'.\n", node->name); + style->errors++; + return; + } + } + /* + * Assign the current list of in-scope namespaces to the + * item. This is needed for XPath expressions. + */ + if (node->psvi != NULL) { + ((xsltStylePreCompPtr) node->psvi)->inScopeNs = + XSLT_CCTXT(style)->inode->inScopeNs; + } +} + +#else + +/** + * xsltStylePreCompute: + * @style: the XSLT stylesheet + * @inst: the instruction in the stylesheet + * + * Precompute an XSLT stylesheet element + */ +void +xsltStylePreCompute(xsltStylesheetPtr style, xmlNodePtr inst) { + /* + * URGENT TODO: Normally inst->psvi Should never be reserved here, + * BUT: since if we include the same stylesheet from + * multiple imports, then the stylesheet will be parsed + * again. We simply must not try to compute the stylesheet again. + * TODO: Get to the point where we don't need to query the + * namespace- and local-name of the node, but can evaluate this + * using cctxt->style->inode->category; + */ + if (inst->psvi != NULL) + return; + + if (IS_XSLT_ELEM(inst)) { + xsltStylePreCompPtr cur; + + if (IS_XSLT_NAME(inst, "apply-templates")) { + xsltCheckInstructionElement(style, inst); + xsltApplyTemplatesComp(style, inst); + } else if (IS_XSLT_NAME(inst, "with-param")) { + xsltCheckParentElement(style, inst, BAD_CAST "apply-templates", + BAD_CAST "call-template"); + xsltWithParamComp(style, inst); + } else if (IS_XSLT_NAME(inst, "value-of")) { + xsltCheckInstructionElement(style, inst); + xsltValueOfComp(style, inst); + } else if (IS_XSLT_NAME(inst, "copy")) { + xsltCheckInstructionElement(style, inst); + xsltCopyComp(style, inst); + } else if (IS_XSLT_NAME(inst, "copy-of")) { + xsltCheckInstructionElement(style, inst); + xsltCopyOfComp(style, inst); + } else if (IS_XSLT_NAME(inst, "if")) { + xsltCheckInstructionElement(style, inst); + xsltIfComp(style, inst); + } else if (IS_XSLT_NAME(inst, "when")) { + xsltCheckParentElement(style, inst, BAD_CAST "choose", NULL); + xsltWhenComp(style, inst); + } else if (IS_XSLT_NAME(inst, "choose")) { + xsltCheckInstructionElement(style, inst); + xsltChooseComp(style, inst); + } else if (IS_XSLT_NAME(inst, "for-each")) { + xsltCheckInstructionElement(style, inst); + xsltForEachComp(style, inst); + } else if (IS_XSLT_NAME(inst, "apply-imports")) { + xsltCheckInstructionElement(style, inst); + xsltApplyImportsComp(style, inst); + } else if (IS_XSLT_NAME(inst, "attribute")) { + xmlNodePtr parent = inst->parent; + + if ((parent == NULL) || (parent->ns == NULL) || + ((parent->ns != inst->ns) && + (!xmlStrEqual(parent->ns->href, inst->ns->href))) || + (!xmlStrEqual(parent->name, BAD_CAST "attribute-set"))) { + xsltCheckInstructionElement(style, inst); + } + xsltAttributeComp(style, inst); + } else if (IS_XSLT_NAME(inst, "element")) { + xsltCheckInstructionElement(style, inst); + xsltElementComp(style, inst); + } else if (IS_XSLT_NAME(inst, "text")) { + xsltCheckInstructionElement(style, inst); + xsltTextComp(style, inst); + } else if (IS_XSLT_NAME(inst, "sort")) { + xsltCheckParentElement(style, inst, BAD_CAST "apply-templates", + BAD_CAST "for-each"); + xsltSortComp(style, inst); + } else if (IS_XSLT_NAME(inst, "comment")) { + xsltCheckInstructionElement(style, inst); + xsltCommentComp(style, inst); + } else if (IS_XSLT_NAME(inst, "number")) { + xsltCheckInstructionElement(style, inst); + xsltNumberComp(style, inst); + } else if (IS_XSLT_NAME(inst, "processing-instruction")) { + xsltCheckInstructionElement(style, inst); + xsltProcessingInstructionComp(style, inst); + } else if (IS_XSLT_NAME(inst, "call-template")) { + xsltCheckInstructionElement(style, inst); + xsltCallTemplateComp(style, inst); + } else if (IS_XSLT_NAME(inst, "param")) { + if (xsltCheckTopLevelElement(style, inst, 0) == 0) + xsltCheckInstructionElement(style, inst); + xsltParamComp(style, inst); + } else if (IS_XSLT_NAME(inst, "variable")) { + if (xsltCheckTopLevelElement(style, inst, 0) == 0) + xsltCheckInstructionElement(style, inst); + xsltVariableComp(style, inst); + } else if (IS_XSLT_NAME(inst, "otherwise")) { + xsltCheckParentElement(style, inst, BAD_CAST "choose", NULL); + xsltCheckInstructionElement(style, inst); + return; + } else if (IS_XSLT_NAME(inst, "template")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "output")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "preserve-space")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "strip-space")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if ((IS_XSLT_NAME(inst, "stylesheet")) || + (IS_XSLT_NAME(inst, "transform"))) { + xmlNodePtr parent = inst->parent; + + if ((parent == NULL) || (parent->type != XML_DOCUMENT_NODE)) { + xsltTransformError(NULL, style, inst, + "element %s only allowed only as root element\n", + inst->name); + style->errors++; + } + return; + } else if (IS_XSLT_NAME(inst, "key")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "message")) { + xsltCheckInstructionElement(style, inst); + return; + } else if (IS_XSLT_NAME(inst, "attribute-set")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "namespace-alias")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "include")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "import")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "decimal-format")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "fallback")) { + xsltCheckInstructionElement(style, inst); + return; + } else if (IS_XSLT_NAME(inst, "document")) { + xsltCheckInstructionElement(style, inst); + inst->psvi = (void *) xsltDocumentComp(style, inst, + (xsltTransformFunction) xsltDocumentElem); + } else { + xsltTransformError(NULL, style, inst, + "xsltStylePreCompute: unknown xsl:%s\n", inst->name); + if (style != NULL) style->warnings++; + } + + cur = (xsltStylePreCompPtr) inst->psvi; + /* + * A ns-list is build for every XSLT item in the + * node-tree. This is needed for XPath expressions. + */ + if (cur != NULL) { + int i = 0; + + cur->nsList = xmlGetNsList(inst->doc, inst); + if (cur->nsList != NULL) { + while (cur->nsList[i] != NULL) + i++; + } + cur->nsNr = i; + } + } else { + inst->psvi = + (void *) xsltPreComputeExtModuleElement(style, inst); + + /* + * Unknown element, maybe registered at the context + * level. Mark it for later recognition. + */ + if (inst->psvi == NULL) + inst->psvi = (void *) xsltExtMarker; + } +} +#endif /* XSLT_REFACTORED */ diff --git a/libxslt/templates.c b/libxslt/templates.c index c20e287a..1d82852f 100644 --- a/libxslt/templates.c +++ b/libxslt/templates.c @@ -1,818 +1,820 @@ -/* - * templates.c: Implementation of the template processing - * - * Reference: - * http://www.w3.org/TR/1999/REC-xslt-19991116 - * - * See Copyright for the status of this software. - * - * daniel@veillard.com - */ - -#define IN_LIBXSLT -#include "libxslt.h" - -#include - -#include -#include -#include -#include -#include -#include -#include "xslt.h" -#include "xsltInternals.h" -#include "xsltutils.h" -#include "variables.h" -#include "functions.h" -#include "templates.h" -#include "transform.h" -#include "namespaces.h" -#include "attributes.h" - -#ifdef WITH_XSLT_DEBUG -#define WITH_XSLT_DEBUG_TEMPLATES -#endif - -/************************************************************************ - * * - * Module interfaces * - * * - ************************************************************************/ - -/** - * xsltEvalXPathPredicate: - * @ctxt: the XSLT transformation context - * @comp: the XPath compiled expression - * @nsList: the namespaces in scope - * @nsNr: the number of namespaces in scope - * - * Process the expression using XPath and evaluate the result as - * an XPath predicate - * - * Returns 1 is the predicate was true, 0 otherwise - */ -int -xsltEvalXPathPredicate(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, - xmlNsPtr *nsList, int nsNr) { - int ret; - xmlXPathObjectPtr res; - int oldNsNr; - xmlNsPtr *oldNamespaces; - xmlNodePtr oldInst; - int oldProximityPosition, oldContextSize; - - oldContextSize = ctxt->xpathCtxt->contextSize; - oldProximityPosition = ctxt->xpathCtxt->proximityPosition; - oldNsNr = ctxt->xpathCtxt->nsNr; - oldNamespaces = ctxt->xpathCtxt->namespaces; - oldInst = ctxt->inst; - - ctxt->xpathCtxt->node = ctxt->node; - ctxt->xpathCtxt->namespaces = nsList; - ctxt->xpathCtxt->nsNr = nsNr; - - res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); - - if (res != NULL) { - ret = xmlXPathEvalPredicate(ctxt->xpathCtxt, res); - xmlXPathFreeObject(res); -#ifdef WITH_XSLT_DEBUG_TEMPLATES - XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, - "xsltEvalXPathPredicate: returns %d\n", ret)); -#endif - } else { -#ifdef WITH_XSLT_DEBUG_TEMPLATES - XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, - "xsltEvalXPathPredicate: failed\n")); -#endif - ctxt->state = XSLT_STATE_STOPPED; - ret = 0; - } - ctxt->xpathCtxt->nsNr = oldNsNr; - - ctxt->xpathCtxt->namespaces = oldNamespaces; - ctxt->inst = oldInst; - ctxt->xpathCtxt->contextSize = oldContextSize; - ctxt->xpathCtxt->proximityPosition = oldProximityPosition; - - return(ret); -} - -/** - * xsltEvalXPathStringNs: - * @ctxt: the XSLT transformation context - * @comp: the compiled XPath expression - * @nsNr: the number of namespaces in the list - * @nsList: the list of in-scope namespaces to use - * - * Process the expression using XPath, allowing to pass a namespace mapping - * context and get a string - * - * Returns the computed string value or NULL, must be deallocated by the - * caller. - */ -xmlChar * -xsltEvalXPathStringNs(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, - int nsNr, xmlNsPtr *nsList) { - xmlChar *ret = NULL; - xmlXPathObjectPtr res; - xmlNodePtr oldInst; - xmlNodePtr oldNode; - int oldPos, oldSize; - int oldNsNr; - xmlNsPtr *oldNamespaces; - - oldInst = ctxt->inst; - oldNode = ctxt->node; - oldPos = ctxt->xpathCtxt->proximityPosition; - oldSize = ctxt->xpathCtxt->contextSize; - oldNsNr = ctxt->xpathCtxt->nsNr; - oldNamespaces = ctxt->xpathCtxt->namespaces; - - ctxt->xpathCtxt->node = ctxt->node; - /* TODO: do we need to propagate the namespaces here ? */ - ctxt->xpathCtxt->namespaces = nsList; - ctxt->xpathCtxt->nsNr = nsNr; - res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); - if (res != NULL) { - if (res->type != XPATH_STRING) - res = xmlXPathConvertString(res); - if (res->type == XPATH_STRING) { - ret = res->stringval; - res->stringval = NULL; - } else { - xsltTransformError(ctxt, NULL, NULL, - "xpath : string() function didn't return a String\n"); - } - xmlXPathFreeObject(res); - } else { - ctxt->state = XSLT_STATE_STOPPED; - } -#ifdef WITH_XSLT_DEBUG_TEMPLATES - XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, - "xsltEvalXPathString: returns %s\n", ret)); -#endif - ctxt->inst = oldInst; - ctxt->node = oldNode; - ctxt->xpathCtxt->contextSize = oldSize; - ctxt->xpathCtxt->proximityPosition = oldPos; - ctxt->xpathCtxt->nsNr = oldNsNr; - ctxt->xpathCtxt->namespaces = oldNamespaces; - return(ret); -} - -/** - * xsltEvalXPathString: - * @ctxt: the XSLT transformation context - * @comp: the compiled XPath expression - * - * Process the expression using XPath and get a string - * - * Returns the computed string value or NULL, must be deallocated by the - * caller. - */ -xmlChar * -xsltEvalXPathString(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp) { - return(xsltEvalXPathStringNs(ctxt, comp, 0, NULL)); -} - -/** - * xsltEvalTemplateString: - * @ctxt: the XSLT transformation context - * @currentNode: the current node in the source tree - * @parent: the content parent - * - * Evaluate a template string value, i.e. the parent list is interpreted - * as template content and the resulting tree string value is returned - * This is needed for example by xsl:comment and xsl:processing-instruction - * - * Returns the computed string value or NULL, must be deallocated by the - * caller. - */ -xmlChar * -xsltEvalTemplateString(xsltTransformContextPtr ctxt, - xmlNodePtr currentNode, - xmlNodePtr parent) -{ - xmlNodePtr oldInsert, insert = NULL; - xmlChar *ret; - - if ((ctxt == NULL) || (currentNode == NULL) || (parent == NULL)) - return(NULL); - - if (parent->children == NULL) - return(NULL); - - /* - * This creates a temporary element-node to add the resulting - * text content to. - * OPTIMIZE TODO: Keep such an element-node in the transformation - * context to avoid creating it every time. - */ - insert = xmlNewDocNode(ctxt->output, NULL, - (const xmlChar *)"fake", NULL); - if (insert == NULL) { - xsltTransformError(ctxt, NULL, currentNode, - "Failed to create temporary node\n"); - return(NULL); - } - oldInsert = ctxt->insert; - ctxt->insert = insert; - /* OPTIMIZE TODO: if parent->children consists only of text-nodes. */ - xsltApplyOneTemplate(ctxt, currentNode, parent->children, NULL, NULL); - - ctxt->insert = oldInsert; - - ret = xmlNodeGetContent(insert); - if (insert != NULL) - xmlFreeNode(insert); - return(ret); -} - -/** - * xsltAttrTemplateValueProcessNode: - * @ctxt: the XSLT transformation context - * @str: the attribute template node value - * @inst: the instruction (or LRE) in the stylesheet holding the - * attribute with an AVT - * - * Process the given string, allowing to pass a namespace mapping - * context and return the new string value. - * - * Called by: - * - xsltAttrTemplateValueProcess() (templates.c) - * - xsltEvalAttrValueTemplate() (templates.c) - * - * QUESTION: Why is this function public? It is not used outside - * of templates.c. - * - * Returns the computed string value or NULL, must be deallocated by the - * caller. - */ -xmlChar * -xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt, - const xmlChar *str, xmlNodePtr inst) -{ - xmlChar *ret = NULL; - const xmlChar *cur; - xmlChar *expr, *val; - xmlNsPtr *nsList = NULL; - int nsNr = 0; - - if (str == NULL) return(NULL); - if (*str == 0) - return(xmlStrndup((xmlChar *)"", 0)); - - cur = str; - while (*cur != 0) { - if (*cur == '{') { - if (*(cur+1) == '{') { /* escaped '{' */ - cur++; - ret = xmlStrncat(ret, str, cur - str); - cur++; - str = cur; - continue; - } - ret = xmlStrncat(ret, str, cur - str); - str = cur; - cur++; - while ((*cur != 0) && (*cur != '}')) cur++; - if (*cur == 0) { - xsltTransformError(ctxt, NULL, inst, - "xsltAttrTemplateValueProcessNode: unmatched '{'\n"); - ret = xmlStrncat(ret, str, cur - str); - return(ret); - } - str++; - expr = xmlStrndup(str, cur - str); - if (expr == NULL) - return(ret); - else if (*expr == '{') { - ret = xmlStrcat(ret, expr); - xmlFree(expr); - } else { - xmlXPathCompExprPtr comp; - /* - * TODO: keep precompiled form around - */ - if ((nsList == NULL) && (inst != NULL)) { - int i = 0; - - nsList = xmlGetNsList(inst->doc, inst); - if (nsList != NULL) { - while (nsList[i] != NULL) - i++; - nsNr = i; - } - } - comp = xmlXPathCompile(expr); - val = xsltEvalXPathStringNs(ctxt, comp, nsNr, nsList); - xmlXPathFreeCompExpr(comp); - xmlFree(expr); - if (val != NULL) { - ret = xmlStrcat(ret, val); - xmlFree(val); - } - } - cur++; - str = cur; - } else if (*cur == '}') { - cur++; - if (*cur == '}') { /* escaped '}' */ - ret = xmlStrncat(ret, str, cur - str); - cur++; - str = cur; - continue; - } else { - xsltTransformError(ctxt, NULL, inst, - "xsltAttrTemplateValueProcessNode: unmatched '}'\n"); - } - } else - cur++; - } - if (cur != str) { - ret = xmlStrncat(ret, str, cur - str); - } - - if (nsList != NULL) - xmlFree(nsList); - - return(ret); -} - -/** - * xsltAttrTemplateValueProcess: - * @ctxt: the XSLT transformation context - * @str: the attribute template node value - * - * Process the given node and return the new string value. - * - * Returns the computed string value or NULL, must be deallocated by the - * caller. - */ -xmlChar * -xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt, const xmlChar *str) { - return(xsltAttrTemplateValueProcessNode(ctxt, str, NULL)); -} - -/** - * xsltEvalAttrValueTemplate: - * @ctxt: the XSLT transformation context - * @inst: the instruction (or LRE) in the stylesheet holding the - * attribute with an AVT - * @name: the attribute QName - * @ns: the attribute namespace URI - * - * Evaluate a attribute value template, i.e. the attribute value can - * contain expressions contained in curly braces ({}) and those are - * substituted by they computed value. - * - * Returns the computed string value or NULL, must be deallocated by the - * caller. - */ -xmlChar * -xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst, - const xmlChar *name, const xmlChar *ns) -{ - xmlChar *ret; - xmlChar *expr; - - if ((ctxt == NULL) || (inst == NULL) || (name == NULL)) - return(NULL); - - expr = xsltGetNsProp(inst, name, ns); - if (expr == NULL) - return(NULL); - - /* - * TODO: though now {} is detected ahead, it would still be good to - * optimize both functions to keep the splitted value if the - * attribute content and the XPath precompiled expressions around - */ - - ret = xsltAttrTemplateValueProcessNode(ctxt, expr, inst); -#ifdef WITH_XSLT_DEBUG_TEMPLATES - XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, - "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret)); -#endif - if (expr != NULL) - xmlFree(expr); - return(ret); -} - -/** - * xsltEvalStaticAttrValueTemplate: - * @style: the XSLT stylesheet - * @inst: the instruction (or LRE) in the stylesheet holding the - * attribute with an AVT - * @name: the attribute Name - * @ns: the attribute namespace URI - * @found: indicator whether the attribute is present - * - * Check if an attribute value template has a static value, i.e. the - * attribute value does not contain expressions contained in curly braces ({}) - * - * Returns the static string value or NULL, must be deallocated by the - * caller. - */ -const xmlChar * -xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr inst, - const xmlChar *name, const xmlChar *ns, int *found) { - const xmlChar *ret; - xmlChar *expr; - - if ((style == NULL) || (inst == NULL) || (name == NULL)) - return(NULL); - - expr = xsltGetNsProp(inst, name, ns); - if (expr == NULL) { - *found = 0; - return(NULL); - } - *found = 1; - - ret = xmlStrchr(expr, '{'); - if (ret != NULL) { - xmlFree(expr); - return(NULL); - } - ret = xmlDictLookup(style->dict, expr, -1); - xmlFree(expr); - return(ret); -} - -/** - * xsltAttrTemplateProcess: - * @ctxt: the XSLT transformation context - * @target: the element where the attribute will be grafted - * @attr: the attribute node of a literal result element - * - * Process one attribute of a Literal Result Element (in the stylesheet). - * Evaluates Attribute Value Templates and copies the attribute over to - * the result element. - * This does *not* process attribute sets (xsl:use-attribute-set). - * - * - * Returns the generated attribute node. - */ -xmlAttrPtr -xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target, - xmlAttrPtr attr) -{ - const xmlChar *value; - xmlAttrPtr ret; - - if ((ctxt == NULL) || (attr == NULL) || (target == NULL)) - return(NULL); - - if (attr->type != XML_ATTRIBUTE_NODE) - return(NULL); - - /* - * Skip all XSLT attributes. - */ -#ifdef XSLT_REFACTORED - if (attr->psvi == xsltXSLTAttrMarker) - return(NULL); -#else - if ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) - return(NULL); -#endif - /* - * Get the value. - */ - if (attr->children != NULL) { - if ((attr->children->type != XML_TEXT_NODE) || - (attr->children->next != NULL)) - { - xsltTransformError(ctxt, NULL, attr->parent, - "Internal error: The children of an attribute node of a " - "literal result element are not in the expected form.\n"); - return(NULL); - } - value = attr->children->content; - if (value == NULL) - value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); - } else - value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); - /* - * Overwrite duplicates. - */ - ret = target->properties; - while (ret != NULL) { - if (((attr->ns != NULL) == (ret->ns != NULL)) && - xmlStrEqual(ret->name, attr->name) && - ((attr->ns == NULL) || xmlStrEqual(ret->ns->href, attr->ns->href))) - { - break; - } - ret = ret->next; - } - if (ret != NULL) { - /* free the existing value */ - xmlFreeNodeList(ret->children); - ret->children = ret->last = NULL; - /* - * Adjust ns-prefix if needed. - */ - if ((ret->ns != NULL) && - (! xmlStrEqual(ret->ns->prefix, attr->ns->prefix))) - { - ret->ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target); - } - } else { - /* create a new attribute */ - if (attr->ns != NULL) - ret = xmlNewNsProp(target, - xsltGetNamespace(ctxt, attr->parent, attr->ns, target), - attr->name, NULL); - else - ret = xmlNewNsProp(target, NULL, attr->name, NULL); - } - /* - * Set the value. - */ - if (ret != NULL) { - xmlNodePtr text; - - text = xmlNewText(NULL); - if (text != NULL) { - ret->last = ret->children = text; - text->parent = (xmlNodePtr) ret; - text->doc = ret->doc; - - if (attr->psvi != NULL) { - /* - * Evaluate the Attribute Value Template. - */ - xmlChar *val; - val = xsltEvalAVT(ctxt, attr->psvi, attr->parent); - if (val == NULL) { - /* - * TODO: Damn, we need an easy mechanism to report - * qualified names! - */ - if (attr->ns) { - xsltTransformError(ctxt, NULL, attr->parent, - "Internal error: Failed to evaluate the AVT " - "of attribute '{%s}%s'.\n", - attr->ns->href, attr->name); - } else { - xsltTransformError(ctxt, NULL, attr->parent, - "Internal error: Failed to evaluate the AVT " - "of attribute '%s'.\n", - attr->name); - } - text->content = xmlStrdup(BAD_CAST ""); - } else { - text->content = val; - } - } else if ((ctxt->internalized) && (target != NULL) && - (target->doc != NULL) && - (target->doc->dict == ctxt->dict)) { - text->content = (xmlChar *) value; - } else { - text->content = xmlStrdup(value); - } - } - } else { - if (attr->ns) { - xsltTransformError(ctxt, NULL, attr->parent, - "Internal error: Failed to create attribute '{%s}%s'.\n", - attr->ns->href, attr->name); - } else { - xsltTransformError(ctxt, NULL, attr->parent, - "Internal error: Failed to create attribute '%s'.\n", - attr->name); - } - } - return(ret); -} - - -/** - * xsltAttrListTemplateProcess: - * @ctxt: the XSLT transformation context - * @target: the element where the attributes will be grafted - * @cur: the first attribute - * - * Processes all attributes of a Literal Result Element. - * Attribute references are applied via xsl:use-attribute-set - * attributes. - * Copies all non XSLT-attributes over to the @target element - * and evaluates Attribute Value Templates. - * - * Called by xsltApplyOneTemplateInt() (transform.c). - * - * Returns a new list of attribute nodes, or NULL in case of error. - * (Don't assign the result to @target->properties; if - * the result is NULL, you'll get memory leaks, since the - * attributes will be disattached.) - */ -xmlAttrPtr -xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt, - xmlNodePtr target, xmlAttrPtr attrs) -{ - xmlAttrPtr attr, copy, last; - xmlNodePtr oldInsert, text; - xmlNsPtr origNs = NULL, copyNs = NULL; - const xmlChar *value; - xmlChar *valueAVT; - - if ((ctxt == NULL) || (target == NULL) || (attrs == NULL)) - return(NULL); - - oldInsert = ctxt->insert; - ctxt->insert = target; - - /* - * Instantiate LRE-attributes. - */ - if (target->properties) { - last = target->properties; - while (last->next != NULL) - last = last->next; - } else { - last = NULL; - } - attr = attrs; - do { - /* - * Skip XSLT attributes. - */ -#ifdef XSLT_REFACTORED - if (attr->psvi == xsltXSLTAttrMarker) { - goto next_attribute; - } -#else - if ((attr->ns != NULL) && - xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) - { - goto next_attribute; - } -#endif - /* - * Get the value. - */ - if (attr->children != NULL) { - if ((attr->children->type != XML_TEXT_NODE) || - (attr->children->next != NULL)) - { - xsltTransformError(ctxt, NULL, attr->parent, - "Internal error: The children of an attribute node of a " - "literal result element are not in the expected form.\n"); - goto error; - } - value = attr->children->content; - if (value == NULL) - value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); - } else - value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); - - /* - * Create a new attribute. - */ - copy = xmlNewDocProp(target->doc, attr->name, NULL); - if (copy == NULL) { - if (attr->ns) { - xsltTransformError(ctxt, NULL, attr->parent, - "Internal error: Failed to create attribute '{%s}%s'.\n", - attr->ns->href, attr->name); - } else { - xsltTransformError(ctxt, NULL, attr->parent, - "Internal error: Failed to create attribute '%s'.\n", - attr->name); - } - goto error; - } - /* - * Attach it to the target element. - */ - copy->parent = target; - if (last == NULL) { - target->properties = copy; - last = copy; - } else { - last->next = copy; - copy->prev = last; - last = copy; - } - /* - * Set the namespace. Avoid lookups of same namespaces. - */ - if (attr->ns != origNs) { - origNs = attr->ns; - if (attr->ns != NULL) { -#ifdef XSLT_REFACTORED - copyNs = xsltGetSpecialNamespace(ctxt, attr->parent, - attr->ns->href, attr->ns->prefix, target); -#else - copyNs = xsltGetNamespace(ctxt, attr->parent, - attr->ns, target); -#endif - if (copyNs == NULL) - goto error; - } else - copyNs = NULL; - } - copy->ns = copyNs; - - /* - * Set the value. - */ - text = xmlNewText(NULL); - if (text != NULL) { - copy->last = copy->children = text; - text->parent = (xmlNodePtr) copy; - text->doc = copy->doc; - - if (attr->psvi != NULL) { - /* - * Evaluate the Attribute Value Template. - */ - valueAVT = xsltEvalAVT(ctxt, attr->psvi, attr->parent); - if (valueAVT == NULL) { - /* - * TODO: Damn, we need an easy mechanism to report - * qualified names! - */ - if (attr->ns) { - xsltTransformError(ctxt, NULL, attr->parent, - "Internal error: Failed to evaluate the AVT " - "of attribute '{%s}%s'.\n", - attr->ns->href, attr->name); - } else { - xsltTransformError(ctxt, NULL, attr->parent, - "Internal error: Failed to evaluate the AVT " - "of attribute '%s'.\n", - attr->name); - } - text->content = xmlStrdup(BAD_CAST ""); - goto error; - } else { - text->content = valueAVT; - } - } else if ((ctxt->internalized) && - (target->doc != NULL) && - (target->doc->dict == ctxt->dict)) - { - text->content = (xmlChar *) value; - } else { - text->content = xmlStrdup(value); - } - } - -next_attribute: - attr = attr->next; - } while (attr != NULL); - - /* - * Apply attribute-sets. - * The creation of such attributes will not overwrite any existing - * attribute. - */ - attr = attrs; - do { -#ifdef XSLT_REFACTORED - if ((attr->psvi == xsltXSLTAttrMarker) && - xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets")) - { - xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); - } -#else - if ((attr->ns != NULL) && - xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets") && - xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) - { - xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); - } -#endif - attr = attr->next; - } while (attr != NULL); - - ctxt->insert = oldInsert; - return(target->properties); - -error: - ctxt->insert = oldInsert; - return(NULL); -} - - -/** - * xsltTemplateProcess: - * @ctxt: the XSLT transformation context - * @node: the attribute template node - * - * Obsolete. Does always return NULL. Don't use it. - */ -xmlNodePtr * -xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) { - if (node == NULL) - return(NULL); - - return(0); -} - - +/* + * templates.c: Implementation of the template processing + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include + +#include +#include +#include +#include +#include +#include +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "variables.h" +#include "functions.h" +#include "templates.h" +#include "transform.h" +#include "namespaces.h" +#include "attributes.h" + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_TEMPLATES +#endif + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +/** + * xsltEvalXPathPredicate: + * @ctxt: the XSLT transformation context + * @comp: the XPath compiled expression + * @nsList: the namespaces in scope + * @nsNr: the number of namespaces in scope + * + * Process the expression using XPath and evaluate the result as + * an XPath predicate + * + * Returns 1 is the predicate was true, 0 otherwise + */ +int +xsltEvalXPathPredicate(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, + xmlNsPtr *nsList, int nsNr) { + int ret; + xmlXPathObjectPtr res; + int oldNsNr; + xmlNsPtr *oldNamespaces; + xmlNodePtr oldInst; + int oldProximityPosition, oldContextSize; + + oldContextSize = ctxt->xpathCtxt->contextSize; + oldProximityPosition = ctxt->xpathCtxt->proximityPosition; + oldNsNr = ctxt->xpathCtxt->nsNr; + oldNamespaces = ctxt->xpathCtxt->namespaces; + oldInst = ctxt->inst; + + ctxt->xpathCtxt->node = ctxt->node; + ctxt->xpathCtxt->namespaces = nsList; + ctxt->xpathCtxt->nsNr = nsNr; + + res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); + + if (res != NULL) { + ret = xmlXPathEvalPredicate(ctxt->xpathCtxt, res); + xmlXPathFreeObject(res); +#ifdef WITH_XSLT_DEBUG_TEMPLATES + XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltEvalXPathPredicate: returns %d\n", ret)); +#endif + } else { +#ifdef WITH_XSLT_DEBUG_TEMPLATES + XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltEvalXPathPredicate: failed\n")); +#endif + ctxt->state = XSLT_STATE_STOPPED; + ret = 0; + } + ctxt->xpathCtxt->nsNr = oldNsNr; + + ctxt->xpathCtxt->namespaces = oldNamespaces; + ctxt->inst = oldInst; + ctxt->xpathCtxt->contextSize = oldContextSize; + ctxt->xpathCtxt->proximityPosition = oldProximityPosition; + + return(ret); +} + +/** + * xsltEvalXPathStringNs: + * @ctxt: the XSLT transformation context + * @comp: the compiled XPath expression + * @nsNr: the number of namespaces in the list + * @nsList: the list of in-scope namespaces to use + * + * Process the expression using XPath, allowing to pass a namespace mapping + * context and get a string + * + * Returns the computed string value or NULL, must be deallocated by the + * caller. + */ +xmlChar * +xsltEvalXPathStringNs(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, + int nsNr, xmlNsPtr *nsList) { + xmlChar *ret = NULL; + xmlXPathObjectPtr res; + xmlNodePtr oldInst; + xmlNodePtr oldNode; + int oldPos, oldSize; + int oldNsNr; + xmlNsPtr *oldNamespaces; + + oldInst = ctxt->inst; + oldNode = ctxt->node; + oldPos = ctxt->xpathCtxt->proximityPosition; + oldSize = ctxt->xpathCtxt->contextSize; + oldNsNr = ctxt->xpathCtxt->nsNr; + oldNamespaces = ctxt->xpathCtxt->namespaces; + + ctxt->xpathCtxt->node = ctxt->node; + /* TODO: do we need to propagate the namespaces here ? */ + ctxt->xpathCtxt->namespaces = nsList; + ctxt->xpathCtxt->nsNr = nsNr; + res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); + if (res != NULL) { + if (res->type != XPATH_STRING) + res = xmlXPathConvertString(res); + if (res->type == XPATH_STRING) { + ret = res->stringval; + res->stringval = NULL; + } else { + xsltTransformError(ctxt, NULL, NULL, + "xpath : string() function didn't return a String\n"); + } + xmlXPathFreeObject(res); + } else { + ctxt->state = XSLT_STATE_STOPPED; + } +#ifdef WITH_XSLT_DEBUG_TEMPLATES + XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltEvalXPathString: returns %s\n", ret)); +#endif + ctxt->inst = oldInst; + ctxt->node = oldNode; + ctxt->xpathCtxt->contextSize = oldSize; + ctxt->xpathCtxt->proximityPosition = oldPos; + ctxt->xpathCtxt->nsNr = oldNsNr; + ctxt->xpathCtxt->namespaces = oldNamespaces; + return(ret); +} + +/** + * xsltEvalXPathString: + * @ctxt: the XSLT transformation context + * @comp: the compiled XPath expression + * + * Process the expression using XPath and get a string + * + * Returns the computed string value or NULL, must be deallocated by the + * caller. + */ +xmlChar * +xsltEvalXPathString(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp) { + return(xsltEvalXPathStringNs(ctxt, comp, 0, NULL)); +} + +/** + * xsltEvalTemplateString: + * @ctxt: the XSLT transformation context + * @contextNode: the current node in the source tree + * @inst: the XSLT instruction (xsl:comment, xsl:processing-instruction) + * + * Processes the sequence constructor of the given instruction on + * @contextNode and converts the resulting tree to a string. + * This is needed by e.g. xsl:comment and xsl:processing-instruction. + * + * Returns the computed string value or NULL; it's up to the caller to + * free the result. + */ +xmlChar * +xsltEvalTemplateString(xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, + xmlNodePtr inst) +{ + xmlNodePtr oldInsert, insert = NULL; + xmlChar *ret; + + if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) + return(NULL); + + if (inst->children == NULL) + return(NULL); + + /* + * This creates a temporary element-node to add the resulting + * text content to. + * OPTIMIZE TODO: Keep such an element-node in the transformation + * context to avoid creating it every time. + */ + insert = xmlNewDocNode(ctxt->output, NULL, + (const xmlChar *)"fake", NULL); + if (insert == NULL) { + xsltTransformError(ctxt, NULL, contextNode, + "Failed to create temporary node\n"); + return(NULL); + } + oldInsert = ctxt->insert; + ctxt->insert = insert; + /* + * OPTIMIZE TODO: if inst->children consists only of text-nodes. + */ + xsltApplyOneTemplate(ctxt, contextNode, inst->children, NULL, NULL); + + ctxt->insert = oldInsert; + + ret = xmlNodeGetContent(insert); + if (insert != NULL) + xmlFreeNode(insert); + return(ret); +} + +/** + * xsltAttrTemplateValueProcessNode: + * @ctxt: the XSLT transformation context + * @str: the attribute template node value + * @inst: the instruction (or LRE) in the stylesheet holding the + * attribute with an AVT + * + * Process the given string, allowing to pass a namespace mapping + * context and return the new string value. + * + * Called by: + * - xsltAttrTemplateValueProcess() (templates.c) + * - xsltEvalAttrValueTemplate() (templates.c) + * + * QUESTION: Why is this function public? It is not used outside + * of templates.c. + * + * Returns the computed string value or NULL, must be deallocated by the + * caller. + */ +xmlChar * +xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt, + const xmlChar *str, xmlNodePtr inst) +{ + xmlChar *ret = NULL; + const xmlChar *cur; + xmlChar *expr, *val; + xmlNsPtr *nsList = NULL; + int nsNr = 0; + + if (str == NULL) return(NULL); + if (*str == 0) + return(xmlStrndup((xmlChar *)"", 0)); + + cur = str; + while (*cur != 0) { + if (*cur == '{') { + if (*(cur+1) == '{') { /* escaped '{' */ + cur++; + ret = xmlStrncat(ret, str, cur - str); + cur++; + str = cur; + continue; + } + ret = xmlStrncat(ret, str, cur - str); + str = cur; + cur++; + while ((*cur != 0) && (*cur != '}')) cur++; + if (*cur == 0) { + xsltTransformError(ctxt, NULL, inst, + "xsltAttrTemplateValueProcessNode: unmatched '{'\n"); + ret = xmlStrncat(ret, str, cur - str); + return(ret); + } + str++; + expr = xmlStrndup(str, cur - str); + if (expr == NULL) + return(ret); + else if (*expr == '{') { + ret = xmlStrcat(ret, expr); + xmlFree(expr); + } else { + xmlXPathCompExprPtr comp; + /* + * TODO: keep precompiled form around + */ + if ((nsList == NULL) && (inst != NULL)) { + int i = 0; + + nsList = xmlGetNsList(inst->doc, inst); + if (nsList != NULL) { + while (nsList[i] != NULL) + i++; + nsNr = i; + } + } + comp = xmlXPathCompile(expr); + val = xsltEvalXPathStringNs(ctxt, comp, nsNr, nsList); + xmlXPathFreeCompExpr(comp); + xmlFree(expr); + if (val != NULL) { + ret = xmlStrcat(ret, val); + xmlFree(val); + } + } + cur++; + str = cur; + } else if (*cur == '}') { + cur++; + if (*cur == '}') { /* escaped '}' */ + ret = xmlStrncat(ret, str, cur - str); + cur++; + str = cur; + continue; + } else { + xsltTransformError(ctxt, NULL, inst, + "xsltAttrTemplateValueProcessNode: unmatched '}'\n"); + } + } else + cur++; + } + if (cur != str) { + ret = xmlStrncat(ret, str, cur - str); + } + + if (nsList != NULL) + xmlFree(nsList); + + return(ret); +} + +/** + * xsltAttrTemplateValueProcess: + * @ctxt: the XSLT transformation context + * @str: the attribute template node value + * + * Process the given node and return the new string value. + * + * Returns the computed string value or NULL, must be deallocated by the + * caller. + */ +xmlChar * +xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt, const xmlChar *str) { + return(xsltAttrTemplateValueProcessNode(ctxt, str, NULL)); +} + +/** + * xsltEvalAttrValueTemplate: + * @ctxt: the XSLT transformation context + * @inst: the instruction (or LRE) in the stylesheet holding the + * attribute with an AVT + * @name: the attribute QName + * @ns: the attribute namespace URI + * + * Evaluate a attribute value template, i.e. the attribute value can + * contain expressions contained in curly braces ({}) and those are + * substituted by they computed value. + * + * Returns the computed string value or NULL, must be deallocated by the + * caller. + */ +xmlChar * +xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst, + const xmlChar *name, const xmlChar *ns) +{ + xmlChar *ret; + xmlChar *expr; + + if ((ctxt == NULL) || (inst == NULL) || (name == NULL)) + return(NULL); + + expr = xsltGetNsProp(inst, name, ns); + if (expr == NULL) + return(NULL); + + /* + * TODO: though now {} is detected ahead, it would still be good to + * optimize both functions to keep the splitted value if the + * attribute content and the XPath precompiled expressions around + */ + + ret = xsltAttrTemplateValueProcessNode(ctxt, expr, inst); +#ifdef WITH_XSLT_DEBUG_TEMPLATES + XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret)); +#endif + if (expr != NULL) + xmlFree(expr); + return(ret); +} + +/** + * xsltEvalStaticAttrValueTemplate: + * @style: the XSLT stylesheet + * @inst: the instruction (or LRE) in the stylesheet holding the + * attribute with an AVT + * @name: the attribute Name + * @ns: the attribute namespace URI + * @found: indicator whether the attribute is present + * + * Check if an attribute value template has a static value, i.e. the + * attribute value does not contain expressions contained in curly braces ({}) + * + * Returns the static string value or NULL, must be deallocated by the + * caller. + */ +const xmlChar * +xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr inst, + const xmlChar *name, const xmlChar *ns, int *found) { + const xmlChar *ret; + xmlChar *expr; + + if ((style == NULL) || (inst == NULL) || (name == NULL)) + return(NULL); + + expr = xsltGetNsProp(inst, name, ns); + if (expr == NULL) { + *found = 0; + return(NULL); + } + *found = 1; + + ret = xmlStrchr(expr, '{'); + if (ret != NULL) { + xmlFree(expr); + return(NULL); + } + ret = xmlDictLookup(style->dict, expr, -1); + xmlFree(expr); + return(ret); +} + +/** + * xsltAttrTemplateProcess: + * @ctxt: the XSLT transformation context + * @target: the element where the attribute will be grafted + * @attr: the attribute node of a literal result element + * + * Process one attribute of a Literal Result Element (in the stylesheet). + * Evaluates Attribute Value Templates and copies the attribute over to + * the result element. + * This does *not* process attribute sets (xsl:use-attribute-set). + * + * + * Returns the generated attribute node. + */ +xmlAttrPtr +xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target, + xmlAttrPtr attr) +{ + const xmlChar *value; + xmlAttrPtr ret; + + if ((ctxt == NULL) || (attr == NULL) || (target == NULL)) + return(NULL); + + if (attr->type != XML_ATTRIBUTE_NODE) + return(NULL); + + /* + * Skip all XSLT attributes. + */ +#ifdef XSLT_REFACTORED + if (attr->psvi == xsltXSLTAttrMarker) + return(NULL); +#else + if ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) + return(NULL); +#endif + /* + * Get the value. + */ + if (attr->children != NULL) { + if ((attr->children->type != XML_TEXT_NODE) || + (attr->children->next != NULL)) + { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: The children of an attribute node of a " + "literal result element are not in the expected form.\n"); + return(NULL); + } + value = attr->children->content; + if (value == NULL) + value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); + } else + value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); + /* + * Overwrite duplicates. + */ + ret = target->properties; + while (ret != NULL) { + if (((attr->ns != NULL) == (ret->ns != NULL)) && + xmlStrEqual(ret->name, attr->name) && + ((attr->ns == NULL) || xmlStrEqual(ret->ns->href, attr->ns->href))) + { + break; + } + ret = ret->next; + } + if (ret != NULL) { + /* free the existing value */ + xmlFreeNodeList(ret->children); + ret->children = ret->last = NULL; + /* + * Adjust ns-prefix if needed. + */ + if ((ret->ns != NULL) && + (! xmlStrEqual(ret->ns->prefix, attr->ns->prefix))) + { + ret->ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target); + } + } else { + /* create a new attribute */ + if (attr->ns != NULL) + ret = xmlNewNsProp(target, + xsltGetNamespace(ctxt, attr->parent, attr->ns, target), + attr->name, NULL); + else + ret = xmlNewNsProp(target, NULL, attr->name, NULL); + } + /* + * Set the value. + */ + if (ret != NULL) { + xmlNodePtr text; + + text = xmlNewText(NULL); + if (text != NULL) { + ret->last = ret->children = text; + text->parent = (xmlNodePtr) ret; + text->doc = ret->doc; + + if (attr->psvi != NULL) { + /* + * Evaluate the Attribute Value Template. + */ + xmlChar *val; + val = xsltEvalAVT(ctxt, attr->psvi, attr->parent); + if (val == NULL) { + /* + * TODO: Damn, we need an easy mechanism to report + * qualified names! + */ + if (attr->ns) { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to evaluate the AVT " + "of attribute '{%s}%s'.\n", + attr->ns->href, attr->name); + } else { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to evaluate the AVT " + "of attribute '%s'.\n", + attr->name); + } + text->content = xmlStrdup(BAD_CAST ""); + } else { + text->content = val; + } + } else if ((ctxt->internalized) && (target != NULL) && + (target->doc != NULL) && + (target->doc->dict == ctxt->dict)) { + text->content = (xmlChar *) value; + } else { + text->content = xmlStrdup(value); + } + } + } else { + if (attr->ns) { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to create attribute '{%s}%s'.\n", + attr->ns->href, attr->name); + } else { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to create attribute '%s'.\n", + attr->name); + } + } + return(ret); +} + + +/** + * xsltAttrListTemplateProcess: + * @ctxt: the XSLT transformation context + * @target: the element where the attributes will be grafted + * @cur: the first attribute + * + * Processes all attributes of a Literal Result Element. + * Attribute references are applied via xsl:use-attribute-set + * attributes. + * Copies all non XSLT-attributes over to the @target element + * and evaluates Attribute Value Templates. + * + * Called by xsltApplySequenceConstructor() (transform.c). + * + * Returns a new list of attribute nodes, or NULL in case of error. + * (Don't assign the result to @target->properties; if + * the result is NULL, you'll get memory leaks, since the + * attributes will be disattached.) + */ +xmlAttrPtr +xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt, + xmlNodePtr target, xmlAttrPtr attrs) +{ + xmlAttrPtr attr, copy, last; + xmlNodePtr oldInsert, text; + xmlNsPtr origNs = NULL, copyNs = NULL; + const xmlChar *value; + xmlChar *valueAVT; + + if ((ctxt == NULL) || (target == NULL) || (attrs == NULL)) + return(NULL); + + oldInsert = ctxt->insert; + ctxt->insert = target; + + /* + * Instantiate LRE-attributes. + */ + if (target->properties) { + last = target->properties; + while (last->next != NULL) + last = last->next; + } else { + last = NULL; + } + attr = attrs; + do { + /* + * Skip XSLT attributes. + */ +#ifdef XSLT_REFACTORED + if (attr->psvi == xsltXSLTAttrMarker) { + goto next_attribute; + } +#else + if ((attr->ns != NULL) && + xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) + { + goto next_attribute; + } +#endif + /* + * Get the value. + */ + if (attr->children != NULL) { + if ((attr->children->type != XML_TEXT_NODE) || + (attr->children->next != NULL)) + { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: The children of an attribute node of a " + "literal result element are not in the expected form.\n"); + goto error; + } + value = attr->children->content; + if (value == NULL) + value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); + } else + value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); + + /* + * Create a new attribute. + */ + copy = xmlNewDocProp(target->doc, attr->name, NULL); + if (copy == NULL) { + if (attr->ns) { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to create attribute '{%s}%s'.\n", + attr->ns->href, attr->name); + } else { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to create attribute '%s'.\n", + attr->name); + } + goto error; + } + /* + * Attach it to the target element. + */ + copy->parent = target; + if (last == NULL) { + target->properties = copy; + last = copy; + } else { + last->next = copy; + copy->prev = last; + last = copy; + } + /* + * Set the namespace. Avoid lookups of same namespaces. + */ + if (attr->ns != origNs) { + origNs = attr->ns; + if (attr->ns != NULL) { +#ifdef XSLT_REFACTORED + copyNs = xsltGetSpecialNamespace(ctxt, attr->parent, + attr->ns->href, attr->ns->prefix, target); +#else + copyNs = xsltGetNamespace(ctxt, attr->parent, + attr->ns, target); +#endif + if (copyNs == NULL) + goto error; + } else + copyNs = NULL; + } + copy->ns = copyNs; + + /* + * Set the value. + */ + text = xmlNewText(NULL); + if (text != NULL) { + copy->last = copy->children = text; + text->parent = (xmlNodePtr) copy; + text->doc = copy->doc; + + if (attr->psvi != NULL) { + /* + * Evaluate the Attribute Value Template. + */ + valueAVT = xsltEvalAVT(ctxt, attr->psvi, attr->parent); + if (valueAVT == NULL) { + /* + * TODO: Damn, we need an easy mechanism to report + * qualified names! + */ + if (attr->ns) { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to evaluate the AVT " + "of attribute '{%s}%s'.\n", + attr->ns->href, attr->name); + } else { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to evaluate the AVT " + "of attribute '%s'.\n", + attr->name); + } + text->content = xmlStrdup(BAD_CAST ""); + goto error; + } else { + text->content = valueAVT; + } + } else if ((ctxt->internalized) && + (target->doc != NULL) && + (target->doc->dict == ctxt->dict)) + { + text->content = (xmlChar *) value; + } else { + text->content = xmlStrdup(value); + } + } + +next_attribute: + attr = attr->next; + } while (attr != NULL); + + /* + * Apply attribute-sets. + * The creation of such attributes will not overwrite any existing + * attribute. + */ + attr = attrs; + do { +#ifdef XSLT_REFACTORED + if ((attr->psvi == xsltXSLTAttrMarker) && + xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets")) + { + xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); + } +#else + if ((attr->ns != NULL) && + xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets") && + xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) + { + xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); + } +#endif + attr = attr->next; + } while (attr != NULL); + + ctxt->insert = oldInsert; + return(target->properties); + +error: + ctxt->insert = oldInsert; + return(NULL); +} + + +/** + * xsltTemplateProcess: + * @ctxt: the XSLT transformation context + * @node: the attribute template node + * + * Obsolete. Does always return NULL. Don't use it. + */ +xmlNodePtr * +xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) { + if (node == NULL) + return(NULL); + + return(0); +} + + diff --git a/libxslt/templates.h b/libxslt/templates.h index b9a6e8f8..54907e11 100644 --- a/libxslt/templates.h +++ b/libxslt/templates.h @@ -1,77 +1,77 @@ -/* - * Summary: interface for the template processing - * Description: This set of routine encapsulates XPath calls - * and Attribute Value Templates evaluation. - * - * Copy: See Copyright for the status of this software. - * - * Author: Daniel Veillard - */ - -#ifndef __XML_XSLT_TEMPLATES_H__ -#define __XML_XSLT_TEMPLATES_H__ - -#include -#include -#include "xsltexports.h" -#include "xsltInternals.h" - -#ifdef __cplusplus -extern "C" { -#endif - -XSLTPUBFUN int XSLTCALL - xsltEvalXPathPredicate (xsltTransformContextPtr ctxt, - xmlXPathCompExprPtr comp, - xmlNsPtr *nsList, - int nsNr); -XSLTPUBFUN xmlChar * XSLTCALL - xsltEvalTemplateString (xsltTransformContextPtr ctxt, - xmlNodePtr node, - xmlNodePtr parent); -XSLTPUBFUN xmlChar * XSLTCALL - xsltEvalAttrValueTemplate (xsltTransformContextPtr ctxt, - xmlNodePtr node, - const xmlChar *name, - const xmlChar *ns); -XSLTPUBFUN const xmlChar * XSLTCALL - xsltEvalStaticAttrValueTemplate (xsltStylesheetPtr style, - xmlNodePtr node, - const xmlChar *name, - const xmlChar *ns, - int *found); - -/* TODO: this is obviously broken ... the namespaces should be passed too ! */ -XSLTPUBFUN xmlChar * XSLTCALL - xsltEvalXPathString (xsltTransformContextPtr ctxt, - xmlXPathCompExprPtr comp); -XSLTPUBFUN xmlChar * XSLTCALL - xsltEvalXPathStringNs (xsltTransformContextPtr ctxt, - xmlXPathCompExprPtr comp, - int nsNr, - xmlNsPtr *nsList); - -XSLTPUBFUN xmlNodePtr * XSLTCALL - xsltTemplateProcess (xsltTransformContextPtr ctxt, - xmlNodePtr node); -XSLTPUBFUN xmlAttrPtr XSLTCALL - xsltAttrListTemplateProcess (xsltTransformContextPtr ctxt, - xmlNodePtr target, - xmlAttrPtr cur); -XSLTPUBFUN xmlAttrPtr XSLTCALL - xsltAttrTemplateProcess (xsltTransformContextPtr ctxt, - xmlNodePtr target, - xmlAttrPtr attr); -XSLTPUBFUN xmlChar * XSLTCALL - xsltAttrTemplateValueProcess (xsltTransformContextPtr ctxt, - const xmlChar* attr); -XSLTPUBFUN xmlChar * XSLTCALL - xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt, - const xmlChar* str, - xmlNodePtr node); -#ifdef __cplusplus -} -#endif - -#endif /* __XML_XSLT_TEMPLATES_H__ */ - +/* + * Summary: interface for the template processing + * Description: This set of routine encapsulates XPath calls + * and Attribute Value Templates evaluation. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_TEMPLATES_H__ +#define __XML_XSLT_TEMPLATES_H__ + +#include +#include +#include "xsltexports.h" +#include "xsltInternals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +XSLTPUBFUN int XSLTCALL + xsltEvalXPathPredicate (xsltTransformContextPtr ctxt, + xmlXPathCompExprPtr comp, + xmlNsPtr *nsList, + int nsNr); +XSLTPUBFUN xmlChar * XSLTCALL + xsltEvalTemplateString (xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, + xmlNodePtr inst); +XSLTPUBFUN xmlChar * XSLTCALL + xsltEvalAttrValueTemplate (xsltTransformContextPtr ctxt, + xmlNodePtr node, + const xmlChar *name, + const xmlChar *ns); +XSLTPUBFUN const xmlChar * XSLTCALL + xsltEvalStaticAttrValueTemplate (xsltStylesheetPtr style, + xmlNodePtr node, + const xmlChar *name, + const xmlChar *ns, + int *found); + +/* TODO: this is obviously broken ... the namespaces should be passed too ! */ +XSLTPUBFUN xmlChar * XSLTCALL + xsltEvalXPathString (xsltTransformContextPtr ctxt, + xmlXPathCompExprPtr comp); +XSLTPUBFUN xmlChar * XSLTCALL + xsltEvalXPathStringNs (xsltTransformContextPtr ctxt, + xmlXPathCompExprPtr comp, + int nsNr, + xmlNsPtr *nsList); + +XSLTPUBFUN xmlNodePtr * XSLTCALL + xsltTemplateProcess (xsltTransformContextPtr ctxt, + xmlNodePtr node); +XSLTPUBFUN xmlAttrPtr XSLTCALL + xsltAttrListTemplateProcess (xsltTransformContextPtr ctxt, + xmlNodePtr target, + xmlAttrPtr cur); +XSLTPUBFUN xmlAttrPtr XSLTCALL + xsltAttrTemplateProcess (xsltTransformContextPtr ctxt, + xmlNodePtr target, + xmlAttrPtr attr); +XSLTPUBFUN xmlChar * XSLTCALL + xsltAttrTemplateValueProcess (xsltTransformContextPtr ctxt, + const xmlChar* attr); +XSLTPUBFUN xmlChar * XSLTCALL + xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt, + const xmlChar* str, + xmlNodePtr node); +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_TEMPLATES_H__ */ + diff --git a/libxslt/transform.c b/libxslt/transform.c index 76b5af83..ed1bfc04 100644 --- a/libxslt/transform.c +++ b/libxslt/transform.c @@ -1,5559 +1,6399 @@ -/* - * 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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 -#endif - -#define XSLT_GENERATE_HTML_DOCTYPE -#ifdef XSLT_GENERATE_HTML_DOCTYPE -static int xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID, - const xmlChar **systemID); -#endif - -static void -xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNodePtr list, xsltTemplatePtr templ, - xsltStackElemPtr params, int notcur); - -int xsltMaxDepth = 5000; - -/* - * 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); - -/** - * 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); - } - } - 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); -} -/** - * varsPush: - * @ctxt: the transformation context - * @value: the variable to push on the stack - * - * Push a variable on the stack - * - * Returns the new index in the stack or 0 in case of error - */ -static int -varsPush(xsltTransformContextPtr ctxt, xsltStackElemPtr value) -{ - if (ctxt->varsMax == 0) { - ctxt->varsMax = 4; - ctxt->varsTab = - (xsltStackElemPtr *) xmlMalloc(ctxt->varsMax * - sizeof(ctxt->varsTab[0])); - if (ctxt->varsTab == NULL) { - xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); - return (0); - } - } - 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 (0); - } - } - ctxt->varsTab[ctxt->varsNr] = value; - ctxt->vars = value; - return (ctxt->varsNr++); -} -/** - * varsPop: - * @ctxt: the transformation context - * - * Pop a variable value from the stack - * - * Returns the stored variable value - */ -static xsltStackElemPtr -varsPop(xsltTransformContextPtr ctxt) -{ - xsltStackElemPtr ret; - - if (ctxt->varsNr <= 0) - return (0); - ctxt->varsNr--; - if (ctxt->varsNr > 0) - ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1]; - else - ctxt->vars = (xsltStackElemPtr) 0; - ret = ctxt->varsTab[ctxt->varsNr]; - ctxt->varsTab[ctxt->varsNr] = 0; - return (ret); -} -/** - * 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); - } - } - 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); -} - -/************************************************************************ - * * - * 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 * - * * - ************************************************************************/ - -/** - * 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; - - cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext)); - if (cur == NULL) { - xsltTransformError(NULL, NULL, (xmlNodePtr)doc, - "xsltNewTransformContext : malloc failed\n"); - return(NULL); - } - memset(cur, 0, sizeof(xsltTransformContext)); - - /* - * setup of the dictionnary 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; - - /* - * 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 = 5; - cur->vars = NULL; - cur->varsBase = 0; - - /* - * 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; - } - cur->xpathCtxt->proximityPosition = 0; - cur->xpathCtxt->contextSize = 0; - /* - * 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(); - - 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); - xmlDictFree(ctxt->dict); -#ifdef WITH_XSLT_DEBUG - xsltGenericDebug(xsltGenericDebugContext, - "freeing transformation dictionnary\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); - -/** - * 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. - * - * Create a text node - * - * Returns: a new xmlNodePtr, or NULL in case of error. - */ -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 - - /* 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)))) - { - if ((target != NULL) && (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) { - 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 { - 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) { - if (target != NULL) - xmlAddChild(target, copy); - 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 dictionnary - * - * Do a copy of a text node - * - * Returns: a new xmlNodePtr, or NULL in case of error. - */ -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 - - 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)))) { - /* - * OPTIMIZE TODO: xsltCopyText() is also used for attribute content. - */ - /* - * TODO: Since this doesn't merge adjacent CDATA-section nodes, - * we'll get: . - * TODO: Reported in #321505. - */ - copy = xmlNewCDataBlock(ctxt->output, cur->content, - xmlStrlen(cur->content)); - ctxt->lasttext = NULL; - } 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 - */ - return(xsltAddTextString(ctxt, target->last, cur->content, - xmlStrlen(cur->content))); - } 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) - return NULL; - if (cur->name == xmlStringTextNoenc) - copy->name = xmlStringTextNoenc; - - /* OPTIMIZE TODO: get rid of xmlDictOwns() in safe cases; - * e.g. attribute values don't need the lookup. - * - * Must confirm that content is in dict (bug 302821) - * TODO: Check if bug 302821 still applies here. - */ - 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) - return NULL; - 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. - */ - xmlAddChild(target, copy); - } - } else { - xsltTransformError(ctxt, NULL, target, - "xsltCopyText: text copy failed\n"); - } - 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 last = NULL, copy; - xmlNsPtr origNs = NULL, copyNs = NULL; - xmlChar *value = NULL; - - /* - * 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 (attr->children) - value = xmlNodeListGetString(attr->doc, attr->children, 1); - /* - * REVISIT: I think xmlNewDocProp() is the only attr function - * which does not eval if the attr is of type ID. This is good, - * since we don't need this. - */ - copy = xmlNewDocProp(target->doc, attr->name, BAD_CAST value); - if (copy == NULL) - return(-1); - copy->parent = target; - copy->ns = copyNs; - - if (last == NULL) { - target->properties = copy; - last = copy; - } else { - last->next = copy; - copy->prev = last; - last = copy; - } - /* - * OPTIMIZE TODO: How to avoid this intermediate string? - */ - if (value != NULL) { - xmlFree(value); - value = NULL; - } - 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: - * xsltApplyOneTemplateInt() (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; - xmlAddChild(insert, 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. - * @literal: is this a literal result element list - * - * 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 int -xsltShallowCopyNsNode(xsltTransformContextPtr ctxt, - xmlNodePtr invocNode, - xmlNodePtr insert, - xmlNsPtr ns) -{ - xmlNsPtr tmpns; - - if ((insert == NULL) || (insert->type != XML_ELEMENT_NODE)) - return(-1); - - if (insert->children != NULL) { - xsltTransformError(ctxt, NULL, invocNode, - "Namespace nodes must be added before " - "any child nodes are added to an element.\n"); - return(1); - } - /* - * - * 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. - * , 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")) - { - return(0); - } - - 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(0); - goto occupied; - } - } - tmpns = tmpns->next; - } while (tmpns != NULL); - } - tmpns = xmlSearchNs(insert->doc, insert, ns->prefix); - if ((tmpns != NULL) && xmlStrEqual(tmpns->href, ns->href)) - return(0); - /* - * 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 :-/ - */ - xmlNewNs(insert, ns->href, ns->prefix); - return(0); - -occupied: - /* - * TODO: We could as well raise an error here (like Saxon does), - * or at least generate a warning. - */ - return(0); -} - -/** - * 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; - xmlAddChild(insert, 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++; - xsltApplyOneTemplateInt(ctxt, node, child->children, NULL, NULL, 0); - } - child = child->next; - } - return(ret); -} - -/************************************************************************ - * * - * Default processing * - * * - ************************************************************************/ - -void xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node, - xsltStackElemPtr params); -/** - * 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: - * - * - * - * - * and - * - * - * - * - * - * 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")); - } 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 - */ - 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 - xsltApplyOneTemplateInt(ctxt, cur, template->content, - template, params, 0); - } 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; - xsltApplyOneTemplateInt(ctxt, cur, template->content, - template, params, 0); - } 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; - xsltApplyOneTemplateInt(ctxt, cur, template->content, - template, params, 0); - } - break; - default: - break; - } - cur = cur->next; - } - ctxt->xpathCtxt->contextSize = oldSize; - ctxt->xpathCtxt->proximityPosition = oldPos; -} - -/** - * xsltProcessOneNode: - * @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. - */ -void -xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node, - xsltStackElemPtr params) { - xsltTemplatePtr template; - xmlNodePtr oldNode; - - template = xsltGetTemplate(ctxt, node, NULL); - /* - * If no template is found, apply the default rule. - */ - if (template == NULL) { -#ifdef WITH_XSLT_DEBUG_PROCESS - if (node->type == XML_DOCUMENT_NODE) { - XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, - "xsltProcessOneNode: no template found for /\n")); - } else if (node->type == XML_CDATA_SECTION_NODE) { - XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, - "xsltProcessOneNode: no template found for CDATA\n")); - } else if (node->type == XML_ATTRIBUTE_NODE) { - XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, - "xsltProcessOneNode: no template found for attribute %s\n", - ((xmlAttrPtr) node)->name)); - } else { - XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, - "xsltProcessOneNode: no template found for %s\n", node->name)); - } -#endif - oldNode = ctxt->node; - ctxt->node = node; - xsltDefaultProcessOneNode(ctxt, node, params); - ctxt->node = oldNode; - return; - } - - if (node->type == XML_ATTRIBUTE_NODE) { -#ifdef WITH_XSLT_DEBUG_PROCESS - XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, - "xsltProcessOneNode: applying template '%s' for attribute %s\n", - template->match, node->name)); -#endif - xsltApplyOneTemplateInt(ctxt, node, template->content, template, params, 0); - } else { -#ifdef WITH_XSLT_DEBUG_PROCESS - if (node->type == XML_DOCUMENT_NODE) { - XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, - "xsltProcessOneNode: applying template '%s' for /\n", - template->match)); - } else { - XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, - "xsltProcessOneNode: applying template '%s' for %s\n", - template->match, node->name)); - } -#endif - xsltApplyOneTemplateInt(ctxt, node, template->content, template, params, 0); - } -} - -/** - * xsltApplyOneTemplate: - * @ctxt: a XSLT process context - * @node: the node in the source tree. - * @list: the template replacement nodelist - * @templ: if is this a real template processing, the template processed - * @params: a set of parameters for the template or NULL - * - * Process the apply-templates node on the source node, if params are passed - * they are pushed on the variable stack but not popped, it's left to the - * caller to handle them after return (they may be reused). - */ -void -xsltApplyOneTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNodePtr list, xsltTemplatePtr templ, - xsltStackElemPtr params) -{ - xsltApplyOneTemplateInt(ctxt, node, list, templ, params, 0); -} - -/** - * xsltApplyOneTemplateInt: - * @ctxt: a XSLT process context - * @node: the node in the source tree. - * @list: the template replacement nodelist - * @templ: if is this a real template processing, the template processed - * @params: a set of parameters for the template or NULL - * @notcur: flag to show current template rule doesn't change - * - * See above description for xsltApplyOneTemplate. Internally there is - * an additional parameter 'notcur'. When this parameter is non-zero, - * ctxt->templ is not changed (i.e. templPush and tempPop are not called). - * This is used by xsltCallTemplate in order to meet the XSLT spec (5.6) - * requirement that the "current template rule" should not be changed - * (bug 157859). - */ -static void -xsltApplyOneTemplateInt(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNodePtr list, xsltTemplatePtr templ, - xsltStackElemPtr params, int notcur) -{ - xmlNodePtr cur = NULL, insert, copy = NULL; - xmlNodePtr oldInsert; - xmlNodePtr oldCurrent = NULL; - xmlNodePtr oldInst = NULL; - int oldBase; - xmlDocPtr tmpRVT = NULL; -#ifdef XSLT_REFACTORED - xsltStylePreCompPtr info; -#endif - - int level = 0; - -#ifdef WITH_DEBUGGER - int addCallResult = 0; - xmlNodePtr debugedNode = NULL; -#endif - long start = 0; - - if (ctxt == NULL) return; - -#ifdef WITH_DEBUGGER - 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; - } - - if (templ) { - xslHandleDebugger(templ->elem, node, templ, ctxt); - debugedNode = templ->elem; - } else if (list) { - xslHandleDebugger(list, node, templ, ctxt); - debugedNode = list; - } else if (ctxt->inst) { - xslHandleDebugger(ctxt->inst, node, templ, ctxt); - debugedNode = ctxt->inst; - } - } -#endif - - if (list == NULL) - return; - CHECK_STOPPED; - - if ((ctxt->templNr >= xsltMaxDepth) || - (ctxt->varsNr >= 5 * xsltMaxDepth)) { - xsltTransformError(ctxt, NULL, list, - "xsltApplyOneTemplate: loop found ???\n"); - xsltGenericError(xsltGenericErrorContext, - "try increasing xsltMaxDepth (--maxdepth)\n"); - xsltDebug(ctxt, node, list, NULL); - return; - } - - /* - * stack saves, beware ordering of operations counts - */ - oldInsert = insert = ctxt->insert; - oldInst = ctxt->inst; - oldCurrent = ctxt->node; - varsPush(ctxt, params); - oldBase = ctxt->varsBase; /* only needed if templ != NULL */ - if (templ != NULL) { - ctxt->varsBase = ctxt->varsNr - 1; - ctxt->node = node; - if (ctxt->profile) { - templ->nbCalls++; - start = xsltTimestamp(); - profPush(ctxt, 0); - } - tmpRVT = ctxt->tmpRVT; - ctxt->tmpRVT = NULL; - if (!notcur) - templPush(ctxt, templ); -#ifdef WITH_XSLT_DEBUG_PROCESS - if (templ->name != NULL) - XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, - "applying template '%s'\n", templ->name)); -#endif - } - - /* - * Insert all non-XSLT nodes found in the template - */ - 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, - "xsltApplyOneTemplateInt: insert == NULL !\n")); -#endif - goto error; - } -#ifdef WITH_DEBUGGER - if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (debugedNode != cur)) - xslHandleDebugger(cur, node, 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, node, cur); - goto skip_children; - } - /* - * Something really went wrong: - */ - xsltTransformError(ctxt, NULL, cur, - "Internal error in xsltApplyOneTemplateInt(): " - "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, - "xsltApplyOneTemplateInt: 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 xsltApplyOneTemplateInt(): " - "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; - xmlAddChild(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 xsltApplyOneTemplateInt(): " - "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." - * TODO: Refactor this, since it still uses ns-aliasing. - * 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, node, cur)) { - xsltTransformError(ctxt, NULL, cur, - "The is no fallback behaviour defined for " - "the unknown XSLT element '%s'.\n", - cur->name); - } - ctxt->insert = oldInsert; - goto skip_children; - } - /* - * Execute the XSLT instruction. - */ - if (info->func != NULL) { - ctxt->insert = insert; - info->func(ctxt, node, cur, (xsltElemPreCompPtr) info); - ctxt->insert = oldInsert; - goto skip_children; - } - /* - * Some XSLT instructions need custom execution. - */ - if (info->type == XSLT_FUNC_VARIABLE) { - if (level != 0) { - /* - * Build a new subframe and skip all the nodes - * at that level. - */ - ctxt->insert = insert; - xsltApplyOneTemplateInt(ctxt, node, cur, NULL, NULL, 0); - while (cur->next != NULL) - cur = cur->next; - ctxt->insert = oldInsert; - } else { - xsltParseStylesheetVariable(ctxt, cur); - } - } else if (info->type == XSLT_FUNC_PARAM) { - xsltParseStylesheetParam(ctxt, cur); - } else if (info->type == XSLT_FUNC_MESSAGE) { - /* - * TODO: Won't be hit, since we don't compile xsl:message. - */ - xsltMessage(ctxt, node, cur); - } else { - xsltGenericError(xsltGenericErrorContext, - "Internal error in xsltApplyOneTemplateInt(): " - "Don't know how to process the 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, - "xsltApplyOneTemplate: unknown extension %s\n", - cur->name)); -#endif - ctxt->insert = insert; - if (!xsltApplyFallbacks(ctxt, node, 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, - "xsltApplyOneTemplate: extension construct %s\n", - cur->name)); -#endif - ctxt->insert = insert; - func(ctxt, node, cur, cur->psvi); - 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, - "xsltApplyOneTemplateInt: copy unescaped text '%s'\n", - cur->content)); - } else { - XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, - xsltGenericDebug(xsltGenericDebugContext, - "xsltApplyOneTemplateInt: 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, node, cur); - } else { - /* - * That's an error try to apply one of the fallback cases - */ - ctxt->insert = insert; - if (!xsltApplyFallbacks(ctxt, node, cur)) { - xsltGenericError(xsltGenericErrorContext, - "xsltApplyOneTemplate: %s was not compiled\n", - cur->name); - } - ctxt->insert = oldInsert; - } - goto skip_children; - } - - if (info->func != NULL) { - ctxt->insert = insert; - info->func(ctxt, node, cur, (xsltElemPreCompPtr) info); - ctxt->insert = oldInsert; - goto skip_children; - } - - if (IS_XSLT_NAME(cur, "variable")) { - if (level != 0) { - /* - * Build a new subframe and skip all the nodes - * at that level. - */ - ctxt->insert = insert; - xsltApplyOneTemplateInt(ctxt, node, cur, NULL, NULL, 0); - while (cur->next != NULL) - cur = cur->next; - ctxt->insert = oldInsert; - } else { - xsltParseStylesheetVariable(ctxt, cur); - } - } else if (IS_XSLT_NAME(cur, "param")) { - xsltParseStylesheetParam(ctxt, cur); - } else if (IS_XSLT_NAME(cur, "message")) { - xsltMessage(ctxt, node, cur); - } else { - xsltGenericError(xsltGenericErrorContext, - "xsltApplyOneTemplate: problem with xsl:%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, - "xsltApplyOneTemplate: copy CDATA text %s\n", - cur->content)); - } else if (cur->name == xmlStringTextNoenc) { - XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, - "xsltApplyOneTemplate: copy unescaped text %s\n", - cur->content)); - } else { - XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, - "xsltApplyOneTemplate: 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; - - /* - * 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, - "xsltApplyOneTemplate: 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; - xsltApplyOneTemplateInt(ctxt, node, child->children, - NULL, NULL, 0); - } - child = child->next; - } - - if (!found) { - xsltTransformError(ctxt, NULL, cur, - "xsltApplyOneTemplate: failed to find extension %s\n", - cur->name); - } - } else { -#ifdef WITH_XSLT_DEBUG_PROCESS - XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, - "xsltApplyOneTemplate: extension construct %s\n", - cur->name)); -#endif - - ctxt->insert = insert; - function(ctxt, node, cur, cur->psvi); - ctxt->insert = oldInsert; - } - goto skip_children; - } else if (cur->type == XML_ELEMENT_NODE) { -#ifdef WITH_XSLT_DEBUG_PROCESS - XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, - "xsltApplyOneTemplate: copy node %s\n", - cur->name)); -#endif - 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); - } - } -#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--; - 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: - ctxt->node = oldCurrent; - ctxt->inst = oldInst; - ctxt->insert = oldInsert; - if (params == NULL) - xsltFreeStackElemList(varsPop(ctxt)); - else { - xsltStackElemPtr p, tmp = varsPop(ctxt); - - if (tmp != params) { - p = tmp; - while ((p != NULL) && (p->next != params)) - p = p->next; - if (p == NULL) { - xsltFreeStackElemList(tmp); - } else { - p->next = NULL; - xsltFreeStackElemList(tmp); - } - } - } - if (templ != NULL) { - ctxt->varsBase = oldBase; - if (!notcur) - templPop(ctxt); - /* - * Free up all the unreferenced RVT - * Also set any global variables instantiated - * using them, to be "not yet computed". - */ - if (ctxt->tmpRVT != NULL) { - xsltStackElemPtr elem; - xmlDocPtr tmp = ctxt->tmpRVT, next; - while (tmp != NULL) { - elem = (xsltStackElemPtr)tmp->psvi; - if (elem != NULL) { - elem->computed = 0; - xmlXPathFreeObject(elem->value); - } - next = (xmlDocPtr) tmp->next; - if (tmp->_private != NULL) { - xsltFreeDocumentKeys(tmp->_private); - xmlFree(tmp->_private); - } - xmlFreeDoc(tmp); - tmp = next; - } - } - ctxt->tmpRVT = tmpRVT; - 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 -} - -/************************************************************************ - * * - * XSLT-1.1 extensions * - * * - ************************************************************************/ - -/** - * xsltDocumentElem: - * @ctxt: an XSLT processing context - * @node: The current node - * @inst: the instruction in the stylesheet - * @comp: 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; - - 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) - - 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", - style->method); - 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", - style->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 (style->encoding != NULL) - res->encoding = xmlStrdup(style->encoding); - ctxt->output = res; - ctxt->insert = (xmlNodePtr) res; - xsltApplyOneTemplateInt(ctxt, node, inst->children, NULL, NULL, 0); - - /* - * 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); - } - } - - /* - * Save the result - */ - ret = xsltSaveResultToFilename((const char *) filename, - res, style, 0); - if (ret < 0) { - xsltTransformError(ctxt, NULL, inst, - "xsltDocumentElem: unable to save to %s\n", - filename); - ctxt->state = XSLT_STATE_ERROR; -#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 - * @comp: 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); - xmlAddChild(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); - xmlAddChild(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: - xsltApplyOneTemplateInt(ctxt, ctxt->node, inst->children, - NULL, NULL, 0); - 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; - } - xmlAddChild(ctxt->insert, copy); - text = text->next; - } - } -} - -/** - * xsltElement: - * @ctxt: a XSLT process context - * @node: the node in the source tree. - * @inst: the xslt element node - * @comp: 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); - if ((prefix != NULL) && - (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3))) - { - /* - * TODO: Should we really disallow an "xml" prefix? - */ - goto error; - } - } 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; - } - xmlAddChild(ctxt->insert, copy); - - /* - * 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); - }; - } 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) { - 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) - xsltApplyOneTemplateInt(ctxt, ctxt->node, inst->children, - NULL, NULL, 0); - -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); - xmlAddChild(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 - * @comp: 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); - xmlAddChild(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 - * @comp: 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; - int oldProximityPosition, oldContextSize; - int oldNsNr; - xmlNsPtr *oldNamespaces; - - 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 - - /* - * Set up the XPath evaluation context. - */ - oldProximityPosition = ctxt->xpathCtxt->proximityPosition; - oldContextSize = ctxt->xpathCtxt->contextSize; - oldNsNr = ctxt->xpathCtxt->nsNr; - oldNamespaces = ctxt->xpathCtxt->namespaces; - ctxt->xpathCtxt->node = node; -#ifdef XSLT_REFACTORED - if (comp->inScopeNs != NULL) { - ctxt->xpathCtxt->namespaces = comp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber; - } else { - ctxt->xpathCtxt->namespaces = NULL; - ctxt->xpathCtxt->nsNr = 0; - } -#else - ctxt->xpathCtxt->namespaces = comp->nsList; - ctxt->xpathCtxt->nsNr = comp->nsNr; -#endif - /* - * Evaluate the "select" expression. - */ - res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt); - /* - * Revert the XPath evaluation context to previous state. - */ - ctxt->xpathCtxt->proximityPosition = oldProximityPosition; - ctxt->xpathCtxt->contextSize = oldContextSize; - ctxt->xpathCtxt->nsNr = oldNsNr; - ctxt->xpathCtxt->namespaces = oldNamespaces; - - 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 ) - * -------------------- - */ -#ifdef WITH_XSLT_DEBUG_PROCESS - XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, - "xsltCopyOf: result is a result tree fragment\n")); -#endif - /* - * TODO: Is list->nodeTab[0] is an xmlDocPtr? - */ - 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 { - /* Convert to a string. */ - res = xmlXPathConvertString(res); - if ((res != NULL) && (res->type == XPATH_STRING)) { -#ifdef WITH_XSLT_DEBUG_PROCESS - XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, - "xsltCopyOf: result %s\n", res->stringval)); -#endif - /* Append content as text node. */ - xsltCopyTextString(ctxt, ctxt->insert, res->stringval, 0); - } - } - } 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 - * @comp: 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; - xmlNodePtr copy = NULL; - int oldProximityPosition, oldContextSize; - int oldNsNr; - xmlNsPtr *oldNamespaces; - - if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) - return; - if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) { - xsltTransformError(ctxt, NULL, inst, - "xsl:value-of : compilation failed\n"); - return; - } - -#ifdef WITH_XSLT_DEBUG_PROCESS - XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext, - "xsltValueOf: select %s\n", comp->select)); -#endif - - oldProximityPosition = ctxt->xpathCtxt->proximityPosition; - oldContextSize = ctxt->xpathCtxt->contextSize; - oldNsNr = ctxt->xpathCtxt->nsNr; - oldNamespaces = ctxt->xpathCtxt->namespaces; - ctxt->xpathCtxt->node = node; -#ifdef XSLT_REFACTORED - if (comp->inScopeNs != NULL) { - ctxt->xpathCtxt->namespaces = comp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber; - } else { - ctxt->xpathCtxt->namespaces = NULL; - ctxt->xpathCtxt->nsNr = 0; - } -#else - ctxt->xpathCtxt->namespaces = comp->nsList; - ctxt->xpathCtxt->nsNr = comp->nsNr; -#endif - res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt); - ctxt->xpathCtxt->proximityPosition = oldProximityPosition; - ctxt->xpathCtxt->contextSize = oldContextSize; - ctxt->xpathCtxt->nsNr = oldNsNr; - ctxt->xpathCtxt->namespaces = oldNamespaces; - if (res != NULL) { - if (res->type != XPATH_STRING) - res = xmlXPathConvertString(res); - if (res->type == XPATH_STRING) { - copy = xsltCopyTextString(ctxt, ctxt->insert, res->stringval, - comp->noescape); - } - } else { - ctxt->state = XSLT_STATE_STOPPED; - } - if (copy == NULL) { - if ((res == NULL) || (res->stringval != NULL)) { - xsltTransformError(ctxt, NULL, inst, - "xsltValueOf: text copy failed\n"); - } - } -#ifdef WITH_XSLT_DEBUG_PROCESS - else - XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext, - "xsltValueOf: result %s\n", res->stringval)); -#endif - if (res != NULL) - xmlXPathFreeObject(res); -} - -/** - * xsltNumber: - * @ctxt: a XSLT process context - * @node: the node in the source tree. - * @inst: the xslt number node - * @comp: 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 - 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; - - xsltNumberFormat(ctxt, &comp->numdata, node); -} - -/** - * xsltApplyImports: - * @ctxt: a XSLT process context - * @node: the node in the source tree. - * @inst: the xslt apply-imports node - * @comp: precomputed information - * - * Process the xslt apply-imports node on the source node - */ -void -xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNodePtr inst, - xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) { - xsltTemplatePtr template; - - if ((ctxt->templ == NULL) || (ctxt->templ->style == NULL)) { - xsltTransformError(ctxt, NULL, inst, - "xsl:apply-imports : internal error no current template\n"); - return; - } - template = xsltGetTemplate(ctxt, node, ctxt->templ->style); - if (template != NULL) { - xsltApplyOneTemplateInt(ctxt, node, template->content, template, NULL, 0); - } -} - -/** - * xsltCallTemplate: - * @ctxt: a XSLT process context - * @node: the node in the source tree. - * @inst: the xslt call-template node - * @comp: precomputed information - * - * Process the xslt call-template node 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 - xmlNodePtr cur = NULL; - xsltStackElemPtr params = NULL, param; - - if (ctxt->insert == NULL) - return; - if (comp == NULL) { - xsltTransformError(ctxt, NULL, inst, - "xsl:call-template : compilation failed\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, - "xsl:call-template : template %s:%s not found\n", - comp->ns, comp->name); - } else { - xsltTransformError(ctxt, NULL, inst, - "xsl:call-template : template %s 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 - - 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. - */ -#ifdef XSLT_REFACTORED - if (IS_XSLT_ELEM_FAST(cur)) { -#else - if (IS_XSLT_ELEM(cur)) { -#endif - if (IS_XSLT_NAME(cur, "with-param")) { - param = xsltParseStylesheetCallerParam(ctxt, cur); - if (param != NULL) { - param->next = params; - params = 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 - * Set the "notcur" flag to abide by Section 5.6 of the spec - */ - xsltApplyOneTemplateInt(ctxt, node, comp->templ->content, comp->templ, params, 1); - if (params != NULL) - xsltFreeStackElemList(params); - -#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 process context - * @node: the node in the source tree. - * @inst: the apply-templates node - * @comp: precomputed information - * - * Process the apply-templates node on the source node - */ -void -xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNodePtr inst, xsltStylePreCompPtr castedComp) -{ -#ifdef XSLT_REFACTORED - xsltStyleItemApplyTemplatesPtr comp = - (xsltStyleItemApplyTemplatesPtr) castedComp; -#else - xsltStylePreCompPtr comp = castedComp; -#endif - xmlNodePtr cur, delete = NULL, oldNode; - xmlXPathObjectPtr res = NULL; - xmlNodeSetPtr list = NULL, oldList; - int i, oldProximityPosition, oldContextSize; - const xmlChar *oldMode, *oldModeURI; - xsltStackElemPtr params = NULL, param; - int nbsorts = 0; - xmlNodePtr sorts[XSLT_MAX_SORT]; - xmlDocPtr oldXDocPtr; - xsltDocumentPtr oldCDocPtr; - int oldNsNr; - xmlNsPtr *oldNamespaces; - - 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 - - /* - * Get mode if any - */ - oldNode = ctxt->node; - oldMode = ctxt->mode; - oldModeURI = ctxt->modeURI; - ctxt->mode = comp->mode; - ctxt->modeURI = comp->modeURI; - - /* - * 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 - */ - oldXDocPtr = ctxt->xpathCtxt->doc; - oldCDocPtr = ctxt->document; - oldContextSize = ctxt->xpathCtxt->contextSize; - oldProximityPosition = ctxt->xpathCtxt->proximityPosition; - oldNsNr = ctxt->xpathCtxt->nsNr; - oldNamespaces = ctxt->xpathCtxt->namespaces; - oldList = ctxt->nodeList; - - if (comp->select != 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 - - ctxt->xpathCtxt->node = node; -#ifdef XSLT_REFACTORED - if (comp->inScopeNs != NULL) { - ctxt->xpathCtxt->namespaces = comp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber; - } else { - ctxt->xpathCtxt->namespaces = NULL; - ctxt->xpathCtxt->nsNr = 0; - } -#else - ctxt->xpathCtxt->namespaces = comp->nsList; - ctxt->xpathCtxt->nsNr = comp->nsNr; -#endif - res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt); - ctxt->xpathCtxt->contextSize = oldContextSize; - ctxt->xpathCtxt->proximityPosition = oldProximityPosition; - if (res != NULL) { - if (res->type == XPATH_NODESET) { - list = res->nodesetval; - res->nodesetval = NULL; - /* - In order to take care of potential keys we need to - do some extra work in the case of an RVT converted - into a nodeset (e.g. exslt:node-set()) - We create a "pseudo-doc" (if not already created) and - store it's pointer into _private. This doc, together - with the keyset, will be freed when the RVT is freed. - */ - if ((list != NULL) && (ctxt->document->keys != NULL)) { - if ((list->nodeNr != 0) && - (list->nodeTab[0]->doc != NULL) && - - XSLT_IS_RES_TREE_FRAG(list->nodeTab[0]->doc) && - - (list->nodeTab[0]->doc->_private == NULL)) { - list->nodeTab[0]->doc->_private = xsltNewDocument( - ctxt, list->nodeTab[0]->doc); - if (list->nodeTab[0]->doc->_private == NULL) { - xsltTransformError(ctxt, NULL, inst, - "xsltApplyTemplates : failed to allocate subdoc\n"); - } - - ctxt->document = list->nodeTab[0]->doc->_private; - } - - } - } else { - list = NULL; - } - } else { - ctxt->state = XSLT_STATE_STOPPED; - } - 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 error; - } - } else { - /* - * Build an XPath nodelist with the children - */ - list = xmlXPathNodeSetCreate(NULL); - cur = node->children; - 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"))) { - delete = 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; - default: -#ifdef WITH_XSLT_DEBUG_PROCESS - XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, - "xsltApplyTemplates: skipping cur type %d\n", - cur->type)); -#endif - delete = cur; - } - cur = cur->next; - if (delete != NULL) { -#ifdef WITH_XSLT_DEBUG_PROCESS - XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, - "xsltApplyTemplates: removing ignorable blank cur\n")); -#endif - xmlUnlinkNode(delete); - xmlFreeNode(delete); - delete = 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 - - ctxt->nodeList = list; - ctxt->xpathCtxt->contextSize = list->nodeNr; - - /* - * handle (or skip) the xsl:sort and xsl:with-param - */ - cur = inst->children; - while (cur!=NULL) { -#ifdef WITH_DEBUGGER - if (ctxt->debugStatus != XSLT_DEBUG_NONE) -#ifdef XSLT_REFACTORED - xslHandleDebugger(cur, node, NULL, ctxt); -#else - /* TODO: Isn't comp->templ always NULL for apply-template? */ - xslHandleDebugger(cur, node, comp->templ, ctxt); -#endif -#endif - if (ctxt->state == XSLT_STATE_STOPPED) break; -#ifdef XSLT_REFACTORED - if (IS_XSLT_ELEM_FAST(cur)) { -#else - if (IS_XSLT_ELEM(cur)) { -#endif - if (IS_XSLT_NAME(cur, "with-param")) { - param = xsltParseStylesheetCallerParam(ctxt, cur); - if (param != NULL) { - param->next = params; - params = param; - } - } else if (IS_XSLT_NAME(cur, "sort")) { - if (nbsorts >= XSLT_MAX_SORT) { - xsltGenericError(xsltGenericErrorContext, - "xsl:apply-template: %s too many sort\n", node->name); - } else { - sorts[nbsorts++] = cur; - } - } else { - xsltGenericError(xsltGenericErrorContext, - "xsl:apply-template: misplaced xsl:%s\n", cur->name); - } - } else { - xsltGenericError(xsltGenericErrorContext, - "xsl:apply-template: misplaced %s element\n", cur->name); - } - cur = cur->next; - } - - if (nbsorts > 0) { - xsltDoSortFunction(ctxt, sorts, nbsorts); - } - - for (i = 0;i < list->nodeNr;i++) { - ctxt->node = list->nodeTab[i]; - ctxt->xpathCtxt->proximityPosition = i + 1; - /* For a 'select' nodeset, need to check if document has changed */ - if ((IS_XSLT_REAL_NODE(list->nodeTab[i])) && - (list->nodeTab[i]->doc!=NULL) && - (list->nodeTab[i]->doc->doc!=NULL) && - (list->nodeTab[i]->doc->doc)!=ctxt->xpathCtxt->doc) { - /* The nodeset is from another document, so must change */ - ctxt->xpathCtxt->doc=list->nodeTab[i]->doc->doc; - if ((list->nodeTab[i]->doc->name != NULL) || - (list->nodeTab[i]->doc->URL != NULL)) { - ctxt->document = xsltFindDocument(ctxt, - list->nodeTab[i]->doc->doc); - if (ctxt->document == NULL) { - /* restore the previous context */ - ctxt->document = oldCDocPtr; - } - ctxt->xpathCtxt->node = list->nodeTab[i]; -#ifdef WITH_XSLT_DEBUG_PROCESS - if ((ctxt->document != NULL) && - (ctxt->document->doc != NULL)) { - XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, - "xsltApplyTemplates: Changing document - context doc %s, xpathdoc %s\n", - ctxt->document->doc->URL, ctxt->xpathCtxt->doc->URL)); - } else { - XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, - "xsltApplyTemplates: Changing document - Return tree fragment\n")); - } -#endif - } - } - xsltProcessOneNode(ctxt, list->nodeTab[i], params); - } -error: - if (params != NULL) - xsltFreeStackElemList(params); /* free the parameter list */ - if (list != NULL) - xmlXPathFreeNodeSet(list); - /* - * res must be deallocated after list - */ - if (res != NULL) - xmlXPathFreeObject(res); - - ctxt->nodeList = oldList; - ctxt->xpathCtxt->contextSize = oldContextSize; - ctxt->xpathCtxt->proximityPosition = oldProximityPosition; - ctxt->xpathCtxt->doc = oldXDocPtr; - ctxt->document = oldCDocPtr; - ctxt->xpathCtxt->nsNr = oldNsNr; - ctxt->xpathCtxt->namespaces = oldNamespaces; - - ctxt->node = oldNode; - ctxt->mode = oldMode; - ctxt->modeURI = oldModeURI; -} - - -/** - * xsltChoose: - * @ctxt: a XSLT process context - * @node: the node in the source tree. - * @inst: the xslt choose node - * @comp: precomputed information - * - * Process the xslt choose node on the source node - */ -void -xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) -{ - xmlXPathObjectPtr res = NULL; - xmlNodePtr replacement, when; - int doit = 1; - int oldProximityPosition, oldContextSize; - int oldNsNr; - xmlNsPtr *oldNamespaces; - - if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) - return; - - /* - * Check the when's - */ - replacement = inst->children; - if (replacement == NULL) { - xsltTransformError(ctxt, NULL, inst, - "xsl:choose: empty content not allowed\n"); - goto error; - } -#ifdef XSLT_REFACTORED - if (((!IS_XSLT_ELEM_FAST(replacement)) || -#else - if (((!IS_XSLT_ELEM(replacement)) || -#endif - (!IS_XSLT_NAME(replacement, "when"))) - && (!xmlIsBlankNode(replacement))) { - xsltTransformError(ctxt, NULL, inst, - "xsl:choose: xsl:when expected first\n"); - goto error; - } -#ifdef XSLT_REFACTORED - while ((IS_XSLT_ELEM_FAST(replacement) && -#else - while ((IS_XSLT_ELEM(replacement) && -#endif - (IS_XSLT_NAME(replacement, "when"))) - || xmlIsBlankNode(replacement)) { -#ifdef XSLT_REFACTORED - xsltStyleItemWhenPtr wcomp = - (xsltStyleItemWhenPtr) replacement->psvi; -#else - xsltStylePreCompPtr wcomp = replacement->psvi; -#endif - - if (xmlIsBlankNode(replacement)) { - replacement = replacement->next; - continue; - } - - if ((wcomp == NULL) || (wcomp->test == NULL) || (wcomp->comp == NULL)) { - xsltTransformError(ctxt, NULL, inst, - "xsl:choose: compilation failed !\n"); - goto error; - } - when = replacement; - - -#ifdef WITH_DEBUGGER - if (xslDebugStatus != XSLT_DEBUG_NONE) -#ifdef XSLT_REFACTORED - xslHandleDebugger(when, node, NULL, ctxt); -#else - /* TODO: Isn't comp->templ always NULL for xsl:choose? */ - xslHandleDebugger(when, node, comp->templ, ctxt); -#endif -#endif - -#ifdef WITH_XSLT_DEBUG_PROCESS - XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, - "xsltChoose: test %s\n", wcomp->test)); -#endif - - oldProximityPosition = ctxt->xpathCtxt->proximityPosition; - oldContextSize = ctxt->xpathCtxt->contextSize; - oldNsNr = ctxt->xpathCtxt->nsNr; - oldNamespaces = ctxt->xpathCtxt->namespaces; - ctxt->xpathCtxt->node = node; -#ifdef XSLT_REFACTORED - if (wcomp->inScopeNs != NULL) { - ctxt->xpathCtxt->namespaces = wcomp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = wcomp->inScopeNs->xpathNumber; - } else { - ctxt->xpathCtxt->namespaces = NULL; - ctxt->xpathCtxt->nsNr = 0; - } -#else - ctxt->xpathCtxt->namespaces = wcomp->nsList; - ctxt->xpathCtxt->nsNr = wcomp->nsNr; -#endif - res = xmlXPathCompiledEval(wcomp->comp, ctxt->xpathCtxt); - ctxt->xpathCtxt->proximityPosition = oldProximityPosition; - ctxt->xpathCtxt->contextSize = oldContextSize; - ctxt->xpathCtxt->nsNr = oldNsNr; - ctxt->xpathCtxt->namespaces = oldNamespaces; - if (res != NULL) { - if (res->type != XPATH_BOOLEAN) - res = xmlXPathConvertBoolean(res); - if (res->type == XPATH_BOOLEAN) - doit = 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; - } - } else { - ctxt->state = XSLT_STATE_STOPPED; - } - -#ifdef WITH_XSLT_DEBUG_PROCESS - XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, - "xsltChoose: test evaluate to %d\n", doit)); -#endif - if (doit) { - xsltApplyOneTemplateInt(ctxt, ctxt->node, when->children, - NULL, NULL, 0); - goto done; - } - if (res != NULL) - xmlXPathFreeObject(res); - res = NULL; - replacement = replacement->next; - } -#ifdef XSLT_REFACTORED - if (IS_XSLT_ELEM_FAST(replacement) && -#else - if (IS_XSLT_ELEM(replacement) && -#endif - (IS_XSLT_NAME(replacement, "otherwise"))) { -#ifdef WITH_DEBUGGER - if (xslDebugStatus != XSLT_DEBUG_NONE) -#ifdef XSLT_REFACTORED - xslHandleDebugger(replacement, node, NULL, ctxt); -#else - /* TODO: Isn't comp->templ always NULL for xsl:otherwise? */ - xslHandleDebugger(replacement, node, comp->templ, ctxt); -#endif -#endif - -#ifdef WITH_XSLT_DEBUG_PROCESS - XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, - "evaluating xsl:otherwise\n")); -#endif - xsltApplyOneTemplateInt(ctxt, ctxt->node, replacement->children, - NULL, NULL, 0); - replacement = replacement->next; - } - while (xmlIsBlankNode(replacement)) { - replacement = replacement->next; - } - if (replacement != NULL) { - xsltTransformError(ctxt, NULL, inst, - "xsl:choose: unexpected content %s\n", replacement->name); - goto error; - } - -done: -error: - if (res != NULL) - xmlXPathFreeObject(res); -} - -/** - * xsltIf: - * @ctxt: a XSLT process context - * @node: the node in the source tree. - * @inst: the xslt if node - * @comp: precomputed information - * - * Process the xslt if node on the source node - */ -void -xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNodePtr inst, xsltStylePreCompPtr castedComp){ -#ifdef XSLT_REFACTORED - xsltStyleItemIfPtr comp = (xsltStyleItemIfPtr) castedComp; -#else - xsltStylePreCompPtr comp = castedComp; -#endif - xmlXPathObjectPtr res = NULL; - int doit = 1; - int oldContextSize, oldProximityPosition; - int oldNsNr; - xmlNsPtr *oldNamespaces; - - if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) - return; - if ((comp == NULL) || (comp->test == NULL) || (comp->comp == NULL)) { - xsltTransformError(ctxt, NULL, inst, - "xsl:if : compilation failed\n"); - return; - } - -#ifdef WITH_XSLT_DEBUG_PROCESS - XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, - "xsltIf: test %s\n", comp->test)); -#endif - - oldContextSize = ctxt->xpathCtxt->contextSize; - oldProximityPosition = ctxt->xpathCtxt->proximityPosition; - oldNsNr = ctxt->xpathCtxt->nsNr; - oldNamespaces = ctxt->xpathCtxt->namespaces; - ctxt->xpathCtxt->node = node; -#ifdef XSLT_REFACTORED - if (comp->inScopeNs != NULL) { - ctxt->xpathCtxt->namespaces = comp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber; - } else { - ctxt->xpathCtxt->namespaces = NULL; - ctxt->xpathCtxt->nsNr = 0; - } -#else - ctxt->xpathCtxt->namespaces = comp->nsList; - ctxt->xpathCtxt->nsNr = comp->nsNr; -#endif - /* - * OPTIMIZE TODO: Use a specialized function, which returns only - * true/false. - */ - res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt); - ctxt->xpathCtxt->contextSize = oldContextSize; - ctxt->xpathCtxt->proximityPosition = oldProximityPosition; - ctxt->xpathCtxt->nsNr = oldNsNr; - ctxt->xpathCtxt->namespaces = oldNamespaces; - if (res != NULL) { - if (res->type != XPATH_BOOLEAN) - res = xmlXPathConvertBoolean(res); - if (res->type == XPATH_BOOLEAN) - doit = res->boolval; - else { -#ifdef WITH_XSLT_DEBUG_PROCESS - XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, - "xsltIf: test didn't evaluate to a boolean\n")); -#endif - goto error; - } - } else { - ctxt->state = XSLT_STATE_STOPPED; - } - -#ifdef WITH_XSLT_DEBUG_PROCESS - XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, - "xsltIf: test evaluate to %d\n", doit)); -#endif - if (doit) { - xsltApplyOneTemplateInt(ctxt, node, inst->children, NULL, NULL, 0); - } - -error: - if (res != NULL) - xmlXPathFreeObject(res); -} - -/** - * xsltForEach: - * @ctxt: a XSLT process context - * @node: the node in the source tree. - * @inst: the xslt for-each node - * @comp: precomputed information - * - * Process the xslt for-each node on the source node - */ -void -xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNodePtr inst, xsltStylePreCompPtr castedComp) -{ -#ifdef XSLT_REFACTORED - xsltStyleItemForEachPtr comp = (xsltStyleItemForEachPtr) castedComp; -#else - xsltStylePreCompPtr comp = castedComp; -#endif - xmlXPathObjectPtr res = NULL; - xmlNodePtr replacement; - xmlNodeSetPtr list = NULL, oldList; - int i, oldProximityPosition, oldContextSize; - xmlNodePtr oldNode; - int nbsorts = 0; - xmlNodePtr sorts[XSLT_MAX_SORT]; - xmlDocPtr oldXDocPtr; - xsltDocumentPtr oldCDocPtr; - int oldNsNr; - xmlNsPtr *oldNamespaces; - - if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) - return; - if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) { - xsltTransformError(ctxt, NULL, inst, - "xsl:for-each : compilation failed\n"); - return; - } - oldNode = ctxt->node; - -#ifdef WITH_XSLT_DEBUG_PROCESS - XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, - "xsltForEach: select %s\n", comp->select)); -#endif - - oldProximityPosition = ctxt->xpathCtxt->proximityPosition; - oldContextSize = ctxt->xpathCtxt->contextSize; - oldNsNr = ctxt->xpathCtxt->nsNr; - oldNamespaces = ctxt->xpathCtxt->namespaces; - ctxt->xpathCtxt->node = node; -#ifdef XSLT_REFACTORED - if (comp->inScopeNs != NULL) { - ctxt->xpathCtxt->namespaces = comp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber; - } else { - ctxt->xpathCtxt->namespaces = NULL; - ctxt->xpathCtxt->nsNr = 0; - } -#else - ctxt->xpathCtxt->namespaces = comp->nsList; - ctxt->xpathCtxt->nsNr = comp->nsNr; -#endif - oldCDocPtr = ctxt->document; - oldXDocPtr = ctxt->xpathCtxt->doc; - res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt); - ctxt->xpathCtxt->contextSize = oldContextSize; - ctxt->xpathCtxt->proximityPosition = oldProximityPosition; - ctxt->xpathCtxt->nsNr = oldNsNr; - ctxt->xpathCtxt->namespaces = oldNamespaces; - if (res != NULL) { - if (res->type == XPATH_NODESET) - list = res->nodesetval; - } else { - ctxt->state = XSLT_STATE_STOPPED; - } - if (list == NULL) { -#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; - } - -#ifdef WITH_XSLT_DEBUG_PROCESS - XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, - "xsltForEach: select evaluates to %d nodes\n", list->nodeNr)); -#endif - - oldList = ctxt->nodeList; - ctxt->nodeList = list; - oldContextSize = ctxt->xpathCtxt->contextSize; - oldProximityPosition = ctxt->xpathCtxt->proximityPosition; - ctxt->xpathCtxt->contextSize = list->nodeNr; - - /* - * handle and skip the xsl:sort - */ - replacement = inst->children; -#ifdef XSLT_REFACTORED - while (IS_XSLT_ELEM_FAST(replacement) && -#else - while (IS_XSLT_ELEM(replacement) && -#endif - (IS_XSLT_NAME(replacement, "sort"))) { - if (nbsorts >= XSLT_MAX_SORT) { - xsltGenericError(xsltGenericErrorContext, - "xsl:for-each: too many sorts\n"); - } else { - sorts[nbsorts++] = replacement; - } -#ifdef WITH_DEBUGGER - if (xslDebugStatus != XSLT_DEBUG_NONE) - xslHandleDebugger(replacement, node, NULL, ctxt); -#endif - replacement = replacement->next; - } - - if (nbsorts > 0) { - xsltDoSortFunction(ctxt, sorts, nbsorts); - } - - - for (i = 0;i < list->nodeNr;i++) { - ctxt->node = list->nodeTab[i]; - ctxt->xpathCtxt->proximityPosition = i + 1; - /* For a 'select' nodeset, need to check if document has changed */ - if ((IS_XSLT_REAL_NODE(list->nodeTab[i])) && - (list->nodeTab[i]->doc!=NULL) && - (list->nodeTab[i]->doc->doc!=NULL) && - (list->nodeTab[i]->doc->doc)!=ctxt->xpathCtxt->doc) { - /* The nodeset is from another document, so must change */ - ctxt->xpathCtxt->doc=list->nodeTab[i]->doc->doc; - if ((list->nodeTab[i]->doc->name != NULL) || - (list->nodeTab[i]->doc->URL != NULL)) { - ctxt->document = xsltFindDocument(ctxt, - list->nodeTab[i]->doc->doc); - if (ctxt->document == NULL) { - /* restore the previous context */ - ctxt->document = oldCDocPtr; - } - ctxt->xpathCtxt->node = list->nodeTab[i]; -#ifdef WITH_XSLT_DEBUG_PROCESS - if ((ctxt->document != NULL) && - (ctxt->document->doc != NULL)) { - XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, - "xsltForEach: Changing document - context doc %s, xpathdoc %s\n", - ctxt->document->doc->URL, ctxt->xpathCtxt->doc->URL)); - } else { - XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, - "xsltForEach: Changing document - Return tree fragment\n")); - } -#endif - } - } - xsltApplyOneTemplateInt(ctxt, list->nodeTab[i], replacement, NULL, NULL, 0); - } - ctxt->document = oldCDocPtr; - ctxt->nodeList = oldList; - ctxt->node = oldNode; - ctxt->xpathCtxt->doc = oldXDocPtr; - ctxt->xpathCtxt->contextSize = oldContextSize; - ctxt->xpathCtxt->proximityPosition = oldProximityPosition; - ctxt->xpathCtxt->nsNr = oldNsNr; - ctxt->xpathCtxt->namespaces = oldNamespaces; - -error: - if (res != NULL) - xmlXPathFreeObject(res); -} - -/************************************************************************ - * * - * 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; -} - -#ifdef XSLT_REFACTORED_KEYCOMP -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); -} -#endif /* XSLT_REFACTORED_KEYCOMP */ - -/** - * 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; - xsltStackElemPtr variables; - xsltStackElemPtr vptr; - - 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); - - 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) - - 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", - style->method); - 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", - style->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 (style->encoding != NULL) - res->encoding = xmlStrdup(style->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)); - ctxt->output = res; - ctxt->insert = (xmlNodePtr) res; - if (ctxt->globalVars == NULL) - ctxt->globalVars = xmlHashCreate(20); - if (params != NULL) - xsltEvalUserParams(ctxt, params); - xsltEvalGlobalVariables(ctxt); -#ifdef XSLT_REFACTORED_KEYCOMP - xsltCountKeys(ctxt); -#endif - ctxt->node = (xmlNodePtr) doc; - varsPush(ctxt, NULL); - ctxt->varsBase = ctxt->varsNr - 1; - xsltProcessOneNode(ctxt, ctxt->node, NULL); - xsltFreeStackElemList(varsPop(ctxt)); - 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; - } - - - /* - * 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_ERROR)) { - 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); - } - } - - if ((ctxt != NULL) && (userCtxt == NULL)) - xsltFreeTransformContext(ctxt); - - return (res); - -error: - if (res != NULL) - xmlFreeDoc(res); - 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); - -} +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#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; + +/* + * 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); + } + } + 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); +} + +/** + * xsltVariablePop: + * @ctxt: the transformation context + * @depth: the depth in the xsl:template's tree + * + * Pops all variable values at the given @depth from the stack. + * + * Returns the stored variable value + */ +static 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); + } + } + 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); +} + +/************************************************************************ + * * + * 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; + + 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 dictionnary 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; + + /* + * 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; + + /* + * 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(); + + 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 dictionnary\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); + +/** + * 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 save 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) { + if (target != NULL) + xmlAddChild(target, copy); + 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 dictionnary + * + * 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: . + * 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. + */ + xmlAddChild(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 last = NULL, copy; + xmlNsPtr origNs = NULL, copyNs = NULL; + xmlChar *value = NULL; + + /* + * 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 (attr->children) + value = xmlNodeListGetString(attr->doc, attr->children, 1); + /* + * REVISIT: I think xmlNewDocProp() is the only attr function + * which does not eval if the attr is of type ID. This is good, + * since we don't need this. + */ + copy = xmlNewDocProp(target->doc, attr->name, BAD_CAST value); + if (copy == NULL) + return(-1); + copy->parent = target; + copy->ns = copyNs; + + if (last == NULL) { + target->properties = copy; + last = copy; + } else { + last->next = copy; + copy->prev = last; + last = copy; + } + /* + * OPTIMIZE TODO: How to avoid this intermediate string? + */ + if (value != NULL) { + xmlFree(value); + value = NULL; + } + 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; + xmlAddChild(insert, 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. + * @literal: is this a literal result element list + * + * 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 int +xsltShallowCopyNsNode(xsltTransformContextPtr ctxt, + xmlNodePtr invocNode, + xmlNodePtr insert, + xmlNsPtr ns) +{ + xmlNsPtr tmpns; + + if ((insert == NULL) || (insert->type != XML_ELEMENT_NODE)) + return(-1); + + if (insert->children != NULL) { + xsltTransformError(ctxt, NULL, invocNode, + "Namespace nodes must be added before " + "any child nodes are added to an element.\n"); + return(1); + } + /* + * + * 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. + * , 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")) + { + return(0); + } + + 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(0); + goto occupied; + } + } + tmpns = tmpns->next; + } while (tmpns != NULL); + } + tmpns = xmlSearchNs(insert->doc, insert, ns->prefix); + if ((tmpns != NULL) && xmlStrEqual(tmpns->href, ns->href)) + return(0); + /* + * 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 :-/ + */ + xmlNewNs(insert, ns->href, ns->prefix); + return(0); + +occupied: + /* + * TODO: We could as well raise an error here (like Saxon does), + * or at least generate a warning. + */ + return(0); +} + +/** + * 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; + xmlAddChild(insert, 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 * + * * + ************************************************************************/ + +void xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node, + xsltStackElemPtr params); +/** + * 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: + * + * + * + * + * and + * + * + * + * + * + * 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")); + } 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 + */ + 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); +} + +static 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; + + while ((cur != NULL) && (cur != base)) { + if (cur->psvi == (void *) ((long) 1)) { + cur = (xmlDocPtr) cur->next; + } else { + tmp = cur; + cur = (xmlDocPtr) cur->next; + + if (tmp == ctxt->localRVT) + ctxt->localRVT = (xmlDocPtr) tmp->next; + + /* + * We need ctxt->localRVTBase for extension instructions + * which return values (like EXSLT's function). + */ + if (tmp == ctxt->localRVTBase) + ctxt->localRVTBase = (xmlDocPtr) tmp->next; + + if (tmp->prev) + tmp->prev->next = (xmlNodePtr) cur; + if (cur) + cur->prev = tmp->prev; + xsltReleaseRVT(ctxt, tmp); + } + } +} + +/** + * 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, oldLocalFragmentBase; + +#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; + + 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; + xmlAddChild(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; + /* + * We need the fragment base for extension instructions + * which return values (like EXSLT's function). + */ + oldLocalFragmentBase = ctxt->localRVTBase; + ctxt->localRVTBase = NULL; + + func(ctxt, contextNode, cur, cur->psvi); + + ctxt->localRVTBase = oldLocalFragmentBase; + /* + * 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; + /* + * We need the fragment base for extension instructions + * which return values (like EXSLT's function). + */ + oldLocalFragmentBase = ctxt->localRVTBase; + ctxt->localRVTBase = NULL; + + function(ctxt, contextNode, cur, cur->psvi); + /* + * Cleanup temporary tree fragments. + */ + if (oldLocalFragmentTop != ctxt->localRVT) + xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); + + ctxt->localRVTBase = oldLocalFragmentBase; + 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; + +#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, oldLocalFragmentTop; + +#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; + + /* + * Check for infinite recursion: stop if the maximum of nested templates + * is excceeded. Adjust xsltMaxDepth if you need more. + */ + if (((ctxt->templNr >= xsltMaxDepth) || + (ctxt->varsNr >= 5 * xsltMaxDepth))) + { + xsltTransformError(ctxt, NULL, list, + "xsltApplyXSLTTemplate: 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", + xsltMaxDepth); + xsltDebug(ctxt, contextNode, list, NULL); + return; + } + + oldUserFragmentTop = ctxt->tmpRVT; + ctxt->tmpRVT = NULL; + oldLocalFragmentTop = ctxt->localRVT; + + /* + * 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); + } + /* + * 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; + + /* + * Clean up remaining local tree fragments. + * This also frees fragments which are the result of + * extension instructions. Should normally not be hit; but + * just for the case xsltExtensionInstructionResultFinalize() + * was not called by the extension author. + */ + if (oldLocalFragmentTop != ctxt->localRVT) { + xmlDocPtr cur = ctxt->localRVT, tmp; + + do { + tmp = cur; + cur = (xmlDocPtr) cur->next; + xsltReleaseRVT(ctxt, tmp); + } while (cur != oldLocalFragmentTop); + } + ctxt->localRVT = oldLocalFragmentTop; + + /* + * 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 cur = ctxt->tmpRVT, tmp; + + while (cur != NULL) { + tmp = cur; + cur = (xmlDocPtr) cur->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. 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) { + 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 + * @comp: 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; + + 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) + + 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", + style->method); + 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", + style->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 (style->encoding != NULL) + res->encoding = xmlStrdup(style->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); + } + } + + /* + * Save the result + */ + ret = xsltSaveResultToFilename((const char *) filename, + res, style, 0); + if (ret < 0) { + xsltTransformError(ctxt, NULL, inst, + "xsltDocumentElem: unable to save to %s\n", + filename); + ctxt->state = XSLT_STATE_ERROR; +#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 + * @comp: 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); + xmlAddChild(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); + xmlAddChild(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; + } + xmlAddChild(ctxt->insert, copy); + text = text->next; + } + } +} + +/** + * xsltElement: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt element node + * @comp: 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); + if ((prefix != NULL) && + (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3))) + { + /* + * TODO: Should we really disallow an "xml" prefix? + */ + goto error; + } + } 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; + } + xmlAddChild(ctxt->insert, copy); + + /* + * 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); + }; + } 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) { + 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); + xmlAddChild(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 + * @comp: 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); + xmlAddChild(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 + * @comp: 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; + xmlDocPtr oldXPContextDoc; + xmlNsPtr *oldXPNamespaces; + xmlNodePtr oldXPContextNode; + int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; + xmlXPathContextPtr xpctxt; + + 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. + */ + xpctxt = ctxt->xpathCtxt; + oldXPContextDoc = xpctxt->doc; + oldXPContextNode = xpctxt->node; + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPContextSize = xpctxt->contextSize; + oldXPNsNr = xpctxt->nsNr; + oldXPNamespaces = xpctxt->namespaces; + + xpctxt->node = node; + if (comp != NULL) { + +#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 + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } + + res = xmlXPathCompiledEval(comp->comp, xpctxt); + + xpctxt->doc = oldXPContextDoc; + xpctxt->node = oldXPContextNode; + xpctxt->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->nsNr = oldXPNsNr; + xpctxt->namespaces = oldXPNamespaces; + + 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 + * 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); + } + +#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 + * @comp: 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; + xmlNodePtr copy = NULL; + xmlChar *value = NULL; + xmlDocPtr oldXPContextDoc; + xmlNsPtr *oldXPNamespaces; + xmlNodePtr oldXPContextNode; + int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; + xmlXPathContextPtr xpctxt; + + 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 + + xpctxt = ctxt->xpathCtxt; + oldXPContextDoc = xpctxt->doc; + oldXPContextNode = xpctxt->node; + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPContextSize = xpctxt->contextSize; + oldXPNsNr = xpctxt->nsNr; + oldXPNamespaces = xpctxt->namespaces; + + xpctxt->node = node; + if (comp != NULL) { + +#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 + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } + + res = xmlXPathCompiledEval(comp->comp, xpctxt); + + xpctxt->doc = oldXPContextDoc; + xpctxt->node = oldXPContextNode; + xpctxt->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->nsNr = oldXPNsNr; + xpctxt->namespaces = oldXPNamespaces; + + /* + * 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) { + copy = 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 + * @comp: 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 + 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; + + xsltNumberFormat(ctxt, &comp->numdata, node); +} + +/** + * 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; + } +} + +/** + * xsltCallTemplate: + * @ctxt: a XSLT transformation context + * @node: the "current node" in the source tree + * @inst: the XSLT 'call-template' instruction + * @comp: 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 + * @comp: 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 oldDoc; + 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; + oldDoc = ctxt->tmpDoc; + 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; + + /* + * 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 + + /* + * Set up XPath. + */ + xpctxt->node = node; /* Set the "context 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->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; + 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; + cur = node->children; + 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; + 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 = oldDoc; + xpctxt->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; + + ctxt->tmpDoc = oldDoc; + 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; + xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; + xmlDocPtr oldXPContextDoc = xpctxt->doc; + int oldXPProximityPosition = xpctxt->proximityPosition; + int oldXPContextSize = xpctxt->contextSize; + +#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 + + xpctxt->node = contextNode; + xpctxt->doc = oldXPContextDoc; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->contextSize = oldXPContextSize; + +#ifdef XSLT_REFACTORED + if (wcomp->inScopeNs != NULL) { + xpctxt->namespaces = wcomp->inScopeNs->list; + xpctxt->nsNr = wcomp->inScopeNs->xpathNumber; + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } +#else + xpctxt->namespaces = wcomp->nsList; + xpctxt->nsNr = wcomp->nsNr; +#endif + + +#ifdef XSLT_FAST_IF + res = xmlXPathCompiledEvalToBoolean(wcomp->comp, xpctxt); + + if (res == -1) { + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + testRes = (res == 1) ? 1 : 0; + +#else /* XSLT_FAST_IF */ + + res = xmlXPathCompiledEval(wcomp->comp, xpctxt); + + 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; + } + xpctxt->node = contextNode; + xpctxt->doc = oldXPContextDoc; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->contextSize = oldXPContextSize; + goto exit; + +test_is_true: + + xpctxt->node = contextNode; + xpctxt->doc = oldXPContextDoc; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->contextSize = oldXPContextSize; + 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 + * @comp: 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 + { + xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; + xmlDocPtr oldXPContextDoc = xpctxt->doc; + xmlNsPtr *oldXPNamespaces = xpctxt->namespaces; + xmlNodePtr oldXPContextNode = xpctxt->node; + int oldXPProximityPosition = xpctxt->proximityPosition; + int oldXPContextSize = xpctxt->contextSize; + int oldXPNsNr = xpctxt->nsNr; + xmlDocPtr oldLocalFragmentTop = ctxt->localRVT; + + xpctxt->node = contextNode; + if (comp != NULL) { + +#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 + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } + /* + * This XPath function is optimized for boolean results. + */ + res = xmlXPathCompiledEvalToBoolean(comp->comp, xpctxt); + + /* + * Cleanup fragments created during evaluation of the + * "select" expression. + */ + if (oldLocalFragmentTop != ctxt->localRVT) + xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); + + xpctxt->doc = oldXPContextDoc; + xpctxt->node = oldXPContextNode; + xpctxt->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->nsNr = oldXPNsNr; + xpctxt->namespaces = oldXPNamespaces; + } + +#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 */ + { + xmlXPathObjectPtr xpobj = NULL; + /* + * OLD CODE: + */ + { + xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; + xmlDocPtr oldXPContextDoc = xpctxt->doc; + xmlNsPtr *oldXPNamespaces = xpctxt->namespaces; + xmlNodePtr oldXPContextNode = xpctxt->node; + int oldXPProximityPosition = xpctxt->proximityPosition; + int oldXPContextSize = xpctxt->contextSize; + int oldXPNsNr = xpctxt->nsNr; + + xpctxt->node = contextNode; + if (comp != NULL) { + +#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 + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } + + /* + * This XPath function is optimized for boolean results. + */ + xpobj = xmlXPathCompiledEval(comp->comp, xpctxt); + + xpctxt->doc = oldXPContextDoc; + xpctxt->node = oldXPContextNode; + xpctxt->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->nsNr = oldXPNsNr; + xpctxt->namespaces = oldXPNamespaces; + } + 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 + * @node: the "current node" in the source tree + * @inst: the element node of the xsl:for-each instruction + * @comp: 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 oldSourceDoc; + 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; + oldSourceDoc = ctxt->tmpDoc; + oldContextNode = ctxt->node; + /* + * The "current template rule" is cleared for the instantiation of + * xsl:for-each. + */ + oldCurTemplRule = ctxt->currentTemplateRule; + ctxt->currentTemplateRule = NULL; + + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPContextSize = xpctxt->contextSize; + /* + * Set up XPath. + */ + xpctxt->node = contextNode; +#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 + + /* + * Evaluate the 'select' expression. + */ + res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt); + + 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 + + /* + * Restore XPath states for the "current node". + */ + xpctxt->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->node = contextNode; + + /* + * 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->tmpDoc = oldSourceDoc; + ctxt->document = oldDocInfo; + ctxt->nodeList = oldList; + ctxt->node = oldContextNode; + ctxt->currentTemplateRule = oldCurTemplRule; + + xpctxt->doc = oldSourceDoc; + 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; +} + +#ifdef XSLT_REFACTORED_KEYCOMP +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); +} +#endif /* XSLT_REFACTORED_KEYCOMP */ + +/** + * 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; + xsltStackElemPtr variables; + xsltStackElemPtr vptr; + + 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) + + 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", + style->method); + 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", + style->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 (style->encoding != NULL) + res->encoding = xmlStrdup(style->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->tmpDoc = doc; + ctxt->node = (xmlNodePtr) doc; + if (ctxt->globalVars == NULL) + ctxt->globalVars = xmlHashCreate(20); + if (params != NULL) { + xsltEvalUserParams(ctxt, params); + } + xsltEvalGlobalVariables(ctxt); + +#ifdef XSLT_REFACTORED_KEYCOMP + xsltCountKeys(ctxt); +#endif + + ctxt->tmpDoc = doc; + 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; + } + /* + * Free all remaining tree fragments. + */ + xsltFreeRVTs(ctxt); + /* + * 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_ERROR)) { + 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); + +} diff --git a/libxslt/variables.c b/libxslt/variables.c index 124f02a9..5adb082c 100644 --- a/libxslt/variables.c +++ b/libxslt/variables.c @@ -1,1740 +1,2306 @@ -/* - * variables.c: Implementation of the variable storage and lookup - * - * Reference: - * http://www.w3.org/TR/1999/REC-xslt-19991116 - * - * See Copyright for the status of this software. - * - * daniel@veillard.com - */ - -#define IN_LIBXSLT -#include "libxslt.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "xslt.h" -#include "xsltInternals.h" -#include "xsltutils.h" -#include "variables.h" -#include "transform.h" -#include "imports.h" -#include "preproc.h" -#include "keys.h" - -#ifdef WITH_XSLT_DEBUG -#define WITH_XSLT_DEBUG_VARIABLE -#endif - -#ifdef XSLT_REFACTORED -const xmlChar *xsltDocFragFake = (const xmlChar *) " fake node libxslt"; -#endif - -/************************************************************************ - * * - * Result Value Tree (Result Tree Fragment) interfaces * - * * - ************************************************************************/ -/** - * xsltCreateRVT: - * @ctxt: an XSLT transformation context - * - * Create a Result Value Tree - * (the XSLT 1.0 term for this is "Result Tree Fragment") - * - * Returns the result value tree or NULL in case of error - */ -xmlDocPtr -xsltCreateRVT(xsltTransformContextPtr ctxt) -{ - xmlDocPtr container; - - /* - * Question: Why is this function public? - * Answer: It is called by the EXSLT module. - */ - if (ctxt == NULL) return(NULL); - - container = xmlNewDoc(NULL); - if (container == NULL) - return(NULL); - container->dict = ctxt->dict; - xmlDictReference(container->dict); -#ifdef WITH_XSLT_DEBUG - xsltGenericDebug(xsltGenericDebugContext, - "reusing transformation dict for RVT\n"); -#endif - - XSLT_MARK_RES_TREE_FRAG(container); - container->doc = container; - container->parent = NULL; - return(container); -} - -/** - * xsltRegisterTmpRVT: - * @ctxt: an XSLT transformation context - * @RVT: a result value tree (Result Tree Fragment) - * - * Register the result value tree (XSLT 1.0 term: Result Tree Fragment) - * for destruction at the end of the context - * - * Returns 0 in case of success and -1 in case of error. - */ -int -xsltRegisterTmpRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) -{ - if ((ctxt == NULL) || (RVT == NULL)) return(-1); - - RVT->next = (xmlNodePtr) ctxt->tmpRVT; - if (ctxt->tmpRVT != NULL) - ctxt->tmpRVT->prev = (xmlNodePtr) RVT; - ctxt->tmpRVT = RVT; - return(0); -} - -/** - * xsltRegisterPersistRVT: - * @ctxt: an XSLT transformation context - * @RVT: a result value tree (Result Tree Fragment) - * - * Register the result value tree (XSLT 1.0 term: Result Tree Fragment) - * for destruction at the end of the processing - * - * Returns 0 in case of success and -1 in case of error. - */ -int -xsltRegisterPersistRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) -{ - if ((ctxt == NULL) || (RVT == NULL)) return(-1); - - RVT->next = (xmlNodePtr) ctxt->persistRVT; - if (ctxt->persistRVT != NULL) - ctxt->persistRVT->prev = (xmlNodePtr) RVT; - ctxt->persistRVT = RVT; - return(0); -} - -/** - * xsltFreeRVTs: - * @ctxt: an XSLT transformation context - * - * Free all the registered result value tree (Result Tree Fragment) - * of the transformation - */ -void -xsltFreeRVTs(xsltTransformContextPtr ctxt) -{ - xmlDocPtr cur, next; - - if (ctxt == NULL) return; - - cur = ctxt->tmpRVT; - while (cur != NULL) { - next = (xmlDocPtr) cur->next; - if (cur->_private != NULL) { - xsltFreeDocumentKeys(cur->_private); - xmlFree(cur->_private); - } - xmlFreeDoc(cur); - cur = next; - } - cur = ctxt->persistRVT; - while (cur != NULL) { - next = (xmlDocPtr) cur->next; - if (cur->_private != NULL) { - xsltFreeDocumentKeys(cur->_private); - xmlFree(cur->_private); - } - xmlFreeDoc(cur); - cur = next; - } -} - -/************************************************************************ - * * - * Module interfaces * - * * - ************************************************************************/ - -/** - * xsltNewStackElem: - * - * Create a new XSLT ParserContext - * - * Returns the newly allocated xsltParserStackElem or NULL in case of error - */ -static xsltStackElemPtr -xsltNewStackElem(void) { - xsltStackElemPtr cur; - - cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem)); - if (cur == NULL) { - xsltTransformError(NULL, NULL, NULL, - "xsltNewStackElem : malloc failed\n"); - return(NULL); - } - cur->computed = 0; - cur->name = NULL; - cur->nameURI = NULL; - cur->select = NULL; - cur->tree = NULL; - cur->value = NULL; - cur->comp = NULL; - return(cur); -} - -/** - * xsltCopyStackElem: - * @elem: an XSLT stack element - * - * Makes a copy of the stack element - * - * Returns the copy of NULL - */ -static xsltStackElemPtr -xsltCopyStackElem(xsltStackElemPtr elem) { - xsltStackElemPtr cur; - - cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem)); - if (cur == NULL) { - xsltTransformError(NULL, NULL, NULL, - "xsltCopyStackElem : malloc failed\n"); - return(NULL); - } - cur->name = elem->name; - cur->nameURI = elem->nameURI; - cur->select = elem->select; - cur->tree = elem->tree; - cur->comp = elem->comp; - cur->computed = 0; - cur->value = NULL; - return(cur); -} - -/** - * xsltFreeStackElem: - * @elem: an XSLT stack element - * - * Free up the memory allocated by @elem - */ -static void -xsltFreeStackElem(xsltStackElemPtr elem) { - if (elem == NULL) - return; - if (elem->value != NULL) - xmlXPathFreeObject(elem->value); - - xmlFree(elem); -} - -/** - * xsltFreeStackElemList: - * @elem: an XSLT stack element - * - * Free up the memory allocated by @elem - */ -void -xsltFreeStackElemList(xsltStackElemPtr elem) { - xsltStackElemPtr next; - - while(elem != NULL) { - next = elem->next; - xsltFreeStackElem(elem); - elem = next; - } -} - -/** - * xsltStackLookup: - * @ctxt: an XSLT transformation context - * @name: the local part of the name - * @nameURI: the URI part of the name - * - * Locate an element in the stack based on its name. - */ -static int stack_addr = 0; -static int stack_cmp = 0; -static xsltStackElemPtr -xsltStackLookup(xsltTransformContextPtr ctxt, const xmlChar *name, - const xmlChar *nameURI) { - int i; - xsltStackElemPtr cur; - - if ((ctxt == NULL) || (name == NULL) || (ctxt->varsNr == 0)) - return(NULL); - - /* - * Do the lookup from the top of the stack, but - * don't use params being computed in a call-param - * First lookup expects the variable name and URI to - * come from the disctionnary and hence get equality - */ - for (i = ctxt->varsNr; i > ctxt->varsBase; i--) { - cur = ctxt->varsTab[i-1]; - while (cur != NULL) { - if (cur->name == name) { - if (nameURI == NULL) { - if (cur->nameURI == NULL) { - stack_addr++; - return(cur); - } - } else { - if ((cur->nameURI != NULL) && - (cur->nameURI == nameURI)) { - stack_addr++; - return(cur); - } - } - - } - cur = cur->next; - } - } - - /* - * Redo the lookup with interned string compares - * to avoid string compares. - */ - name = xmlDictLookup(ctxt->dict, name, -1); - if (nameURI != NULL) - nameURI = xmlDictLookup(ctxt->dict, nameURI, -1); - else - nameURI = NULL; - for (i = ctxt->varsNr; i > ctxt->varsBase; i--) { - cur = ctxt->varsTab[i-1]; - while (cur != NULL) { - if (cur->name == name) { - if (nameURI == NULL) { - if (cur->nameURI == NULL) { - stack_cmp++; - return(cur); - } - } else { - if ((cur->nameURI != NULL) && - (cur->nameURI == nameURI)) { - stack_cmp++; - return(cur); - } - } - - } - cur = cur->next; - } - } - - return(NULL); -} - -/** - * xsltCheckStackElem: - * @ctxt: xn XSLT transformation context - * @name: the variable name - * @nameURI: the variable namespace URI - * - * check wether the variable or param is already defined - * - * Returns 1 if variable is present, 2 if param is present, 3 if this - * is an inherited param, 0 if not found, -1 in case of failure. - */ -static int -xsltCheckStackElem(xsltTransformContextPtr ctxt, const xmlChar *name, - const xmlChar *nameURI) { - xsltStackElemPtr cur; - - if ((ctxt == NULL) || (name == NULL)) - return(-1); - - cur = xsltStackLookup(ctxt, name, nameURI); - if (cur == NULL) - return(0); - if (cur->comp != NULL) { - if (cur->comp->type == XSLT_FUNC_WITHPARAM) - return(3); - else if (cur->comp->type == XSLT_FUNC_PARAM) - return(2); - } - - return(1); -} - -/** - * xsltAddStackElem: - * @ctxt: xn XSLT transformation context - * @elem: a stack element - * - * add a new element at this level of the stack. - * - * Returns 0 in case of success, -1 in case of failure. - */ -static int -xsltAddStackElem(xsltTransformContextPtr ctxt, xsltStackElemPtr elem) { - if ((ctxt == NULL) || (elem == NULL)) - return(-1); - - elem->next = ctxt->varsTab[ctxt->varsNr - 1]; - ctxt->varsTab[ctxt->varsNr - 1] = elem; - ctxt->vars = elem; - return(0); -} - -/** - * xsltAddStackElemList: - * @ctxt: xn XSLT transformation context - * @elems: a stack element list - * - * add the new element list at this level of the stack. - * - * Returns 0 in case of success, -1 in case of failure. - */ -int -xsltAddStackElemList(xsltTransformContextPtr ctxt, xsltStackElemPtr elems) { - xsltStackElemPtr cur; - - if ((ctxt == NULL) || (elems == NULL)) - return(-1); - - /* TODO: check doublons */ - if (ctxt->varsTab[ctxt->varsNr - 1] != NULL) { - cur = ctxt->varsTab[ctxt->varsNr - 1]; - while (cur->next != NULL) - cur = cur->next; - cur->next = elems; - } else { - elems->next = ctxt->varsTab[ctxt->varsNr - 1]; - ctxt->varsTab[ctxt->varsNr - 1] = elems; - ctxt->vars = elems; - } - return(0); -} - -/************************************************************************ - * * - * Module interfaces * - * * - ************************************************************************/ - -/** - * xsltEvalVariable: - * @ctxt: the XSLT transformation context - * @elem: the variable or parameter. - * @precomp: pointer to precompiled data - * - * Evaluate a variable value. - * - * Returns the XPath Object value or NULL in case of error - */ -static xmlXPathObjectPtr -xsltEvalVariable(xsltTransformContextPtr ctxt, xsltStackElemPtr elem, - xsltStylePreCompPtr castedComp) -{ -#ifdef XSLT_REFACTORED - xsltStyleItemVariablePtr precomp = - (xsltStyleItemVariablePtr) castedComp; -#else - xsltStylePreCompPtr precomp = castedComp; -#endif - xmlXPathObjectPtr result = NULL; - int oldProximityPosition, oldContextSize; - xmlNodePtr oldInst, oldNode; - xsltDocumentPtr oldDoc; - int oldNsNr; - xmlNsPtr *oldNamespaces; - - if ((ctxt == NULL) || (elem == NULL)) - return(NULL); - -#ifdef WITH_XSLT_DEBUG_VARIABLE - XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - "Evaluating variable %s\n", elem->name)); -#endif - if (elem->select != NULL) { - xmlXPathCompExprPtr comp = NULL; - - if ((precomp != NULL) && (precomp->comp != NULL)) { - comp = precomp->comp; - } else { - comp = xmlXPathCompile(elem->select); - } - if (comp == NULL) - return(NULL); - oldProximityPosition = ctxt->xpathCtxt->proximityPosition; - oldContextSize = ctxt->xpathCtxt->contextSize; - ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->node; - oldDoc = ctxt->document; - oldNode = ctxt->node; - oldInst = ctxt->inst; - oldNsNr = ctxt->xpathCtxt->nsNr; - oldNamespaces = ctxt->xpathCtxt->namespaces; - if (precomp != NULL) { - ctxt->inst = precomp->inst; -#ifdef XSLT_REFACTORED - if (precomp->inScopeNs != NULL) { - ctxt->xpathCtxt->namespaces = precomp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = precomp->inScopeNs->xpathNumber; - } else { - ctxt->xpathCtxt->namespaces = NULL; - ctxt->xpathCtxt->nsNr = 0; - } -#else - ctxt->xpathCtxt->namespaces = precomp->nsList; - ctxt->xpathCtxt->nsNr = precomp->nsNr; -#endif - } else { - ctxt->inst = NULL; - ctxt->xpathCtxt->namespaces = NULL; - ctxt->xpathCtxt->nsNr = 0; - } - result = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); - ctxt->xpathCtxt->contextSize = oldContextSize; - ctxt->xpathCtxt->proximityPosition = oldProximityPosition; - ctxt->xpathCtxt->nsNr = oldNsNr; - ctxt->xpathCtxt->namespaces = oldNamespaces; - ctxt->inst = oldInst; - ctxt->node = oldNode; - ctxt->document = oldDoc; - if ((precomp == NULL) || (precomp->comp == NULL)) - xmlXPathFreeCompExpr(comp); - if (result == NULL) { - if (precomp == NULL) - xsltTransformError(ctxt, NULL, NULL, - "Evaluating variable %s failed\n", elem->name); - else - xsltTransformError(ctxt, NULL, precomp->inst, - "Evaluating variable %s failed\n", elem->name); - ctxt->state = XSLT_STATE_STOPPED; -#ifdef WITH_XSLT_DEBUG_VARIABLE -#ifdef LIBXML_DEBUG_ENABLED - } else { - if ((xsltGenericDebugContext == stdout) || - (xsltGenericDebugContext == stderr)) - xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, - result, 0); -#endif -#endif - } - } else { - if (elem->tree == NULL) { - result = xmlXPathNewCString(""); - } else { - /* - * This is a result tree fragment. - */ - xmlDocPtr container; - xmlNodePtr oldInsert; - xmlDocPtr oldoutput; - - container = xsltCreateRVT(ctxt); - if (container == NULL) - return(NULL); - /* - * Tag the subtree for removal once consumed - */ - xsltRegisterTmpRVT(ctxt, container); - oldoutput = ctxt->output; - ctxt->output = container; - oldInsert = ctxt->insert; - ctxt->insert = (xmlNodePtr) container; - xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, NULL, NULL); - ctxt->insert = oldInsert; - ctxt->output = oldoutput; - - result = xmlXPathNewValueTree((xmlNodePtr) container); - if (result == NULL) { - result = xmlXPathNewCString(""); - } else { - result->boolval = 0; /* Freeing is not handled there anymore */ - } -#ifdef WITH_XSLT_DEBUG_VARIABLE -#ifdef LIBXML_DEBUG_ENABLED - if ((xsltGenericDebugContext == stdout) || - (xsltGenericDebugContext == stderr)) - xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, - result, 0); -#endif -#endif - } - } - return(result); -} - -/** - * xsltEvalGlobalVariable: - * @elem: the variable or parameter. - * @ctxt: the XSLT transformation context - * - * Evaluate a global variable value. - * - * Returns the XPath Object value or NULL in case of error - */ -static xmlXPathObjectPtr -xsltEvalGlobalVariable(xsltStackElemPtr elem, xsltTransformContextPtr ctxt) -{ - xmlXPathObjectPtr result = NULL; -#ifdef XSLT_REFACTORED - xsltStyleBasicItemVariablePtr precomp; -#else - xsltStylePreCompPtr precomp; -#endif - int oldProximityPosition, oldContextSize; - xmlDocPtr oldDoc; - xmlNodePtr oldInst; - int oldNsNr; - xmlNsPtr *oldNamespaces; - const xmlChar *name; - - if ((ctxt == NULL) || (elem == NULL)) - return(NULL); - if (elem->computed) - return(elem->value); - - -#ifdef WITH_XSLT_DEBUG_VARIABLE - XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - "Evaluating global variable %s\n", elem->name)); -#endif - -#ifdef WITH_DEBUGGER - if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && - elem->comp && elem->comp->inst) - xslHandleDebugger(elem->comp->inst, NULL, NULL, ctxt); -#endif - - name = elem->name; - elem->name = BAD_CAST " being computed ... "; - -#ifdef XSLT_REFACTORED - precomp = (xsltStyleBasicItemVariablePtr) elem->comp; -#else - precomp = elem->comp; -#endif - - /* - * OPTIMIZE TODO: We should consider if instantiating global vars/params - * on a on-demand basis would be better. The vars/params don't - * need to be evaluated if never called; and in the case of - * global params, if values for such params are provided by the - * user. - */ - if (elem->select != NULL) { - xmlXPathCompExprPtr comp = NULL; - - if ((precomp != NULL) && (precomp->comp != NULL)) { - comp = precomp->comp; - } else { - comp = xmlXPathCompile(elem->select); - } - if (comp == NULL) { - elem->name = name; - return(NULL); - } - oldDoc = ctxt->xpathCtxt->doc; - oldProximityPosition = ctxt->xpathCtxt->proximityPosition; - oldContextSize = ctxt->xpathCtxt->contextSize; - oldInst = ctxt->inst; - oldNsNr = ctxt->xpathCtxt->nsNr; - oldNamespaces = ctxt->xpathCtxt->namespaces; - - if (precomp != NULL) { - ctxt->inst = precomp->inst; -#ifdef XSLT_REFACTORED - if (precomp->inScopeNs != NULL) { - ctxt->xpathCtxt->namespaces = precomp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = precomp->inScopeNs->xpathNumber; - } else { - ctxt->xpathCtxt->namespaces = NULL; - ctxt->xpathCtxt->nsNr = 0; - } -#else - ctxt->xpathCtxt->namespaces = precomp->nsList; - ctxt->xpathCtxt->nsNr = precomp->nsNr; -#endif - } else { - ctxt->inst = NULL; - ctxt->xpathCtxt->namespaces = NULL; - ctxt->xpathCtxt->nsNr = 0; - } - ctxt->xpathCtxt->doc = ctxt->tmpDoc; - ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->tmpDoc; - result = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); - - ctxt->xpathCtxt->doc = oldDoc; - ctxt->xpathCtxt->contextSize = oldContextSize; - ctxt->xpathCtxt->proximityPosition = oldProximityPosition; - ctxt->inst = oldInst; - ctxt->xpathCtxt->nsNr = oldNsNr; - ctxt->xpathCtxt->namespaces = oldNamespaces; - if ((precomp == NULL) || (precomp->comp == NULL)) - xmlXPathFreeCompExpr(comp); - if (result == NULL) { - if (precomp == NULL) - xsltTransformError(ctxt, NULL, NULL, - "Evaluating global variable %s failed\n", elem->name); - else - xsltTransformError(ctxt, NULL, precomp->inst, - "Evaluating global variable %s failed\n", elem->name); - ctxt->state = XSLT_STATE_STOPPED; -#ifdef WITH_XSLT_DEBUG_VARIABLE -#ifdef LIBXML_DEBUG_ENABLED - } else { - if ((xsltGenericDebugContext == stdout) || - (xsltGenericDebugContext == stderr)) - xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, - result, 0); -#endif -#endif - } - } else { - if (elem->tree == NULL) { - result = xmlXPathNewCString(""); - } else { - /* - * This is a result tree fragment. - */ - xmlDocPtr container; - xmlNodePtr oldInsert; - xmlDocPtr oldoutput; - - container = xsltCreateRVT(ctxt); - if (container == NULL) - return(NULL); - /* - * Tag the subtree for removal once consumed - */ - xsltRegisterTmpRVT(ctxt, container); - /* - * Save a pointer to the global variable for later cleanup - */ - container->psvi = elem; - oldoutput = ctxt->output; - ctxt->output = container; - oldInsert = ctxt->insert; - ctxt->insert = (xmlNodePtr) container; - xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, NULL, NULL); - ctxt->insert = oldInsert; - ctxt->output = oldoutput; - - result = xmlXPathNewValueTree((xmlNodePtr) container); - if (result == NULL) { - result = xmlXPathNewCString(""); - } else { - result->boolval = 0; /* Freeing is not handled there anymore */ - } -#ifdef WITH_XSLT_DEBUG_VARIABLE -#ifdef LIBXML_DEBUG_ENABLED - if ((xsltGenericDebugContext == stdout) || - (xsltGenericDebugContext == stderr)) - xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, - result, 0); -#endif -#endif - } - } - if (result != NULL) { - elem->value = result; - elem->computed = 1; - } - elem->name = name; - return(result); -} - -/** - * xsltEvalGlobalVariables: - * @ctxt: the XSLT transformation context - * - * Evaluate the global variables of a stylesheet. This need to be - * done on parsed stylesheets before starting to apply transformations - * - * Returns 0 in case of success, -1 in case of error - */ -int -xsltEvalGlobalVariables(xsltTransformContextPtr ctxt) { - xsltStackElemPtr elem; - xsltStylesheetPtr style; - - if ((ctxt == NULL) || (ctxt->document == NULL)) - return(-1); - -#ifdef WITH_XSLT_DEBUG_VARIABLE - XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - "Registering global variables\n")); -#endif - - ctxt->tmpDoc = ctxt->document->doc; - ctxt->node = (xmlNodePtr) ctxt->document->doc; - ctxt->xpathCtxt->contextSize = 1; - ctxt->xpathCtxt->proximityPosition = 1; - - /* - * Walk the list from the stylesheets and populate the hash table - */ - style = ctxt->style; - while (style != NULL) { - elem = style->variables; - -#ifdef WITH_XSLT_DEBUG_VARIABLE - if ((style->doc != NULL) && (style->doc->URL != NULL)) { - XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - "Registering global variables from %s\n", - style->doc->URL)); - } -#endif - - while (elem != NULL) { - xsltStackElemPtr def; - - /* - * Global variables are stored in the variables pool. - */ - def = (xsltStackElemPtr) - xmlHashLookup2(ctxt->globalVars, - elem->name, elem->nameURI); - if (def == NULL) { - - def = xsltCopyStackElem(elem); - xmlHashAddEntry2(ctxt->globalVars, - elem->name, elem->nameURI, def); - } else if ((elem->comp != NULL) && - (elem->comp->type == XSLT_FUNC_VARIABLE)) { - /* - * Redefinition of variables from a different stylesheet - * should not generate a message. - */ - if ((elem->comp->inst != NULL) && - (def->comp != NULL) && (def->comp->inst != NULL) && - (elem->comp->inst->doc == def->comp->inst->doc)) { - xsltTransformError(ctxt, style, elem->comp->inst, - "Global variable %s already defined\n", elem->name); - if (style != NULL) style->errors++; - } - } - elem = elem->next; - } - - style = xsltNextImport(style); - } - - /* - * This part does the actual evaluation - */ - ctxt->node = (xmlNodePtr) ctxt->document->doc; - ctxt->xpathCtxt->contextSize = 1; - ctxt->xpathCtxt->proximityPosition = 1; - xmlHashScan(ctxt->globalVars, - (xmlHashScanner) xsltEvalGlobalVariable, ctxt); - - return(0); -} - -/** - * xsltRegisterGlobalVariable: - * @style: the XSLT transformation context - * @name: the variable name - * @ns_uri: the variable namespace URI - * @sel: the expression which need to be evaluated to generate a value - * @tree: the subtree if sel is NULL - * @comp: the precompiled value - * @value: the string value if available - * - * Register a new variable value. If @value is NULL it unregisters - * the variable - * - * Returns 0 in case of success, -1 in case of error - */ -static int -xsltRegisterGlobalVariable(xsltStylesheetPtr style, const xmlChar *name, - const xmlChar *ns_uri, const xmlChar *sel, - xmlNodePtr tree, xsltStylePreCompPtr comp, - const xmlChar *value) { - xsltStackElemPtr elem, tmp; - if (style == NULL) - return(-1); - if (name == NULL) - return(-1); - if (comp == NULL) - return(-1); - -#ifdef WITH_XSLT_DEBUG_VARIABLE - if (comp->type == XSLT_FUNC_PARAM) - xsltGenericDebug(xsltGenericDebugContext, - "Defining global param %s\n", name); - else - xsltGenericDebug(xsltGenericDebugContext, - "Defining global variable %s\n", name); -#endif - - elem = xsltNewStackElem(); - if (elem == NULL) - return(-1); - elem->comp = comp; - elem->name = xmlDictLookup(style->dict, name, -1); - elem->select = xmlDictLookup(style->dict, sel, -1); - if (ns_uri) - elem->nameURI = xmlDictLookup(style->dict, ns_uri, -1); - elem->tree = tree; - tmp = style->variables; - if (tmp == NULL) { - elem->next = NULL; - style->variables = elem; - } else { - while (tmp != NULL) { - if ((elem->comp->type == XSLT_FUNC_VARIABLE) && - (tmp->comp->type == XSLT_FUNC_VARIABLE) && - (xmlStrEqual(elem->name, tmp->name)) && - ((elem->nameURI == tmp->nameURI) || - (xmlStrEqual(elem->nameURI, tmp->nameURI)))) { - xsltTransformError(NULL, style, comp->inst, - "redefinition of global variable %s\n", elem->name); - if (style != NULL) style->errors++; - } - if (tmp->next == NULL) - break; - tmp = tmp->next; - } - elem->next = NULL; - tmp->next = elem; - } - if (value != NULL) { - elem->computed = 1; - elem->value = xmlXPathNewString(value); - } - return(0); -} - -/** - * xsltProcessUserParamInternal - * - * @ctxt: the XSLT transformation context - * @name: a null terminated parameter name - * @value: a null terminated value (may be an XPath expression) - * @eval: 0 to treat the value literally, else evaluate as XPath expression - * - * If @eval is 0 then @value is treated literally and is stored in the global - * parameter/variable table without any change. - * - * Uf @eval is 1 then @value is treated as an XPath expression and is - * evaluated. In this case, if you want to pass a string which will be - * interpreted literally then it must be enclosed in single or double quotes. - * If the string contains single quotes (double quotes) then it cannot be - * enclosed single quotes (double quotes). If the string which you want to - * be treated literally contains both single and double quotes (e.g. Meet - * at Joe's for "Twelfth Night" at 7 o'clock) then there is no suitable - * quoting character. You cannot use ' or " inside the string - * because the replacement of character entities with their equivalents is - * done at a different stage of processing. The solution is to call - * xsltQuoteUserParams or xsltQuoteOneUserParam. - * - * This needs to be done on parsed stylesheets before starting to apply - * transformations. Normally this will be called (directly or indirectly) - * only from xsltEvalUserParams, xsltEvalOneUserParam, xsltQuoteUserParams, - * or xsltQuoteOneUserParam. - * - * Returns 0 in case of success, -1 in case of error - */ - -static -int -xsltProcessUserParamInternal(xsltTransformContextPtr ctxt, - const xmlChar * name, - const xmlChar * value, - int eval) { - - xsltStylesheetPtr style; - const xmlChar *prefix; - const xmlChar *href; - xmlXPathCompExprPtr comp; - xmlXPathObjectPtr result; - int oldProximityPosition; - int oldContextSize; - int oldNsNr; - xmlNsPtr *oldNamespaces; - xsltStackElemPtr elem; - int res; - void *res_ptr; - - if (ctxt == NULL) - return(-1); - if (name == NULL) - return(0); - if (value == NULL) - return(0); - - style = ctxt->style; - -#ifdef WITH_XSLT_DEBUG_VARIABLE - XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - "Evaluating user parameter %s=%s\n", name, value)); -#endif - - /* - * Name lookup - */ - - name = xsltSplitQName(ctxt->dict, name, &prefix); - href = NULL; - if (prefix != NULL) { - xmlNsPtr ns; - - ns = xmlSearchNs(style->doc, xmlDocGetRootElement(style->doc), - prefix); - if (ns == NULL) { - xsltTransformError(ctxt, style, NULL, - "user param : no namespace bound to prefix %s\n", prefix); - href = NULL; - } else { - href = ns->href; - } - } - - if (name == NULL) - return (-1); - - res_ptr = xmlHashLookup2(ctxt->globalVars, name, href); - if (res_ptr != 0) { - xsltTransformError(ctxt, style, NULL, - "Global parameter %s already defined\n", name); - } - if (ctxt->globalVars == NULL) - ctxt->globalVars = xmlHashCreate(20); - - /* - * do not overwrite variables with parameters from the command line - */ - while (style != NULL) { - elem = ctxt->style->variables; - while (elem != NULL) { - if ((elem->comp != NULL) && - (elem->comp->type == XSLT_FUNC_VARIABLE) && - (xmlStrEqual(elem->name, name)) && - (xmlStrEqual(elem->nameURI, href))) { - return(0); - } - elem = elem->next; - } - style = xsltNextImport(style); - } - style = ctxt->style; - elem = NULL; - - /* - * Do the evaluation if @eval is non-zero. - */ - - result = NULL; - if (eval != 0) { - comp = xmlXPathCompile(value); - if (comp != NULL) { - oldProximityPosition = ctxt->xpathCtxt->proximityPosition; - oldContextSize = ctxt->xpathCtxt->contextSize; - ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->node; - - /* - * There is really no in scope namespace for parameters on the - * command line. - */ - - oldNsNr = ctxt->xpathCtxt->nsNr; - oldNamespaces = ctxt->xpathCtxt->namespaces; - ctxt->xpathCtxt->namespaces = NULL; - ctxt->xpathCtxt->nsNr = 0; - result = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); - ctxt->xpathCtxt->contextSize = oldContextSize; - ctxt->xpathCtxt->proximityPosition = oldProximityPosition; - ctxt->xpathCtxt->nsNr = oldNsNr; - ctxt->xpathCtxt->namespaces = oldNamespaces; - xmlXPathFreeCompExpr(comp); - } - if (result == NULL) { - xsltTransformError(ctxt, style, NULL, - "Evaluating user parameter %s failed\n", name); - ctxt->state = XSLT_STATE_STOPPED; - return(-1); - } - } - - /* - * If @eval is 0 then @value is to be taken literally and result is NULL - * - * If @eval is not 0, then @value is an XPath expression and has been - * successfully evaluated and result contains the resulting value and - * is not NULL. - * - * Now create an xsltStackElemPtr for insertion into the context's - * global variable/parameter hash table. - */ - -#ifdef WITH_XSLT_DEBUG_VARIABLE -#ifdef LIBXML_DEBUG_ENABLED - if ((xsltGenericDebugContext == stdout) || - (xsltGenericDebugContext == stderr)) - xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, - result, 0); -#endif -#endif - - elem = xsltNewStackElem(); - if (elem != NULL) { - elem->name = name; - elem->select = xmlDictLookup(ctxt->dict, value, -1); - if (href != NULL) - elem->nameURI = xmlDictLookup(ctxt->dict, href, -1); - elem->tree = NULL; - elem->computed = 1; - if (eval == 0) { - elem->value = xmlXPathNewString(value); - } - else { - elem->value = result; - } - } - - /* - * Global parameters are stored in the XPath context variables pool. - */ - - res = xmlHashAddEntry2(ctxt->globalVars, name, href, elem); - if (res != 0) { - xsltFreeStackElem(elem); - xsltTransformError(ctxt, style, NULL, - "Global parameter %s already defined\n", name); - } - return(0); -} - -/** - * xsltEvalUserParams: - * - * @ctxt: the XSLT transformation context - * @params: a NULL terminated array of parameters name/value tuples - * - * Evaluate the global variables of a stylesheet. This needs to be - * done on parsed stylesheets before starting to apply transformations. - * Each of the parameters is evaluated as an XPath expression and stored - * in the global variables/parameter hash table. If you want your - * parameter used literally, use xsltQuoteUserParams. - * - * Returns 0 in case of success, -1 in case of error - */ - -int -xsltEvalUserParams(xsltTransformContextPtr ctxt, const char **params) { - int indx = 0; - const xmlChar *name; - const xmlChar *value; - - if (params == NULL) - return(0); - while (params[indx] != NULL) { - name = (const xmlChar *) params[indx++]; - value = (const xmlChar *) params[indx++]; - if (xsltEvalOneUserParam(ctxt, name, value) != 0) - return(-1); - } - return 0; -} - -/** - * xsltQuoteUserParams: - * - * @ctxt: the XSLT transformation context - * @params: a NULL terminated arry of parameters names/values tuples - * - * Similar to xsltEvalUserParams, but the values are treated literally and - * are * *not* evaluated as XPath expressions. This should be done on parsed - * stylesheets before starting to apply transformations. - * - * Returns 0 in case of success, -1 in case of error. - */ - -int -xsltQuoteUserParams(xsltTransformContextPtr ctxt, const char **params) { - int indx = 0; - const xmlChar *name; - const xmlChar *value; - - if (params == NULL) - return(0); - while (params[indx] != NULL) { - name = (const xmlChar *) params[indx++]; - value = (const xmlChar *) params[indx++]; - if (xsltQuoteOneUserParam(ctxt, name, value) != 0) - return(-1); - } - return 0; -} - -/** - * xsltEvalOneUserParam: - * @ctxt: the XSLT transformation context - * @name: a null terminated string giving the name of the parameter - * @value: a null terminated string giving the XPath expression to be evaluated - * - * This is normally called from xsltEvalUserParams to process a single - * parameter from a list of parameters. The @value is evaluated as an - * XPath expression and the result is stored in the context's global - * variable/parameter hash table. - * - * To have a parameter treated literally (not as an XPath expression) - * use xsltQuoteUserParams (or xsltQuoteOneUserParam). For more - * details see description of xsltProcessOneUserParamInternal. - * - * Returns 0 in case of success, -1 in case of error. - */ - -int -xsltEvalOneUserParam(xsltTransformContextPtr ctxt, - const xmlChar * name, - const xmlChar * value) { - return xsltProcessUserParamInternal(ctxt, name, value, - 1 /* xpath eval ? */); -} - -/** - * xsltQuoteOneUserParam: - * @ctxt: the XSLT transformation context - * @name: a null terminated string giving the name of the parameter - * @value: a null terminated string giving the parameter value - * - * This is normally called from xsltQuoteUserParams to process a single - * parameter from a list of parameters. The @value is stored in the - * context's global variable/parameter hash table. - * - * Returns 0 in case of success, -1 in case of error. - */ - -int -xsltQuoteOneUserParam(xsltTransformContextPtr ctxt, - const xmlChar * name, - const xmlChar * value) { - return xsltProcessUserParamInternal(ctxt, name, value, - 0 /* xpath eval ? */); -} - -/** - * xsltBuildVariable: - * @ctxt: the XSLT transformation context - * @comp: the precompiled form - * @tree: the tree if select is NULL - * - * Computes a new variable value. - * - * Returns the xsltStackElemPtr or NULL in case of error - */ -static xsltStackElemPtr -xsltBuildVariable(xsltTransformContextPtr ctxt, - xsltStylePreCompPtr castedComp, - xmlNodePtr tree) -{ -#ifdef XSLT_REFACTORED - xsltStyleBasicItemVariablePtr comp = - (xsltStyleBasicItemVariablePtr) castedComp; -#else - xsltStylePreCompPtr comp = castedComp; -#endif - xsltStackElemPtr elem; - -#ifdef WITH_XSLT_DEBUG_VARIABLE - XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - "Building variable %s", comp->name)); - if (comp->select != NULL) - XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - " select %s", comp->select)); - XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, "\n")); -#endif - - elem = xsltNewStackElem(); - if (elem == NULL) - return(NULL); - elem->comp = (xsltStylePreCompPtr) comp; - elem->name = comp->name; - if (comp->select != NULL) - elem->select = comp->select; - else - elem->select = NULL; - if (comp->ns) - elem->nameURI = comp->ns; - elem->tree = tree; - if (elem->computed == 0) { - elem->value = xsltEvalVariable(ctxt, elem, - (xsltStylePreCompPtr) comp); - if (elem->value != NULL) - elem->computed = 1; - } - return(elem); -} - -/** - * xsltRegisterVariable: - * @ctxt: the XSLT transformation context - * @comp: pointer to precompiled data - * @tree: the tree if select is NULL - * @param: this is a parameter actually - * - * Computes and register a new variable value. - * TODO: Is this intended for xsl:param as well? - * - * Returns 0 in case of success, -1 in case of error - */ -static int -xsltRegisterVariable(xsltTransformContextPtr ctxt, - xsltStylePreCompPtr castedComp, - xmlNodePtr tree, int param) -{ -#ifdef XSLT_REFACTORED - xsltStyleBasicItemVariablePtr comp = - (xsltStyleBasicItemVariablePtr) castedComp; -#else - xsltStylePreCompPtr comp = castedComp; -#endif - xsltStackElemPtr elem; - int present; - - present = xsltCheckStackElem(ctxt, comp->name, comp->ns); - if (param == 0) { - if ((present != 0) && (present != 3)) { - xsltTransformError(ctxt, NULL, comp->inst, - "xsl:variable : redefining %s\n", comp->name); - return(0); - } - } else if (present != 0) { - if ((present == 1) || (present == 2)) { - xsltTransformError(ctxt, NULL, comp->inst, - "xsl:param : redefining %s\n", comp->name); - return(0); - } -#ifdef WITH_XSLT_DEBUG_VARIABLE - XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - "param %s defined by caller\n", comp->name)); -#endif - return(0); - } - elem = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree); - xsltAddStackElem(ctxt, elem); - return(0); -} - -/** - * xsltGlobalVariableLookup: - * @ctxt: the XSLT transformation context - * @name: the variable name - * @ns_uri: the variable namespace URI - * - * Search in the Variable array of the context for the given - * variable value. - * - * Returns the value or NULL if not found - */ -static xmlXPathObjectPtr -xsltGlobalVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name, - const xmlChar *ns_uri) { - xsltStackElemPtr elem; - xmlXPathObjectPtr ret = NULL; - - /* - * Lookup the global variables in XPath global variable hash table - */ - if ((ctxt->xpathCtxt == NULL) || (ctxt->globalVars == NULL)) - return(NULL); - elem = (xsltStackElemPtr) - xmlHashLookup2(ctxt->globalVars, name, ns_uri); - if (elem == NULL) { -#ifdef WITH_XSLT_DEBUG_VARIABLE - XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - "global variable not found %s\n", name)); -#endif - return(NULL); - } - if (elem->computed == 0) { - if (xmlStrEqual(elem->name, BAD_CAST " being computed ... ")) { - xsltTransformError(ctxt, NULL, elem->comp->inst, - "Recursive definition of %s\n", name); - return(NULL); - } - ret = xsltEvalGlobalVariable(elem, ctxt); - } else - ret = elem->value; - return(xmlXPathObjectCopy(ret)); -} - -/** - * xsltVariableLookup: - * @ctxt: the XSLT transformation context - * @name: the variable name - * @ns_uri: the variable namespace URI - * - * Search in the Variable array of the context for the given - * variable value. - * - * Returns the value or NULL if not found - */ -xmlXPathObjectPtr -xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name, - const xmlChar *ns_uri) { - xsltStackElemPtr elem; - - if (ctxt == NULL) - return(NULL); - - elem = xsltStackLookup(ctxt, name, ns_uri); - if (elem == NULL) { - return(xsltGlobalVariableLookup(ctxt, name, ns_uri)); - } - if (elem->computed == 0) { -#ifdef WITH_XSLT_DEBUG_VARIABLE - XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - "uncomputed variable %s\n", name)); -#endif - elem->value = xsltEvalVariable(ctxt, elem, NULL); - elem->computed = 1; - } - if (elem->value != NULL) - return(xmlXPathObjectCopy(elem->value)); -#ifdef WITH_XSLT_DEBUG_VARIABLE - XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - "variable not found %s\n", name)); -#endif - return(NULL); -} - -/** - * xsltParseStylesheetCallerParam: - * @ctxt: the XSLT transformation context - * @cur: the "xsl:with-param" element - * - * parse an XSLT transformation param declaration, compute - * its value but doesn't record it. - * NOTE that this is also called with an *xsl:param* element - * from exsltFuncFunctionFunction(). - * - * Returns the new xsltStackElemPtr or NULL - */ - -xsltStackElemPtr -xsltParseStylesheetCallerParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) -{ -#ifdef XSLT_REFACTORED - xsltStyleBasicItemVariablePtr comp; -#else - xsltStylePreCompPtr comp; -#endif - xmlNodePtr tree = NULL; - xsltStackElemPtr elem = NULL; - - if ((cur == NULL) || (ctxt == NULL)) - return(NULL); -#ifdef XSLT_REFACTORED - comp = (xsltStyleBasicItemVariablePtr) cur->psvi; -#else - comp = (xsltStylePreCompPtr) cur->psvi; -#endif - if (comp == NULL) { - xsltTransformError(ctxt, NULL, cur, - "xsl:with-param : compilation error\n"); - return(NULL); - } - - if (comp->name == NULL) { - xsltTransformError(ctxt, NULL, cur, - "xsl:with-param : missing name attribute\n"); - return(NULL); - } - -#ifdef WITH_XSLT_DEBUG_VARIABLE - XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - "Handling xsl:with-param %s\n", comp->name)); -#endif - - if (comp->select == NULL) { - tree = cur->children; - } else { -#ifdef WITH_XSLT_DEBUG_VARIABLE - XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - " select %s\n", comp->select)); -#endif - tree = cur; - } - - elem = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree); - - return(elem); -} - -/** - * xsltParseGlobalVariable: - * @style: the XSLT stylesheet - * @cur: the "variable" element - * - * parse an XSLT transformation variable declaration and record - * its value. - */ - -void -xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur) -{ -#ifdef XSLT_REFACTORED - xsltStyleItemVariablePtr comp; -#else - xsltStylePreCompPtr comp; -#endif - - if ((cur == NULL) || (style == NULL)) - return; - -#ifdef XSLT_REFACTORED - /* - * Note that xsltStylePreCompute() will be called from - * xslt.c only. - */ - comp = (xsltStyleItemVariablePtr) cur->psvi; -#else - xsltStylePreCompute(style, cur); - comp = (xsltStylePreCompPtr) cur->psvi; -#endif - if (comp == NULL) { - xsltTransformError(NULL, style, cur, - "xsl:variable : compilation failed\n"); - return; - } - - if (comp->name == NULL) { - xsltTransformError(NULL, style, cur, - "xsl:variable : missing name attribute\n"); - return; - } - - /* - * Parse the content (a sequence constructor) of xsl:variable. - */ - if (cur->children != NULL) { -#ifdef XSLT_REFACTORED - xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children); -#else - xsltParseTemplateContent(style, cur); -#endif - } -#ifdef WITH_XSLT_DEBUG_VARIABLE - xsltGenericDebug(xsltGenericDebugContext, - "Registering global variable %s\n", comp->name); -#endif - - xsltRegisterGlobalVariable(style, comp->name, comp->ns, - comp->select, cur->children, (xsltStylePreCompPtr) comp, - NULL); -} - -/** - * xsltParseGlobalParam: - * @style: the XSLT stylesheet - * @cur: the "param" element - * - * parse an XSLT transformation param declaration and record - * its value. - */ - -void -xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) { -#ifdef XSLT_REFACTORED - xsltStyleItemParamPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - - if ((cur == NULL) || (style == NULL)) - return; - -#ifdef XSLT_REFACTORED - /* - * Note that xsltStylePreCompute() will be called from - * xslt.c only. - */ - comp = (xsltStyleItemParamPtr) cur->psvi; -#else - xsltStylePreCompute(style, cur); - comp = (xsltStylePreCompPtr) cur->psvi; -#endif - if (comp == NULL) { - xsltTransformError(NULL, style, cur, - "xsl:param : compilation failed\n"); - return; - } - - if (comp->name == NULL) { - xsltTransformError(NULL, style, cur, - "xsl:param : missing name attribute\n"); - return; - } - - /* - * Parse the content (a sequence constructor) of xsl:param. - */ - if (cur->children != NULL) { -#ifdef XSLT_REFACTORED - xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children); -#else - xsltParseTemplateContent(style, cur); -#endif - } - -#ifdef WITH_XSLT_DEBUG_VARIABLE - xsltGenericDebug(xsltGenericDebugContext, - "Registering global param %s\n", comp->name); -#endif - - xsltRegisterGlobalVariable(style, comp->name, comp->ns, - comp->select, cur->children, (xsltStylePreCompPtr) comp, - NULL); -} - -/** - * xsltParseStylesheetVariable: - * @ctxt: the XSLT transformation context - * @cur: the "variable" element - * - * parse an XSLT transformation variable declaration and record - * its value. - */ - -void -xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr cur) -{ -#ifdef XSLT_REFACTORED - xsltStyleItemVariablePtr comp; -#else - xsltStylePreCompPtr comp; -#endif - - if ((cur == NULL) || (ctxt == NULL)) - return; - -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemVariablePtr) cur->psvi; -#else - comp = (xsltStylePreCompPtr) cur->psvi; -#endif - if (comp == NULL) { - xsltTransformError(ctxt, NULL, cur, - "xsl:variable : compilation failed\n"); - return; - } - - if (comp->name == NULL) { - xsltTransformError(ctxt, NULL, cur, - "xsl:variable : missing name attribute\n"); - return; - } - -#ifdef WITH_XSLT_DEBUG_VARIABLE - XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - "Registering variable %s\n", comp->name)); -#endif - - xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, cur->children, 0); -} - -/** - * xsltParseStylesheetParam: - * @ctxt: the XSLT transformation context - * @cur: the "param" element - * - * parse an XSLT transformation param declaration and record - * its value. - */ - -void -xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) -{ -#ifdef XSLT_REFACTORED - xsltStyleItemParamPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - - if ((cur == NULL) || (ctxt == NULL)) - return; -#ifdef XSLT_REFACTORED - comp = (xsltStyleItemParamPtr) cur->psvi; -#else - comp = (xsltStylePreCompPtr) cur->psvi; -#endif - if (comp == NULL) { - xsltTransformError(ctxt, NULL, cur, - "xsl:param : compilation failed\n"); - return; - } - - if (comp->name == NULL) { - xsltTransformError(ctxt, NULL, cur, - "xsl:param : missing name attribute\n"); - return; - } - -#ifdef WITH_XSLT_DEBUG_VARIABLE - XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - "Registering param %s\n", comp->name)); -#endif - - xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, cur->children, 1); -} - -/** - * xsltFreeGlobalVariables: - * @ctxt: the XSLT transformation context - * - * Free up the data associated to the global variables - * its value. - */ - -void -xsltFreeGlobalVariables(xsltTransformContextPtr ctxt) { - xmlHashFree(ctxt->globalVars, (xmlHashDeallocator) xsltFreeStackElem); -} - -/** - * xsltXPathVariableLookup: - * @ctxt: a void * but the the XSLT transformation context actually - * @name: the variable name - * @ns_uri: the variable namespace URI - * - * This is the entry point when a varibale is needed by the XPath - * interpretor. - * - * Returns the value or NULL if not found - */ -xmlXPathObjectPtr -xsltXPathVariableLookup(void *ctxt, const xmlChar *name, - const xmlChar *ns_uri) { - xsltTransformContextPtr context; - xmlXPathObjectPtr ret; - - if ((ctxt == NULL) || (name == NULL)) - return(NULL); - -#ifdef WITH_XSLT_DEBUG_VARIABLE - XSLT_TRACE(((xsltTransformContextPtr)ctxt),XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - "Lookup variable %s\n", name)); -#endif - context = (xsltTransformContextPtr) ctxt; - ret = xsltVariableLookup(context, name, ns_uri); - if (ret == NULL) { - xsltTransformError(ctxt, NULL, NULL, - "unregistered variable %s\n", name); - } -#ifdef WITH_XSLT_DEBUG_VARIABLE - if (ret != NULL) - XSLT_TRACE(context,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, - "found variable %s\n", name)); -#endif - return(ret); -} - - +/* + * variables.c: Implementation of the variable storage and lookup + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "variables.h" +#include "transform.h" +#include "imports.h" +#include "preproc.h" +#include "keys.h" + +#ifdef WITH_XSLT_DEBUG + #define WITH_XSLT_DEBUG_VARIABLE +#endif + +#ifdef XSLT_REFACTORED +const xmlChar *xsltDocFragFake = (const xmlChar *) " fake node libxslt"; +#endif + +const xmlChar *xsltComputingGlobalVarMarker = + (const xmlChar *) " var/param being computed"; + +/************************************************************************ + * * + * Result Value Tree (Result Tree Fragment) interfaces * + * * + ************************************************************************/ +/** + * xsltCreateRVT: + * @ctxt: an XSLT transformation context + * + * Creates a Result Value Tree + * (the XSLT 1.0 term for this is "Result Tree Fragment") + * + * Returns the result value tree or NULL in case of API or internal errors. + */ +xmlDocPtr +xsltCreateRVT(xsltTransformContextPtr ctxt) +{ + xmlDocPtr container; + + /* + * Question: Why is this function public? + * Answer: It is called by the EXSLT module. + */ + if (ctxt == NULL) + return(NULL); + + /* + * Reuse a RTF from the cache if available. + */ + if (ctxt->cache->RVT) { + container = ctxt->cache->RVT; + ctxt->cache->RVT = (xmlDocPtr) container->next; + container->next = NULL; + if (ctxt->cache->nbRVT > 0) + ctxt->cache->nbRVT--; +#ifdef XSLT_DEBUG_PROFILE_CACHE + ctxt->cache->dbgReusedRVTs++; +#endif + return(container); + } + + container = xmlNewDoc(NULL); + if (container == NULL) + return(NULL); + container->dict = ctxt->dict; + xmlDictReference(container->dict); + XSLT_MARK_RES_TREE_FRAG(container); + container->doc = container; + container->parent = NULL; + return(container); +} + +/** + * xsltRegisterTmpRVT: + * @ctxt: an XSLT transformation context + * @RVT: a result value tree (Result Tree Fragment) + * + * Registers the result value tree (XSLT 1.0 term: Result Tree Fragment) + * in the garbage collector. + * The fragment will be freed at the exit of the currently + * instantiated xsl:template. + * Obsolete; this function might produce massive memory overhead, + * since the fragment is only freed when the current xsl:template + * exits. Use xsltRegisterLocalRVT() instead. + * + * Returns 0 in case of success and -1 in case of API or internal errors. + */ +int +xsltRegisterTmpRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) +{ + if ((ctxt == NULL) || (RVT == NULL)) + return(-1); + + /* + * We'll restrict the lifetime of user-created fragments + * insinde an xsl:variable and xsl:param to the lifetime of the + * var/param itself. + */ + if (ctxt->contextVariable != NULL) { + RVT->next = (xmlNodePtr) XSLT_TCTXT_VARIABLE(ctxt)->fragment; + XSLT_TCTXT_VARIABLE(ctxt)->fragment = RVT; + return(0); + } + + RVT->next = (xmlNodePtr) ctxt->tmpRVT; + if (ctxt->tmpRVT != NULL) + ctxt->tmpRVT->prev = (xmlNodePtr) RVT; + ctxt->tmpRVT = RVT; + return(0); +} + +/** + * xsltRegisterLocalRVT: + * @ctxt: an XSLT transformation context + * @RVT: a result value tree (Result Tree Fragment; xmlDocPtr) + * + * Registers a result value tree (XSLT 1.0 term: Result Tree Fragment) + * in the RVT garbage collector. + * The fragment will be freed when the instruction which created the + * fragment exits. + * + * Returns 0 in case of success and -1 in case of API or internal errors. + */ +int +xsltRegisterLocalRVT(xsltTransformContextPtr ctxt, + xmlDocPtr RVT) +{ + if ((ctxt == NULL) || (RVT == NULL)) + return(-1); + + /* + * When evaluating "select" expressions of xsl:variable + * and xsl:param, we need to bind newly created tree fragments + * to the variable itself; otherwise the tragment will be + * freed before we leave the scope of a var. + */ + if ((ctxt->contextVariable != NULL) && + (XSLT_TCTXT_VARIABLE(ctxt)->flags & XSLT_VAR_IN_SELECT)) + { + RVT->next = (xmlNodePtr) XSLT_TCTXT_VARIABLE(ctxt)->fragment; + XSLT_TCTXT_VARIABLE(ctxt)->fragment = RVT; + return(0); + } + /* + * Store the fragment in the scope of the current instruction. + * If not reference by a returning instruction (like EXSLT's function), + * then this fragment will be freed, when the instruction exits. + */ + RVT->next = (xmlNodePtr) ctxt->localRVT; + if (ctxt->localRVT != NULL) + ctxt->localRVT->prev = (xmlNodePtr) RVT; + ctxt->localRVT = RVT; + /* + * We need to keep track of the first registered fragment + * for extension instructions which return fragments + * (e.g. EXSLT'S function), in order to let + * xsltExtensionInstructionResultFinalize() clear the + * preserving flag on the fragments. + */ + if (ctxt->localRVTBase == NULL) + ctxt->localRVTBase = RVT; + return(0); +} + +/** + * xsltExtensionInstructionResultFinalize: + * @ctxt: an XSLT transformation context + * + * Finalizes the data (e.g. result tree fragments) created + * within a value-returning process (e.g. EXSLT's function). + * Tree fragments marked as being returned by a function are + * set to normal state, which means that the fragment garbage + * collector will free them after the function-calling process exits. + * + * Returns 0 in case of success and -1 in case of API or internal errors. + */ +int +xsltExtensionInstructionResultFinalize(xsltTransformContextPtr ctxt) +{ + xmlDocPtr cur; + + if (ctxt == NULL) + return(-1); + if (ctxt->localRVTBase == NULL) + return(0); + /* + * Enable remaining local tree fragments to be freed + * by the fragment garbage collector. + */ + cur = ctxt->localRVTBase; + do { + cur->psvi = NULL; + cur = (xmlDocPtr) cur->next; + } while (cur != NULL); + return(0); +} + +/** + * xsltExtensionInstructionResultRegister: + * @ctxt: an XSLT transformation context + * @nodeSet: a node set to be inspected for result tree fragments + * + * Marks the result of a value-returning extension instruction + * in order to avoid it being garbage collected before the + * extension instruction exits. + * Note that one still has to additionally register any newly created + * tree fragments (via xsltCreateRVT()) with xsltRegisterLocalRVT(). + * + * Returns 0 in case of success and -1 in case of error. + */ +int +xsltExtensionInstructionResultRegister(xsltTransformContextPtr ctxt, + xmlXPathObjectPtr obj) +{ + int i; + xmlNodePtr cur; + xmlDocPtr doc; + + if ((ctxt == NULL) || (obj == NULL)) + return(-1); + + /* + * OPTIMIZE TODO: If no local variables/params and no local tree + * fragments were created, then we don't need to analyse the XPath + * objects for tree fragments. + */ + + if ((obj->type != XPATH_NODESET) && (obj->type != XPATH_XSLT_TREE)) + return(0); + if ((obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) + return(0); + + for (i = 0; i < obj->nodesetval->nodeNr; i++) { + cur = obj->nodesetval->nodeTab[i]; + if (cur->type == XML_NAMESPACE_DECL) { + /* + * The XPath module sets the owner element of a ns-node on + * the ns->next field. + */ + if ((((xmlNsPtr) cur)->next != NULL) && + (((xmlNsPtr) cur)->next->type == XML_ELEMENT_NODE)) + { + cur = (xmlNodePtr) ((xmlNsPtr) cur)->next; + doc = cur->doc; + } else { + xsltTransformError(ctxt, NULL, ctxt->inst, + "Internal error in " + "xsltExtensionInstructionResultRegister(): " + "Cannot retrieve the doc of a namespace node.\n"); + goto error; + } + } else { + doc = cur->doc; + } + if (doc == NULL) { + xsltTransformError(ctxt, NULL, ctxt->inst, + "Internal error in " + "xsltExtensionInstructionResultRegister(): " + "Cannot retrieve the doc of a node.\n"); + goto error; + } + if (doc->name && (doc->name[0] == ' ')) { + /* + * This is a result tree fragment. + * We'll use the @psvi field for reference counting. + * TODO: How do we know if this is a value of a + * global variable or a doc acquired via the + * document() function? + */ + doc->psvi = (void *) ((long) 1); + } + } + + return(0); +error: + return(-1); +} + +/** + * xsltReleaseRVT: + * @ctxt: an XSLT transformation context + * @RVT: a result value tree (Result Tree Fragment) + * + * Either frees the RVT (which is an xmlDoc) or stores + * it in the context's cache for later reuse. + */ +void +xsltReleaseRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) +{ + if (RVT == NULL) + return; + + if (ctxt && (ctxt->cache->nbRVT < 40)) { + /* + * Store the Result Tree Fragment. + * Free the document info. + */ + if (RVT->_private != NULL) { + xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private); + xmlFree(RVT->_private); + RVT->_private = NULL; + } + /* + * Clear the document tree. + * REVISIT TODO: Do we expect ID/IDREF tables to be existent? + */ + if (RVT->children != NULL) { + xmlFreeNodeList(RVT->children); + RVT->children = NULL; + RVT->last = NULL; + } + if (RVT->ids != NULL) { + xmlFreeIDTable((xmlIDTablePtr) RVT->ids); + RVT->ids = NULL; + } + if (RVT->refs != NULL) { + xmlFreeRefTable((xmlRefTablePtr) RVT->refs); + RVT->refs = NULL; + } + + /* + * Reset the reference counter. + */ + RVT->psvi = 0; + + RVT->next = (xmlNodePtr) ctxt->cache->RVT; + ctxt->cache->RVT = RVT; + + ctxt->cache->nbRVT++; + +#ifdef XSLT_DEBUG_PROFILE_CACHE + ctxt->cache->dbgCachedRVTs++; +#endif + return; + } + /* + * Free it. + */ + if (RVT->_private != NULL) { + xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private); + xmlFree(RVT->_private); + } + xmlFreeDoc(RVT); +} + +/** + * xsltRegisterPersistRVT: + * @ctxt: an XSLT transformation context + * @RVT: a result value tree (Result Tree Fragment) + * + * Register the result value tree (XSLT 1.0 term: Result Tree Fragment) + * in the fragment garbage collector. + * The fragment will be freed when the transformation context is + * freed. + * + * Returns 0 in case of success and -1 in case of error. + */ +int +xsltRegisterPersistRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) +{ + if ((ctxt == NULL) || (RVT == NULL)) return(-1); + + RVT->next = (xmlNodePtr) ctxt->persistRVT; + if (ctxt->persistRVT != NULL) + ctxt->persistRVT->prev = (xmlNodePtr) RVT; + ctxt->persistRVT = RVT; + return(0); +} + +/** + * xsltFreeRVTs: + * @ctxt: an XSLT transformation context + * + * Frees all registered result value trees (Result Tree Fragments) + * of the transformation. Internal function; should not be called + * by user-code. + */ +void +xsltFreeRVTs(xsltTransformContextPtr ctxt) +{ + xmlDocPtr cur, next; + + if (ctxt == NULL) + return; + /* + * Local fragments. + */ + cur = ctxt->localRVT; + while (cur != NULL) { + next = (xmlDocPtr) cur->next; + if (cur->_private != NULL) { + xsltFreeDocumentKeys(cur->_private); + xmlFree(cur->_private); + } + xmlFreeDoc(cur); + cur = next; + } + ctxt->localRVT = NULL; + /* + * User-created per-template fragments. + */ + cur = ctxt->tmpRVT; + while (cur != NULL) { + next = (xmlDocPtr) cur->next; + if (cur->_private != NULL) { + xsltFreeDocumentKeys(cur->_private); + xmlFree(cur->_private); + } + xmlFreeDoc(cur); + cur = next; + } + ctxt->tmpRVT = NULL; + /* + * Global fragments. + */ + cur = ctxt->persistRVT; + while (cur != NULL) { + next = (xmlDocPtr) cur->next; + if (cur->_private != NULL) { + xsltFreeDocumentKeys(cur->_private); + xmlFree(cur->_private); + } + xmlFreeDoc(cur); + cur = next; + } + ctxt->persistRVT = NULL; +} + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +/** + * xsltNewStackElem: + * + * Create a new XSLT ParserContext + * + * Returns the newly allocated xsltParserStackElem or NULL in case of error + */ +static xsltStackElemPtr +xsltNewStackElem(xsltTransformContextPtr ctxt) +{ + xsltStackElemPtr ret; + /* + * Reuse a stack item from the cache if available. + */ + if (ctxt && ctxt->cache->stackItems) { + ret = ctxt->cache->stackItems; + ctxt->cache->stackItems = ret->next; + ret->next = NULL; + ctxt->cache->nbStackItems--; +#ifdef XSLT_DEBUG_PROFILE_CACHE + ctxt->cache->dbgReusedVars++; +#endif + return(ret); + } + ret = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem)); + if (ret == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewStackElem : malloc failed\n"); + return(NULL); + } + memset(ret, 0, sizeof(xsltStackElem)); + ret->context = ctxt; + return(ret); +} + +/** + * xsltCopyStackElem: + * @elem: an XSLT stack element + * + * Makes a copy of the stack element + * + * Returns the copy of NULL + */ +static xsltStackElemPtr +xsltCopyStackElem(xsltStackElemPtr elem) { + xsltStackElemPtr cur; + + cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltCopyStackElem : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltStackElem)); + cur->context = elem->context; + cur->name = elem->name; + cur->nameURI = elem->nameURI; + cur->select = elem->select; + cur->tree = elem->tree; + cur->comp = elem->comp; + return(cur); +} + +/** + * xsltFreeStackElem: + * @elem: an XSLT stack element + * + * Free up the memory allocated by @elem + */ +static void +xsltFreeStackElem(xsltStackElemPtr elem) { + if (elem == NULL) + return; + if (elem->value != NULL) + xmlXPathFreeObject(elem->value); + /* + * Release the list of temporary Result Tree Fragments. + */ + if (elem->fragment) { + xmlDocPtr cur; + + while (elem->fragment != NULL) { + cur = elem->fragment; + elem->fragment = (xmlDocPtr) cur->next; + + if (elem->context && + (cur->psvi == (void *) ((long) 1))) + { + /* + * This fragment is a result of an extension instruction + * (e.g. XSLT's function) and needs to be preserved until + * the instruction exits. + * Example: The fragment of the variable must not be freed + * since it is returned by the EXSLT function: + * + * + * + * + * + * + * + */ + xsltRegisterLocalRVT(elem->context, cur); + } else { + xsltReleaseRVT((xsltTransformContextPtr) elem->context, + cur); + } + } + } + /* + * Cache or free the variable structure. + */ + if (elem->context && (elem->context->cache->nbStackItems < 50)) { + /* + * Store the item in the cache. + */ + xsltTransformContextPtr ctxt = elem->context; + memset(elem, 0, sizeof(xsltStackElem)); + elem->context = ctxt; + elem->next = ctxt->cache->stackItems; + ctxt->cache->stackItems = elem; + ctxt->cache->nbStackItems++; +#ifdef XSLT_DEBUG_PROFILE_CACHE + ctxt->cache->dbgCachedVars++; +#endif + return; + } + xmlFree(elem); +} + +/** + * xsltFreeStackElemList: + * @elem: an XSLT stack element + * + * Free up the memory allocated by @elem + */ +void +xsltFreeStackElemList(xsltStackElemPtr elem) { + xsltStackElemPtr next; + + while (elem != NULL) { + next = elem->next; + xsltFreeStackElem(elem); + elem = next; + } +} + +/** + * xsltStackLookup: + * @ctxt: an XSLT transformation context + * @name: the local part of the name + * @nameURI: the URI part of the name + * + * Locate an element in the stack based on its name. + */ +#if 0 /* TODO: Those seem to have been used for debugging. */ +static int stack_addr = 0; +static int stack_cmp = 0; +#endif + +static xsltStackElemPtr +xsltStackLookup(xsltTransformContextPtr ctxt, const xmlChar *name, + const xmlChar *nameURI) { + int i; + xsltStackElemPtr cur; + + if ((ctxt == NULL) || (name == NULL) || (ctxt->varsNr == 0)) + return(NULL); + + /* + * Do the lookup from the top of the stack, but + * don't use params being computed in a call-param + * First lookup expects the variable name and URI to + * come from the disctionnary and hence pointer comparison. + */ + for (i = ctxt->varsNr; i > ctxt->varsBase; i--) { + cur = ctxt->varsTab[i-1]; + while (cur != NULL) { + if ((cur->name == name) && (cur->nameURI == nameURI)) { +#if 0 + stack_addr++; +#endif + return(cur); + } + cur = cur->next; + } + } + + /* + * Redo the lookup with interned string compares + * to avoid string compares. + */ + name = xmlDictLookup(ctxt->dict, name, -1); + if (nameURI != NULL) + nameURI = xmlDictLookup(ctxt->dict, nameURI, -1); + + for (i = ctxt->varsNr; i > ctxt->varsBase; i--) { + cur = ctxt->varsTab[i-1]; + while (cur != NULL) { + if ((cur->name == name) && (cur->nameURI == nameURI)) { +#if 0 + stack_cmp++; +#endif + return(cur); + } + cur = cur->next; + } + } + + return(NULL); +} + +/** + * xsltCheckStackElem: + * @ctxt: xn XSLT transformation context + * @name: the variable name + * @nameURI: the variable namespace URI + * + * Checks whether a variable or param is already defined. + * + * URGENT TODO: Checks for redefinition of vars/params should be + * done only at compilation time. + * + * Returns 1 if variable is present, 2 if param is present, 3 if this + * is an inherited param, 0 if not found, -1 in case of failure. + */ +static int +xsltCheckStackElem(xsltTransformContextPtr ctxt, const xmlChar *name, + const xmlChar *nameURI) { + xsltStackElemPtr cur; + + if ((ctxt == NULL) || (name == NULL)) + return(-1); + + cur = xsltStackLookup(ctxt, name, nameURI); + if (cur == NULL) + return(0); + if (cur->comp != NULL) { + if (cur->comp->type == XSLT_FUNC_WITHPARAM) + return(3); + else if (cur->comp->type == XSLT_FUNC_PARAM) + return(2); + } + + return(1); +} + +/** + * xsltAddStackElem: + * @ctxt: xn XSLT transformation context + * @elem: a stack element + * + * Push an element (or list) onto the stack. + * In case of a list, each member will be pushed into + * a seperate slot; i.e. there's always 1 stack entry for + * 1 stack element. + * + * Returns 0 in case of success, -1 in case of failure. + */ +static int +xsltAddStackElem(xsltTransformContextPtr ctxt, xsltStackElemPtr elem) +{ + if ((ctxt == NULL) || (elem == NULL)) + return(-1); + + do { + 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++] = elem; + ctxt->vars = elem; + + elem = elem->next; + } while (elem != NULL); + + return(0); +} + +/** + * xsltAddStackElemList: + * @ctxt: xn XSLT transformation context + * @elems: a stack element list + * + * Push an element list onto the stack. + * + * Returns 0 in case of success, -1 in case of failure. + */ +int +xsltAddStackElemList(xsltTransformContextPtr ctxt, xsltStackElemPtr elems) +{ + return(xsltAddStackElem(ctxt, elems)); +} + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +/** + * xsltEvalVariable: + * @ctxt: the XSLT transformation context + * @variable: the variable or parameter item + * @comp: the compiled XSLT instruction + * + * Evaluate a variable value. + * + * Returns the XPath Object value or NULL in case of error + */ +static xmlXPathObjectPtr +xsltEvalVariable(xsltTransformContextPtr ctxt, xsltStackElemPtr variable, + xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemVariablePtr comp = + (xsltStyleItemVariablePtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xmlXPathObjectPtr result = NULL; + xmlNodePtr oldInst; + + if ((ctxt == NULL) || (variable == NULL)) + return(NULL); + + /* + * A variable or parameter are evaluated on demand; thus the + * context (of XSLT and XPath) need to be temporarily adjusted and + * restored on exit. + */ + oldInst = ctxt->inst; + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Evaluating variable '%s'\n", variable->name)); +#endif + if (variable->select != NULL) { + xmlXPathCompExprPtr xpExpr = NULL; + xmlDocPtr oldXPDoc; + xmlNodePtr oldXPContextNode; + int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; + xmlNsPtr *oldXPNamespaces; + xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; + xsltStackElemPtr oldVar = ctxt->contextVariable; + + if ((comp != NULL) && (comp->comp != NULL)) { + xpExpr = comp->comp; + } else { + xpExpr = xmlXPathCompile(variable->select); + } + if (xpExpr == NULL) + return(NULL); + /* + * Save context states. + */ + oldXPDoc = xpctxt->doc; + oldXPContextNode = xpctxt->node; + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPContextSize = xpctxt->contextSize; + oldXPNamespaces = xpctxt->namespaces; + oldXPNsNr = xpctxt->nsNr; + + xpctxt->node = ctxt->node; + /* + * OPTIMIZE TODO: Lame try to set the context doc. + * Get rid of this somehow in xpath.c. + */ + if ((ctxt->node->type != XML_NAMESPACE_DECL) && + ctxt->node->doc) + xpctxt->doc = ctxt->node->doc; + /* + * BUG TODO: The proximity position and the context size will + * potentially be wrong. + * Example: + * + * + * + * + * + * + * Here the proximity position and context size are changed + * to the context of , but + * the variable needs to be evaluated in the context of + * . + */ + if (comp != NULL) { + +#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 + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } + + /* + * We need to mark that we are "selecting" a var's value; + * if any tree fragments are created inside the expression, + * then those need to be stored inside the variable; otherwise + * we'll eventually free still referenced fragments, before + * we leave the scope of the variable. + */ + ctxt->contextVariable = variable; + variable->flags |= XSLT_VAR_IN_SELECT; + + result = xmlXPathCompiledEval(xpExpr, xpctxt); + + variable->flags ^= XSLT_VAR_IN_SELECT; + /* + * Restore Context states. + */ + ctxt->contextVariable = oldVar; + + xpctxt->doc = oldXPDoc; + xpctxt->node = oldXPContextNode; + xpctxt->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->namespaces = oldXPNamespaces; + xpctxt->nsNr = oldXPNsNr; + + if ((comp == NULL) || (comp->comp == NULL)) + xmlXPathFreeCompExpr(xpExpr); + if (result == NULL) { + xsltTransformError(ctxt, NULL, + (comp != NULL) ? comp->inst : NULL, + "Failed to evaluate the expression of variable '%s'.\n", + variable->name); + ctxt->state = XSLT_STATE_STOPPED; + +#ifdef WITH_XSLT_DEBUG_VARIABLE +#ifdef LIBXML_DEBUG_ENABLED + } else { + if ((xsltGenericDebugContext == stdout) || + (xsltGenericDebugContext == stderr)) + xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, + result, 0); +#endif +#endif + } + } else { + if (variable->tree == NULL) { + result = xmlXPathNewCString(""); + } else { + if (variable->tree) { + xmlDocPtr container; + xmlNodePtr oldInsert; + xmlDocPtr oldOutput; + xsltStackElemPtr oldVar = ctxt->contextVariable; + + /* + * Generate a result tree fragment. + */ + container = xsltCreateRVT(ctxt); + if (container == NULL) + goto error; + /* + * NOTE: Local Result Tree Fragments of params/variables + * are not registered globally anymore; the life-time + * is not directly dependant of the param/variable itself. + * + * OLD: xsltRegisterTmpRVT(ctxt, container); + */ + /* + * Attach the Result Tree Fragment to the variable; + * when the variable is freed, it will also free + * the Result Tree Fragment. + */ + variable->fragment = container; + + oldOutput = ctxt->output; + oldInsert = ctxt->insert; + + ctxt->output = container; + ctxt->insert = (xmlNodePtr) container; + ctxt->contextVariable = variable; + /* + * Process the sequence constructor (variable->tree). + * The resulting tree will be held by @container. + */ + xsltApplyOneTemplate(ctxt, ctxt->node, variable->tree, + NULL, NULL); + + ctxt->contextVariable = oldVar; + ctxt->insert = oldInsert; + ctxt->output = oldOutput; + + result = xmlXPathNewValueTree((xmlNodePtr) container); + } + if (result == NULL) { + result = xmlXPathNewCString(""); + } else { + /* + * Freeing is not handled there anymore. + * QUESTION TODO: What does the above comment mean? + */ + result->boolval = 0; + } +#ifdef WITH_XSLT_DEBUG_VARIABLE +#ifdef LIBXML_DEBUG_ENABLED + + if ((xsltGenericDebugContext == stdout) || + (xsltGenericDebugContext == stderr)) + xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, + result, 0); +#endif +#endif + } + } + +error: + ctxt->inst = oldInst; + return(result); +} + +/** + * xsltEvalGlobalVariable: + * @elem: the variable or parameter + * @ctxt: the XSLT transformation context + * + * Evaluates a the value of a global xsl:variable or + * xsl:param declaration. + * + * Returns the XPath Object value or NULL in case of error + */ +static xmlXPathObjectPtr +xsltEvalGlobalVariable(xsltStackElemPtr elem, xsltTransformContextPtr ctxt) +{ + xmlXPathObjectPtr result = NULL; + xmlNodePtr oldInst; + const xmlChar* oldVarName; + +#ifdef XSLT_REFACTORED + xsltStyleBasicItemVariablePtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((ctxt == NULL) || (elem == NULL)) + return(NULL); + if (elem->computed) + return(elem->value); + + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Evaluating global variable %s\n", elem->name)); +#endif + +#ifdef WITH_DEBUGGER + if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && + elem->comp && elem->comp->inst) + xslHandleDebugger(elem->comp->inst, NULL, NULL, ctxt); +#endif + + oldInst = ctxt->inst; + comp = elem->comp; + oldVarName = elem->name; + elem->name = xsltComputingGlobalVarMarker; + /* + * OPTIMIZE TODO: We should consider instantiating global vars/params + * on-demand. The vars/params don't need to be evaluated if never + * called; and in the case of global params, if values for such params + * are provided by the user. + */ + if (elem->select != NULL) { + xmlXPathCompExprPtr xpExpr = NULL; + xmlDocPtr oldXPDoc; + xmlNodePtr oldXPContextNode; + int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; + xmlNsPtr *oldXPNamespaces; + xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; + + if ((comp != NULL) && (comp->comp != NULL)) { + xpExpr = comp->comp; + } else { + xpExpr = xmlXPathCompile(elem->select); + } + if (xpExpr == NULL) + goto error; + + + if (comp != NULL) + ctxt->inst = comp->inst; + else + ctxt->inst = NULL; + /* + * SPEC XSLT 1.0: + * "At top-level, the expression or template specifying the + * variable value is evaluated with the same context as that used + * to process the root node of the source document: the current + * node is the root node of the source document and the current + * node list is a list containing just the root node of the source + * document." + */ + /* + * Save context states. + */ + oldXPDoc = xpctxt->doc; + oldXPContextNode = xpctxt->node; + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPContextSize = xpctxt->contextSize; + oldXPNamespaces = xpctxt->namespaces; + oldXPNsNr = xpctxt->nsNr; + + xpctxt->node = ctxt->initialContextNode; + xpctxt->doc = ctxt->initialContextDoc; + xpctxt->contextSize = 1; + xpctxt->proximityPosition = 1; + + if (comp != NULL) { + +#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 + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } + + result = xmlXPathCompiledEval(xpExpr, xpctxt); + + /* + * Restore Context states. + */ + xpctxt->doc = oldXPDoc; + xpctxt->node = oldXPContextNode; + xpctxt->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->namespaces = oldXPNamespaces; + xpctxt->nsNr = oldXPNsNr; + + if ((comp == NULL) || (comp->comp == NULL)) + xmlXPathFreeCompExpr(xpExpr); + if (result == NULL) { + if (comp == NULL) + xsltTransformError(ctxt, NULL, NULL, + "Evaluating global variable %s failed\n", elem->name); + else + xsltTransformError(ctxt, NULL, comp->inst, + "Evaluating global variable %s failed\n", elem->name); + ctxt->state = XSLT_STATE_STOPPED; +#ifdef WITH_XSLT_DEBUG_VARIABLE +#ifdef LIBXML_DEBUG_ENABLED + } else { + if ((xsltGenericDebugContext == stdout) || + (xsltGenericDebugContext == stderr)) + xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, + result, 0); +#endif +#endif + } + } else { + if (elem->tree == NULL) { + result = xmlXPathNewCString(""); + } else { + xmlDocPtr container; + xmlNodePtr oldInsert; + xmlDocPtr oldOutput, oldXPDoc; + /* + * Generate a result tree fragment. + */ + container = xsltCreateRVT(ctxt); + if (container == NULL) + goto error; + /* + * Let the lifetime of the tree fragment be handled by + * the Libxslt's garbage collector. + */ + xsltRegisterPersistRVT(ctxt, container); + + oldOutput = ctxt->output; + oldInsert = ctxt->insert; + + oldXPDoc = ctxt->xpathCtxt->doc; + + ctxt->output = container; + ctxt->insert = (xmlNodePtr) container; + + ctxt->xpathCtxt->doc = ctxt->initialContextDoc; + /* + * Process the sequence constructor. + */ + xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, NULL, NULL); + + ctxt->xpathCtxt->doc = oldXPDoc; + + ctxt->insert = oldInsert; + ctxt->output = oldOutput; + + result = xmlXPathNewValueTree((xmlNodePtr) container); + if (result == NULL) { + result = xmlXPathNewCString(""); + } else { + result->boolval = 0; /* Freeing is not handled there anymore */ + } +#ifdef WITH_XSLT_DEBUG_VARIABLE +#ifdef LIBXML_DEBUG_ENABLED + if ((xsltGenericDebugContext == stdout) || + (xsltGenericDebugContext == stderr)) + xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, + result, 0); +#endif +#endif + } + } + +error: + elem->name = oldVarName; + ctxt->inst = oldInst; + if (result != NULL) { + elem->value = result; + elem->computed = 1; + } + return(result); +} + +/** + * xsltEvalGlobalVariables: + * @ctxt: the XSLT transformation context + * + * Evaluates all global variables and parameters of a stylesheet. + * For internal use only. This is called at start of a transformation. + * + * Returns 0 in case of success, -1 in case of error + */ +int +xsltEvalGlobalVariables(xsltTransformContextPtr ctxt) { + xsltStackElemPtr elem; + xsltStylesheetPtr style; + + if ((ctxt == NULL) || (ctxt->document == NULL)) + return(-1); + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Registering global variables\n")); +#endif + /* + * Walk the list from the stylesheets and populate the hash table + */ + style = ctxt->style; + while (style != NULL) { + elem = style->variables; + +#ifdef WITH_XSLT_DEBUG_VARIABLE + if ((style->doc != NULL) && (style->doc->URL != NULL)) { + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Registering global variables from %s\n", + style->doc->URL)); + } +#endif + + while (elem != NULL) { + xsltStackElemPtr def; + + /* + * Global variables are stored in the variables pool. + */ + def = (xsltStackElemPtr) + xmlHashLookup2(ctxt->globalVars, + elem->name, elem->nameURI); + if (def == NULL) { + + def = xsltCopyStackElem(elem); + xmlHashAddEntry2(ctxt->globalVars, + elem->name, elem->nameURI, def); + } else if ((elem->comp != NULL) && + (elem->comp->type == XSLT_FUNC_VARIABLE)) { + /* + * Redefinition of variables from a different stylesheet + * should not generate a message. + */ + if ((elem->comp->inst != NULL) && + (def->comp != NULL) && (def->comp->inst != NULL) && + (elem->comp->inst->doc == def->comp->inst->doc)) + { + xsltTransformError(ctxt, style, elem->comp->inst, + "Global variable %s already defined\n", elem->name); + if (style != NULL) style->errors++; + } + } + elem = elem->next; + } + + style = xsltNextImport(style); + } + + /* + * This part does the actual evaluation + */ + xmlHashScan(ctxt->globalVars, + (xmlHashScanner) xsltEvalGlobalVariable, ctxt); + + return(0); +} + +/** + * xsltRegisterGlobalVariable: + * @style: the XSLT transformation context + * @name: the variable name + * @ns_uri: the variable namespace URI + * @sel: the expression which need to be evaluated to generate a value + * @tree: the subtree if sel is NULL + * @comp: the precompiled value + * @value: the string value if available + * + * Register a new variable value. If @value is NULL it unregisters + * the variable + * + * Returns 0 in case of success, -1 in case of error + */ +static int +xsltRegisterGlobalVariable(xsltStylesheetPtr style, const xmlChar *name, + const xmlChar *ns_uri, const xmlChar *sel, + xmlNodePtr tree, xsltStylePreCompPtr comp, + const xmlChar *value) { + xsltStackElemPtr elem, tmp; + if (style == NULL) + return(-1); + if (name == NULL) + return(-1); + if (comp == NULL) + return(-1); + +#ifdef WITH_XSLT_DEBUG_VARIABLE + if (comp->type == XSLT_FUNC_PARAM) + xsltGenericDebug(xsltGenericDebugContext, + "Defining global param %s\n", name); + else + xsltGenericDebug(xsltGenericDebugContext, + "Defining global variable %s\n", name); +#endif + + elem = xsltNewStackElem(NULL); + if (elem == NULL) + return(-1); + elem->comp = comp; + elem->name = xmlDictLookup(style->dict, name, -1); + elem->select = xmlDictLookup(style->dict, sel, -1); + if (ns_uri) + elem->nameURI = xmlDictLookup(style->dict, ns_uri, -1); + elem->tree = tree; + tmp = style->variables; + if (tmp == NULL) { + elem->next = NULL; + style->variables = elem; + } else { + while (tmp != NULL) { + if ((elem->comp->type == XSLT_FUNC_VARIABLE) && + (tmp->comp->type == XSLT_FUNC_VARIABLE) && + (xmlStrEqual(elem->name, tmp->name)) && + ((elem->nameURI == tmp->nameURI) || + (xmlStrEqual(elem->nameURI, tmp->nameURI)))) + { + xsltTransformError(NULL, style, comp->inst, + "redefinition of global variable %s\n", elem->name); + style->errors++; + } + if (tmp->next == NULL) + break; + tmp = tmp->next; + } + elem->next = NULL; + tmp->next = elem; + } + if (value != NULL) { + elem->computed = 1; + elem->value = xmlXPathNewString(value); + } + return(0); +} + +/** + * xsltProcessUserParamInternal + * + * @ctxt: the XSLT transformation context + * @name: a null terminated parameter name + * @value: a null terminated value (may be an XPath expression) + * @eval: 0 to treat the value literally, else evaluate as XPath expression + * + * If @eval is 0 then @value is treated literally and is stored in the global + * parameter/variable table without any change. + * + * Uf @eval is 1 then @value is treated as an XPath expression and is + * evaluated. In this case, if you want to pass a string which will be + * interpreted literally then it must be enclosed in single or double quotes. + * If the string contains single quotes (double quotes) then it cannot be + * enclosed single quotes (double quotes). If the string which you want to + * be treated literally contains both single and double quotes (e.g. Meet + * at Joe's for "Twelfth Night" at 7 o'clock) then there is no suitable + * quoting character. You cannot use ' or " inside the string + * because the replacement of character entities with their equivalents is + * done at a different stage of processing. The solution is to call + * xsltQuoteUserParams or xsltQuoteOneUserParam. + * + * This needs to be done on parsed stylesheets before starting to apply + * transformations. Normally this will be called (directly or indirectly) + * only from xsltEvalUserParams, xsltEvalOneUserParam, xsltQuoteUserParams, + * or xsltQuoteOneUserParam. + * + * Returns 0 in case of success, -1 in case of error + */ + +static +int +xsltProcessUserParamInternal(xsltTransformContextPtr ctxt, + const xmlChar * name, + const xmlChar * value, + int eval) { + + xsltStylesheetPtr style; + const xmlChar *prefix; + const xmlChar *href; + xmlXPathCompExprPtr xpExpr; + xmlXPathObjectPtr result; + + xsltStackElemPtr elem; + int res; + void *res_ptr; + + if (ctxt == NULL) + return(-1); + if (name == NULL) + return(0); + if (value == NULL) + return(0); + + style = ctxt->style; + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Evaluating user parameter %s=%s\n", name, value)); +#endif + + /* + * Name lookup + */ + + name = xsltSplitQName(ctxt->dict, name, &prefix); + href = NULL; + if (prefix != NULL) { + xmlNsPtr ns; + + ns = xmlSearchNs(style->doc, xmlDocGetRootElement(style->doc), + prefix); + if (ns == NULL) { + xsltTransformError(ctxt, style, NULL, + "user param : no namespace bound to prefix %s\n", prefix); + href = NULL; + } else { + href = ns->href; + } + } + + if (name == NULL) + return (-1); + + res_ptr = xmlHashLookup2(ctxt->globalVars, name, href); + if (res_ptr != 0) { + xsltTransformError(ctxt, style, NULL, + "Global parameter %s already defined\n", name); + } + if (ctxt->globalVars == NULL) + ctxt->globalVars = xmlHashCreate(20); + + /* + * do not overwrite variables with parameters from the command line + */ + while (style != NULL) { + elem = ctxt->style->variables; + while (elem != NULL) { + if ((elem->comp != NULL) && + (elem->comp->type == XSLT_FUNC_VARIABLE) && + (xmlStrEqual(elem->name, name)) && + (xmlStrEqual(elem->nameURI, href))) { + return(0); + } + elem = elem->next; + } + style = xsltNextImport(style); + } + style = ctxt->style; + elem = NULL; + + /* + * Do the evaluation if @eval is non-zero. + */ + + result = NULL; + if (eval != 0) { + xpExpr = xmlXPathCompile(value); + if (xpExpr != NULL) { + xmlDocPtr oldXPDoc; + xmlNodePtr oldXPContextNode; + int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; + xmlNsPtr *oldXPNamespaces; + xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; + /* + * SPEC XSLT 1.0: + * "At top-level, the expression or template specifying the + * variable value is evaluated with the same context as that used + * to process the root node of the source document: the current + * node is the root node of the source document and the current + * node list is a list containing just the root node of the source + * document." + */ + xpctxt->doc = ctxt->tmpDoc; + /* + * Save context states. + */ + oldXPDoc = xpctxt->doc; + oldXPContextNode = xpctxt->node; + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPContextSize = xpctxt->contextSize; + oldXPNamespaces = xpctxt->namespaces; + oldXPNsNr = xpctxt->nsNr; + + xpctxt->node = ctxt->initialContextNode; + xpctxt->doc = ctxt->initialContextDoc; + xpctxt->contextSize = 1; + xpctxt->proximityPosition = 1; + /* + * There is really no in scope namespace for parameters on the + * command line. + */ + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + + result = xmlXPathCompiledEval(xpExpr, xpctxt); + + /* + * Restore Context states. + */ + xpctxt->doc = oldXPDoc; + xpctxt->node = oldXPContextNode; + xpctxt->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->namespaces = oldXPNamespaces; + xpctxt->nsNr = oldXPNsNr; + + xmlXPathFreeCompExpr(xpExpr); + } + if (result == NULL) { + xsltTransformError(ctxt, style, NULL, + "Evaluating user parameter %s failed\n", name); + ctxt->state = XSLT_STATE_STOPPED; + return(-1); + } + } + + /* + * If @eval is 0 then @value is to be taken literally and result is NULL + * + * If @eval is not 0, then @value is an XPath expression and has been + * successfully evaluated and result contains the resulting value and + * is not NULL. + * + * Now create an xsltStackElemPtr for insertion into the context's + * global variable/parameter hash table. + */ + +#ifdef WITH_XSLT_DEBUG_VARIABLE +#ifdef LIBXML_DEBUG_ENABLED + if ((xsltGenericDebugContext == stdout) || + (xsltGenericDebugContext == stderr)) + xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, + result, 0); +#endif +#endif + + elem = xsltNewStackElem(NULL); + if (elem != NULL) { + elem->name = name; + elem->select = xmlDictLookup(ctxt->dict, value, -1); + if (href != NULL) + elem->nameURI = xmlDictLookup(ctxt->dict, href, -1); + elem->tree = NULL; + elem->computed = 1; + if (eval == 0) { + elem->value = xmlXPathNewString(value); + } + else { + elem->value = result; + } + } + + /* + * Global parameters are stored in the XPath context variables pool. + */ + + res = xmlHashAddEntry2(ctxt->globalVars, name, href, elem); + if (res != 0) { + xsltFreeStackElem(elem); + xsltTransformError(ctxt, style, NULL, + "Global parameter %s already defined\n", name); + } + return(0); +} + +/** + * xsltEvalUserParams: + * + * @ctxt: the XSLT transformation context + * @params: a NULL terminated array of parameters name/value tuples + * + * Evaluate the global variables of a stylesheet. This needs to be + * done on parsed stylesheets before starting to apply transformations. + * Each of the parameters is evaluated as an XPath expression and stored + * in the global variables/parameter hash table. If you want your + * parameter used literally, use xsltQuoteUserParams. + * + * Returns 0 in case of success, -1 in case of error + */ + +int +xsltEvalUserParams(xsltTransformContextPtr ctxt, const char **params) { + int indx = 0; + const xmlChar *name; + const xmlChar *value; + + if (params == NULL) + return(0); + while (params[indx] != NULL) { + name = (const xmlChar *) params[indx++]; + value = (const xmlChar *) params[indx++]; + if (xsltEvalOneUserParam(ctxt, name, value) != 0) + return(-1); + } + return 0; +} + +/** + * xsltQuoteUserParams: + * + * @ctxt: the XSLT transformation context + * @params: a NULL terminated arry of parameters names/values tuples + * + * Similar to xsltEvalUserParams, but the values are treated literally and + * are * *not* evaluated as XPath expressions. This should be done on parsed + * stylesheets before starting to apply transformations. + * + * Returns 0 in case of success, -1 in case of error. + */ + +int +xsltQuoteUserParams(xsltTransformContextPtr ctxt, const char **params) { + int indx = 0; + const xmlChar *name; + const xmlChar *value; + + if (params == NULL) + return(0); + while (params[indx] != NULL) { + name = (const xmlChar *) params[indx++]; + value = (const xmlChar *) params[indx++]; + if (xsltQuoteOneUserParam(ctxt, name, value) != 0) + return(-1); + } + return 0; +} + +/** + * xsltEvalOneUserParam: + * @ctxt: the XSLT transformation context + * @name: a null terminated string giving the name of the parameter + * @value: a null terminated string giving the XPath expression to be evaluated + * + * This is normally called from xsltEvalUserParams to process a single + * parameter from a list of parameters. The @value is evaluated as an + * XPath expression and the result is stored in the context's global + * variable/parameter hash table. + * + * To have a parameter treated literally (not as an XPath expression) + * use xsltQuoteUserParams (or xsltQuoteOneUserParam). For more + * details see description of xsltProcessOneUserParamInternal. + * + * Returns 0 in case of success, -1 in case of error. + */ + +int +xsltEvalOneUserParam(xsltTransformContextPtr ctxt, + const xmlChar * name, + const xmlChar * value) { + return xsltProcessUserParamInternal(ctxt, name, value, + 1 /* xpath eval ? */); +} + +/** + * xsltQuoteOneUserParam: + * @ctxt: the XSLT transformation context + * @name: a null terminated string giving the name of the parameter + * @value: a null terminated string giving the parameter value + * + * This is normally called from xsltQuoteUserParams to process a single + * parameter from a list of parameters. The @value is stored in the + * context's global variable/parameter hash table. + * + * Returns 0 in case of success, -1 in case of error. + */ + +int +xsltQuoteOneUserParam(xsltTransformContextPtr ctxt, + const xmlChar * name, + const xmlChar * value) { + return xsltProcessUserParamInternal(ctxt, name, value, + 0 /* xpath eval ? */); +} + +/** + * xsltBuildVariable: + * @ctxt: the XSLT transformation context + * @comp: the precompiled form + * @tree: the tree if select is NULL + * + * Computes a new variable value. + * + * Returns the xsltStackElemPtr or NULL in case of error + */ +static xsltStackElemPtr +xsltBuildVariable(xsltTransformContextPtr ctxt, + xsltStylePreCompPtr castedComp, + xmlNodePtr tree) +{ +#ifdef XSLT_REFACTORED + xsltStyleBasicItemVariablePtr comp = + (xsltStyleBasicItemVariablePtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xsltStackElemPtr elem; + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Building variable %s", comp->name)); + if (comp->select != NULL) + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + " select %s", comp->select)); + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, "\n")); +#endif + + elem = xsltNewStackElem(ctxt); + if (elem == NULL) + return(NULL); + elem->comp = (xsltStylePreCompPtr) comp; + elem->name = comp->name; + elem->select = comp->select; + elem->nameURI = comp->ns; + elem->tree = tree; + elem->value = xsltEvalVariable(ctxt, elem, + (xsltStylePreCompPtr) comp); + if (elem->value != NULL) + elem->computed = 1; + return(elem); +} + +/** + * xsltRegisterVariable: + * @ctxt: the XSLT transformation context + * @comp: the compiled XSLT-variable (or param) instruction + * @tree: the tree if select is NULL + * @isParam: indicates if this is a parameter + * + * Computes and registers a new variable. + * + * Returns 0 in case of success, -1 in case of error + */ +static int +xsltRegisterVariable(xsltTransformContextPtr ctxt, + xsltStylePreCompPtr castedComp, + xmlNodePtr tree, int isParam) +{ +#ifdef XSLT_REFACTORED + xsltStyleBasicItemVariablePtr comp = + (xsltStyleBasicItemVariablePtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; + int present; +#endif + xsltStackElemPtr variable; + +#ifdef XSLT_REFACTORED + /* + * REFACTORED NOTE: Redefinitions of vars/params are checked + * at compilation time in the refactored code. + * xsl:with-param parameters are checked in xsltApplyXSLTTemplate(). + */ +#else + present = xsltCheckStackElem(ctxt, comp->name, comp->ns); + if (isParam == 0) { + if ((present != 0) && (present != 3)) { + /* TODO: report QName. */ + xsltTransformError(ctxt, NULL, comp->inst, + "XSLT-variable: Redefinition of variable '%s'.\n", comp->name); + return(0); + } + } else if (present != 0) { + if ((present == 1) || (present == 2)) { + /* TODO: report QName. */ + xsltTransformError(ctxt, NULL, comp->inst, + "XSLT-param: Redefinition of parameter '%s'.\n", comp->name); + return(0); + } +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "param %s defined by caller\n", comp->name)); +#endif + return(0); + } +#endif /* else of XSLT_REFACTORED */ + + variable = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree); + xsltAddStackElem(ctxt, variable); + return(0); +} + +/** + * xsltGlobalVariableLookup: + * @ctxt: the XSLT transformation context + * @name: the variable name + * @ns_uri: the variable namespace URI + * + * Search in the Variable array of the context for the given + * variable value. + * + * Returns the value or NULL if not found + */ +static xmlXPathObjectPtr +xsltGlobalVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name, + const xmlChar *ns_uri) { + xsltStackElemPtr elem; + xmlXPathObjectPtr ret = NULL; + + /* + * Lookup the global variables in XPath global variable hash table + */ + if ((ctxt->xpathCtxt == NULL) || (ctxt->globalVars == NULL)) + return(NULL); + elem = (xsltStackElemPtr) + xmlHashLookup2(ctxt->globalVars, name, ns_uri); + if (elem == NULL) { +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "global variable not found %s\n", name)); +#endif + return(NULL); + } + /* + * URGENT TODO: Move the detection of recursive definitions + * to compile-time. + */ + if (elem->computed == 0) { + if (elem->name == xsltComputingGlobalVarMarker) { + xsltTransformError(ctxt, NULL, elem->comp->inst, + "Recursive definition of %s\n", name); + return(NULL); + } + ret = xsltEvalGlobalVariable(elem, ctxt); + } else + ret = elem->value; + return(xmlXPathObjectCopy(ret)); +} + +/** + * xsltVariableLookup: + * @ctxt: the XSLT transformation context + * @name: the variable name + * @ns_uri: the variable namespace URI + * + * Search in the Variable array of the context for the given + * variable value. + * + * Returns the value or NULL if not found + */ +xmlXPathObjectPtr +xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name, + const xmlChar *ns_uri) { + xsltStackElemPtr elem; + + if (ctxt == NULL) + return(NULL); + + elem = xsltStackLookup(ctxt, name, ns_uri); + if (elem == NULL) { + return(xsltGlobalVariableLookup(ctxt, name, ns_uri)); + } + if (elem->computed == 0) { +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "uncomputed variable %s\n", name)); +#endif + elem->value = xsltEvalVariable(ctxt, elem, NULL); + elem->computed = 1; + } + if (elem->value != NULL) + return(xmlXPathObjectCopy(elem->value)); +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "variable not found %s\n", name)); +#endif + return(NULL); +} + +/** + * xsltParseStylesheetCallerParam: + * @ctxt: the XSLT transformation context + * @inst: the xsl:with-param instruction element + * + * Processes an xsl:with-param instruction at transformation time. + * The value is compute, but not recorded. + * NOTE that this is also called with an *xsl:param* element + * from exsltFuncFunctionFunction(). + * + * Returns the new xsltStackElemPtr or NULL + */ + +xsltStackElemPtr +xsltParseStylesheetCallerParam(xsltTransformContextPtr ctxt, xmlNodePtr inst) +{ +#ifdef XSLT_REFACTORED + xsltStyleBasicItemVariablePtr comp; +#else + xsltStylePreCompPtr comp; +#endif + xmlNodePtr tree = NULL; /* The first child node of the instruction or + the instruction itself. */ + xsltStackElemPtr param = NULL; + + if ((ctxt == NULL) || (inst == NULL)) + return(NULL); + +#ifdef XSLT_REFACTORED + comp = (xsltStyleBasicItemVariablePtr) inst->psvi; +#else + comp = (xsltStylePreCompPtr) inst->psvi; +#endif + + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltParseStylesheetCallerParam(): " + "The XSLT 'with-param' instruction was not compiled.\n"); + return(NULL); + } + if (comp->name == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltParseStylesheetCallerParam(): " + "XSLT 'with-param': The attribute 'name' was not compiled.\n"); + return(NULL); + } + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Handling xsl:with-param %s\n", comp->name)); +#endif + + if (comp->select == NULL) { + tree = inst->children; + } else { +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + " select %s\n", comp->select)); +#endif + tree = inst; + } + + param = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree); + + return(param); +} + +/** + * xsltParseGlobalVariable: + * @style: the XSLT stylesheet + * @cur: the "variable" element + * + * Parses a global XSLT 'variable' declaration at compilation time + * and registers it + */ +void +xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemVariablePtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((cur == NULL) || (style == NULL)) + return; + +#ifdef XSLT_REFACTORED + /* + * Note that xsltStylePreCompute() will be called from + * xslt.c only. + */ + comp = (xsltStyleItemVariablePtr) cur->psvi; +#else + xsltStylePreCompute(style, cur); + comp = (xsltStylePreCompPtr) cur->psvi; +#endif + if (comp == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:variable : compilation failed\n"); + return; + } + + if (comp->name == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:variable : missing name attribute\n"); + return; + } + + /* + * Parse the content (a sequence constructor) of xsl:variable. + */ + if (cur->children != NULL) { +#ifdef XSLT_REFACTORED + xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children); +#else + xsltParseTemplateContent(style, cur); +#endif + } +#ifdef WITH_XSLT_DEBUG_VARIABLE + xsltGenericDebug(xsltGenericDebugContext, + "Registering global variable %s\n", comp->name); +#endif + + xsltRegisterGlobalVariable(style, comp->name, comp->ns, + comp->select, cur->children, (xsltStylePreCompPtr) comp, + NULL); +} + +/** + * xsltParseGlobalParam: + * @style: the XSLT stylesheet + * @cur: the "param" element + * + * parse an XSLT transformation param declaration and record + * its value. + */ + +void +xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) { +#ifdef XSLT_REFACTORED + xsltStyleItemParamPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((cur == NULL) || (style == NULL)) + return; + +#ifdef XSLT_REFACTORED + /* + * Note that xsltStylePreCompute() will be called from + * xslt.c only. + */ + comp = (xsltStyleItemParamPtr) cur->psvi; +#else + xsltStylePreCompute(style, cur); + comp = (xsltStylePreCompPtr) cur->psvi; +#endif + if (comp == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:param : compilation failed\n"); + return; + } + + if (comp->name == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:param : missing name attribute\n"); + return; + } + + /* + * Parse the content (a sequence constructor) of xsl:param. + */ + if (cur->children != NULL) { +#ifdef XSLT_REFACTORED + xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children); +#else + xsltParseTemplateContent(style, cur); +#endif + } + +#ifdef WITH_XSLT_DEBUG_VARIABLE + xsltGenericDebug(xsltGenericDebugContext, + "Registering global param %s\n", comp->name); +#endif + + xsltRegisterGlobalVariable(style, comp->name, comp->ns, + comp->select, cur->children, (xsltStylePreCompPtr) comp, + NULL); +} + +/** + * xsltParseStylesheetVariable: + * @ctxt: the XSLT transformation context + * @inst: the xsl:variable instruction element + * + * Registers a local XSLT 'variable' instruction at transformation time + * and evaluates its value. + */ +void +xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr inst) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemVariablePtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((inst == NULL) || (ctxt == NULL)) + return; + + comp = inst->psvi; + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltParseStylesheetVariable(): " + "The XSLT 'variable' instruction was not compiled.\n"); + return; + } + if (comp->name == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltParseStylesheetVariable(): " + "The attribute 'name' was not compiled.\n"); + return; + } + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Registering variable '%s'\n", comp->name)); +#endif + + xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, inst->children, 0); +} + +/** + * xsltParseStylesheetParam: + * @ctxt: the XSLT transformation context + * @cur: the XSLT 'param' element + * + * Registers a local XSLT 'param' declaration at transformation time and + * evaluates its value. + */ +void +xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemParamPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((cur == NULL) || (ctxt == NULL)) + return; + + comp = cur->psvi; + if ((comp == NULL) || (comp->name == NULL)) { + xsltTransformError(ctxt, NULL, cur, + "Internal error in xsltParseStylesheetParam(): " + "The XSLT 'param' declaration was not compiled correctly.\n"); + return; + } + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Registering param %s\n", comp->name)); +#endif + + xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, cur->children, 1); +} + +/** + * xsltFreeGlobalVariables: + * @ctxt: the XSLT transformation context + * + * Free up the data associated to the global variables + * its value. + */ + +void +xsltFreeGlobalVariables(xsltTransformContextPtr ctxt) { + xmlHashFree(ctxt->globalVars, (xmlHashDeallocator) xsltFreeStackElem); +} + +/** + * xsltXPathVariableLookup: + * @ctxt: a void * but the the XSLT transformation context actually + * @name: the variable name + * @ns_uri: the variable namespace URI + * + * This is the entry point when a varibale is needed by the XPath + * interpretor. + * + * Returns the value or NULL if not found + */ +xmlXPathObjectPtr +xsltXPathVariableLookup(void *ctxt, const xmlChar *name, + const xmlChar *ns_uri) { + xsltTransformContextPtr tctxt; + xmlXPathObjectPtr valueObj = NULL; + + if ((ctxt == NULL) || (name == NULL)) + return(NULL); + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(((xsltTransformContextPtr)ctxt),XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Lookup variable '%s'\n", name)); +#endif + + tctxt = (xsltTransformContextPtr) ctxt; + /* + * Local variables/params --------------------------------------------- + * + * Do the lookup from the top of the stack, but + * don't use params being computed in a call-param + * First lookup expects the variable name and URI to + * come from the disctionnary and hence pointer comparison. + */ + if (tctxt->varsNr != 0) { + int i; + xsltStackElemPtr variable = NULL, cur; + + for (i = tctxt->varsNr; i > tctxt->varsBase; i--) { + cur = tctxt->varsTab[i-1]; + if ((cur->name == name) && (cur->nameURI == ns_uri)) { +#if 0 + stack_addr++; +#endif + variable = cur; + goto local_variable_found; + } + cur = cur->next; + } + /* + * Redo the lookup with interned strings to avoid string comparison. + * + * OPTIMIZE TODO: The problem here is, that if we request a + * global variable, then this will be also executed. + */ + { + const xmlChar *tmpName = name, *tmpNsName = ns_uri; + + name = xmlDictLookup(tctxt->dict, name, -1); + if (ns_uri) + ns_uri = xmlDictLookup(tctxt->dict, ns_uri, -1); + if ((tmpName != name) || (tmpNsName != ns_uri)) { + for (i = tctxt->varsNr; i > tctxt->varsBase; i--) { + cur = tctxt->varsTab[i-1]; + if ((cur->name == name) && (cur->nameURI == ns_uri)) { +#if 0 + stack_cmp++; +#endif + variable = cur; + goto local_variable_found; + } + } + } + } + +local_variable_found: + + if (variable) { + if (variable->computed == 0) { + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "uncomputed variable '%s'\n", name)); +#endif + variable->value = xsltEvalVariable(tctxt, variable, NULL); + variable->computed = 1; + } + if (variable->value != NULL) { + valueObj = xmlXPathObjectCopy(variable->value); + } + return(valueObj); + } + } + /* + * Global variables/params -------------------------------------------- + */ + if (tctxt->globalVars) { + valueObj = xsltGlobalVariableLookup(tctxt, name, ns_uri); + } + + if (valueObj == NULL) { + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "variable not found '%s'\n", name)); +#endif + + if (ns_uri) { + xsltTransformError(tctxt, NULL, tctxt->inst, + "Variable '{%s}%s' has not been declared.\n", ns_uri, name); + } else { + xsltTransformError(tctxt, NULL, tctxt->inst, + "Variable '%s' has not been declared.\n", name); + } + } else { + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "found variable '%s'\n", name)); +#endif + } + + return(valueObj); +} + + diff --git a/libxslt/xslt.c b/libxslt/xslt.c index 96141c62..67899c15 100644 --- a/libxslt/xslt.c +++ b/libxslt/xslt.c @@ -1,6691 +1,6901 @@ -/* - * xslt.c: Implemetation of an XSL Transformation 1.0 engine - * - * Reference: - * XSLT specification - * http://www.w3.org/TR/1999/REC-xslt-19991116 - * - * Associating Style Sheets with XML documents - * http://www.w3.org/1999/06/REC-xml-stylesheet-19990629 - * - * See Copyright for the status of this software. - * - * daniel@veillard.com - */ - -#define IN_LIBXSLT -#include "libxslt.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "xslt.h" -#include "xsltInternals.h" -#include "pattern.h" -#include "variables.h" -#include "namespaces.h" -#include "attributes.h" -#include "xsltutils.h" -#include "imports.h" -#include "keys.h" -#include "documents.h" -#include "extensions.h" -#include "preproc.h" -#include "extra.h" -#include "security.h" - -#ifdef WITH_XSLT_DEBUG -#define WITH_XSLT_DEBUG_PARSING -/* #define WITH_XSLT_DEBUG_BLANKS */ -#endif - -const char *xsltEngineVersion = LIBXSLT_VERSION_STRING LIBXSLT_VERSION_EXTRA; -const int xsltLibxsltVersion = LIBXSLT_VERSION; -const int xsltLibxmlVersion = LIBXML_VERSION; - -#ifdef XSLT_REFACTORED - -const xmlChar *xsltConstNamespaceNameXSLT= (const xmlChar *) XSLT_NAMESPACE; - -/* -* xsltLiteralResultMarker: -* Marker for Literal result elements, in order to avoid multiple attempts -* to recognize such elements in the stylesheet's tree. -* This marker is set on node->psvi during the initial traversal -* of a stylesheet's node tree. -* -const xmlChar *xsltLiteralResultMarker = - (const xmlChar *) "Literal Result Element"; -*/ - -/* -* xsltXSLTTextMarker: -* Marker for xsl:text elements. Used to recognize xsl:text elements -* for post-processing of the stylesheet's tree, where those -* elements are removed from the tree. -*/ -const xmlChar *xsltXSLTTextMarker = (const xmlChar *) "XSLT Text Element"; - -/* -* xsltXSLTAttrMarker: -* Marker for XSLT attribute on Literal Result Elements. -*/ -const xmlChar *xsltXSLTAttrMarker = (const xmlChar *) "LRE XSLT Attr"; - -#endif - -/* - * Harmless but avoiding a problem when compiling against a - * libxml <= 2.3.11 without LIBXML_DEBUG_ENABLED - */ -#ifndef LIBXML_DEBUG_ENABLED -double xmlXPathStringEvalNumber(const xmlChar *str); -#endif -/* - * Useful macros - */ - -#ifdef IS_BLANK -#undef IS_BLANK -#endif -#define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \ - ((c) == 0x0D)) - -#ifdef IS_BLANK_NODE -#undef IS_BLANK_NODE -#endif -#define IS_BLANK_NODE(n) \ - (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content))) - -/** - * xsltParseContentError: - * - * @style: the stylesheet - * @node: the node where the error occured - * - * Compile-time error function. - */ -static void -xsltParseContentError(xsltStylesheetPtr style, - xmlNodePtr node) -{ - if ((style == NULL) || (node == NULL)) - return; - - if (IS_XSLT_ELEM(node)) - xsltTransformError(NULL, style, node, - "The XSLT-element '%s' is not allowed at this position.\n", - node->name); - else - xsltTransformError(NULL, style, node, - "The element '%s' is not allowed at this position.\n", - node->name); - style->errors++; -} - -#ifdef XSLT_REFACTORED -#else -/** - * exclPrefixPush: - * @style: the transformation stylesheet - * @value: the excluded namespace name to push on the stack - * - * Push an excluded namespace name on the stack - * - * Returns the new index in the stack or 0 in case of error - */ -static int -exclPrefixPush(xsltStylesheetPtr style, xmlChar * value) -{ - if (style->exclPrefixMax == 0) { - style->exclPrefixMax = 4; - style->exclPrefixTab = - (xmlChar * *)xmlMalloc(style->exclPrefixMax * - sizeof(style->exclPrefixTab[0])); - if (style->exclPrefixTab == NULL) { - xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); - return (0); - } - } - if (style->exclPrefixNr >= style->exclPrefixMax) { - style->exclPrefixMax *= 2; - style->exclPrefixTab = - (xmlChar * *)xmlRealloc(style->exclPrefixTab, - style->exclPrefixMax * - sizeof(style->exclPrefixTab[0])); - if (style->exclPrefixTab == NULL) { - xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); - return (0); - } - } - style->exclPrefixTab[style->exclPrefixNr] = value; - style->exclPrefix = value; - return (style->exclPrefixNr++); -} -/** - * exclPrefixPop: - * @style: the transformation stylesheet - * - * Pop an excluded prefix value from the stack - * - * Returns the stored excluded prefix value - */ -static xmlChar * -exclPrefixPop(xsltStylesheetPtr style) -{ - xmlChar *ret; - - if (style->exclPrefixNr <= 0) - return (0); - style->exclPrefixNr--; - if (style->exclPrefixNr > 0) - style->exclPrefix = style->exclPrefixTab[style->exclPrefixNr - 1]; - else - style->exclPrefix = NULL; - ret = style->exclPrefixTab[style->exclPrefixNr]; - style->exclPrefixTab[style->exclPrefixNr] = 0; - return (ret); -} -#endif - -/************************************************************************ - * * - * Helper functions * - * * - ************************************************************************/ - -/** - * xsltInit: - * - * Initializes the processor (e.g. registers built-in extensions, - * etc.) - */ - -static int initialized = 0; - -void -xsltInit (void) { - if (initialized == 0) { - initialized = 1; - xsltRegisterAllExtras(); - } -} - -/** - * xsltUninit - * - * Uninitializes the processor. - */ - -void -xsltUninit (void) { - initialized = 0; -} - -/** - * xsltIsBlank: - * @str: a string - * - * Check if a string is ignorable - * - * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise - */ -int -xsltIsBlank(xmlChar *str) { - if (str == NULL) - return(1); - while (*str != 0) { - if (!(IS_BLANK(*str))) return(0); - str++; - } - return(1); -} - -/************************************************************************ - * * - * Routines to handle XSLT data structures * - * * - ************************************************************************/ -static xsltDecimalFormatPtr -xsltNewDecimalFormat(xmlChar *name) -{ - xsltDecimalFormatPtr self; - /* UTF-8 for 0x2030 */ - static const xmlChar permille[4] = {0xe2, 0x80, 0xb0, 0}; - - self = xmlMalloc(sizeof(xsltDecimalFormat)); - if (self != NULL) { - self->next = NULL; - self->name = name; - - /* Default values */ - self->digit = xmlStrdup(BAD_CAST("#")); - self->patternSeparator = xmlStrdup(BAD_CAST(";")); - self->decimalPoint = xmlStrdup(BAD_CAST(".")); - self->grouping = xmlStrdup(BAD_CAST(",")); - self->percent = xmlStrdup(BAD_CAST("%")); - self->permille = xmlStrdup(BAD_CAST(permille)); - self->zeroDigit = xmlStrdup(BAD_CAST("0")); - self->minusSign = xmlStrdup(BAD_CAST("-")); - self->infinity = xmlStrdup(BAD_CAST("Infinity")); - self->noNumber = xmlStrdup(BAD_CAST("NaN")); - } - return self; -} - -static void -xsltFreeDecimalFormat(xsltDecimalFormatPtr self) -{ - if (self != NULL) { - if (self->digit) - xmlFree(self->digit); - if (self->patternSeparator) - xmlFree(self->patternSeparator); - if (self->decimalPoint) - xmlFree(self->decimalPoint); - if (self->grouping) - xmlFree(self->grouping); - if (self->percent) - xmlFree(self->percent); - if (self->permille) - xmlFree(self->permille); - if (self->zeroDigit) - xmlFree(self->zeroDigit); - if (self->minusSign) - xmlFree(self->minusSign); - if (self->infinity) - xmlFree(self->infinity); - if (self->noNumber) - xmlFree(self->noNumber); - if (self->name) - xmlFree(self->name); - xmlFree(self); - } -} - -static void -xsltFreeDecimalFormatList(xsltStylesheetPtr self) -{ - xsltDecimalFormatPtr iter; - xsltDecimalFormatPtr tmp; - - if (self == NULL) - return; - - iter = self->decimalFormat; - while (iter != NULL) { - tmp = iter->next; - xsltFreeDecimalFormat(iter); - iter = tmp; - } -} - -/** - * xsltDecimalFormatGetByName: - * @style: the XSLT stylesheet - * @name: the decimal-format name to find - * - * Find decimal-format by name - * - * Returns the xsltDecimalFormatPtr - */ -xsltDecimalFormatPtr -xsltDecimalFormatGetByName(xsltStylesheetPtr style, xmlChar *name) -{ - xsltDecimalFormatPtr result = NULL; - - if (name == NULL) - return style->decimalFormat; - - while (style != NULL) { - for (result = style->decimalFormat->next; - result != NULL; - result = result->next) { - if (xmlStrEqual(name, result->name)) - return result; - } - style = xsltNextImport(style); - } - return result; -} - - -/** - * xsltNewTemplate: - * - * Create a new XSLT Template - * - * Returns the newly allocated xsltTemplatePtr or NULL in case of error - */ -static xsltTemplatePtr -xsltNewTemplate(void) { - xsltTemplatePtr cur; - - cur = (xsltTemplatePtr) xmlMalloc(sizeof(xsltTemplate)); - if (cur == NULL) { - xsltTransformError(NULL, NULL, NULL, - "xsltNewTemplate : malloc failed\n"); - return(NULL); - } - memset(cur, 0, sizeof(xsltTemplate)); - cur->priority = XSLT_PAT_NO_PRIORITY; - return(cur); -} - -/** - * xsltFreeTemplate: - * @template: an XSLT template - * - * Free up the memory allocated by @template - */ -static void -xsltFreeTemplate(xsltTemplatePtr template) { - if (template == NULL) - return; - if (template->match) xmlFree(template->match); -/* -* NOTE: @name and @nameURI are put into the string dict now. -* if (template->name) xmlFree(template->name); -* if (template->nameURI) xmlFree(template->nameURI); -*/ -/* - if (template->mode) xmlFree(template->mode); - if (template->modeURI) xmlFree(template->modeURI); - */ - if (template->inheritedNs) xmlFree(template->inheritedNs); - memset(template, -1, sizeof(xsltTemplate)); - xmlFree(template); -} - -/** - * xsltFreeTemplateList: - * @template: an XSLT template list - * - * Free up the memory allocated by all the elements of @template - */ -static void -xsltFreeTemplateList(xsltTemplatePtr template) { - xsltTemplatePtr cur; - - while (template != NULL) { - cur = template; - template = template->next; - xsltFreeTemplate(cur); - } -} - -#ifdef XSLT_REFACTORED - -static void -xsltFreeNsAliasList(xsltNsAliasPtr item) -{ - xsltNsAliasPtr tmp; - - while (item) { - tmp = item; - item = item->next; - xmlFree(tmp); - } - return; -} - -#ifdef XSLT_REFACTORED_XSLT_NSCOMP -static void -xsltFreeNamespaceMap(xsltNsMapPtr item) -{ - xsltNsMapPtr tmp; - - while (item) { - tmp = item; - item = item->next; - xmlFree(tmp); - } - return; -} - -static xsltNsMapPtr -xsltNewNamespaceMapItem(xsltCompilerCtxtPtr cctxt, - xmlDocPtr doc, - xmlNsPtr ns, - xmlNodePtr elem) -{ - xsltNsMapPtr ret; - - if ((cctxt == NULL) || (doc == NULL) || (ns == NULL)) - return(NULL); - - ret = (xsltNsMapPtr) xmlMalloc(sizeof(xsltNsMap)); - if (ret == NULL) { - xsltTransformError(NULL, cctxt->style, elem, - "Internal error: (xsltNewNamespaceMapItem) " - "memory allocation failed.\n"); - return(NULL); - } - memset(ret, 0, sizeof(xsltNsMap)); - ret->doc = doc; - ret->ns = ns; - ret->origNsName = ns->href; - /* - * Store the item at current stylesheet-level. - */ - if (cctxt->psData->nsMap != NULL) - ret->next = cctxt->psData->nsMap; - cctxt->psData->nsMap = ret; - - return(ret); -} -#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ - -/** - * xsltCompilerCtxtFree: - * - * Free an XSLT compiler context. - */ -static void -xsltCompilationCtxtFree(xsltCompilerCtxtPtr cctxt) -{ - if (cctxt == NULL) - return; -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "Freeing compilation context\n"); - xsltGenericDebug(xsltGenericDebugContext, - "### Max inodes: %d\n", cctxt->maxNodeInfos); - xsltGenericDebug(xsltGenericDebugContext, - "### Max LREs : %d\n", cctxt->maxLREs); -#endif - /* - * Free node-infos. - */ - if (cctxt->inodeList != NULL) { - xsltCompilerNodeInfoPtr tmp, cur = cctxt->inodeList; - while (cur != NULL) { - tmp = cur; - cur = cur->next; - xmlFree(tmp); - } - } - if (cctxt->tmpList != NULL) - xsltPointerListFree(cctxt->tmpList); -#ifdef XSLT_REFACTORED_XPATHCOMP - if (cctxt->xpathCtxt != NULL) - xmlXPathFreeContext(cctxt->xpathCtxt); -#endif - if (cctxt->nsAliases != NULL) - xsltFreeNsAliasList(cctxt->nsAliases); - - xmlFree(cctxt); -} - -/** - * xsltCompilerCreate: - * - * Creates an XSLT compiler context. - * - * Returns the pointer to the created xsltCompilerCtxt or - * NULL in case of an internal error. - */ -static xsltCompilerCtxtPtr -xsltCompilationCtxtCreate(xsltStylesheetPtr style) { - xsltCompilerCtxtPtr ret; - - ret = (xsltCompilerCtxtPtr) xmlMalloc(sizeof(xsltCompilerCtxt)); - if (ret == NULL) { - xsltTransformError(NULL, style, NULL, - "xsltCompilerCreate: allocation of compiler " - "context failed.\n"); - return(NULL); - } - memset(ret, 0, sizeof(xsltCompilerCtxt)); - - ret->errSeverity = XSLT_ERROR_SEVERITY_ERROR; - ret->tmpList = xsltPointerListCreate(20); - if (ret->tmpList == NULL) { - goto internal_err; - } -#ifdef XSLT_REFACTORED_XPATHCOMP - /* - * Create the XPath compilation context in order - * to speed up precompilation of XPath expressions. - */ - ret->xpathCtxt = xmlXPathNewContext(NULL); - if (ret->xpathCtxt == NULL) - goto internal_err; -#endif - - return(ret); - -internal_err: - xsltCompilationCtxtFree(ret); - return(NULL); -} - -static void -xsltLREEffectiveNsNodesFree(xsltEffectiveNsPtr first) -{ - xsltEffectiveNsPtr tmp; - - while (first != NULL) { - tmp = first; - first = first->nextInStore; - xmlFree(tmp); - } -} - -static void -xsltFreePrincipalStylesheetData(xsltPrincipalStylesheetDataPtr data) -{ - if (data == NULL) - return; - - if (data->inScopeNamespaces != NULL) { - int i; - xsltNsListContainerPtr nsi; - xsltPointerListPtr list = - (xsltPointerListPtr) data->inScopeNamespaces; - - for (i = 0; i < list->number; i++) { - /* - * REVISIT TODO: Free info of in-scope namespaces. - */ - nsi = (xsltNsListContainerPtr) list->items[i]; - if (nsi->list != NULL) - xmlFree(nsi->list); - xmlFree(nsi); - } - xsltPointerListFree(list); - data->inScopeNamespaces = NULL; - } - - if (data->exclResultNamespaces != NULL) { - int i; - xsltPointerListPtr list = (xsltPointerListPtr) - data->exclResultNamespaces; - - for (i = 0; i < list->number; i++) - xsltPointerListFree((xsltPointerListPtr) list->items[i]); - - xsltPointerListFree(list); - data->exclResultNamespaces = NULL; - } - - if (data->extElemNamespaces != NULL) { - xsltPointerListPtr list = (xsltPointerListPtr) - data->extElemNamespaces; - int i; - - for (i = 0; i < list->number; i++) - xsltPointerListFree((xsltPointerListPtr) list->items[i]); - - xsltPointerListFree(list); - data->extElemNamespaces = NULL; - } - if (data->effectiveNs) { - xsltLREEffectiveNsNodesFree(data->effectiveNs); - data->effectiveNs = NULL; - } -#ifdef XSLT_REFACTORED_XSLT_NSCOMP - xsltFreeNamespaceMap(data->nsMap); -#endif - xmlFree(data); -} - -static xsltPrincipalStylesheetDataPtr -xsltNewPrincipalStylesheetData(void) -{ - xsltPrincipalStylesheetDataPtr ret; - - ret = (xsltPrincipalStylesheetDataPtr) - xmlMalloc(sizeof(xsltPrincipalStylesheetData)); - if (ret == NULL) { - xsltTransformError(NULL, NULL, NULL, - "xsltNewPrincipalStylesheetData: memory allocation failed.\n"); - return(NULL); - } - memset(ret, 0, sizeof(xsltPrincipalStylesheetData)); - - /* - * Global list of in-scope namespaces. - */ - ret->inScopeNamespaces = xsltPointerListCreate(-1); - if (ret->inScopeNamespaces == NULL) - goto internal_err; - /* - * Global list of excluded result ns-decls. - */ - ret->exclResultNamespaces = xsltPointerListCreate(-1); - if (ret->exclResultNamespaces == NULL) - goto internal_err; - /* - * Global list of extension instruction namespace names. - */ - ret->extElemNamespaces = xsltPointerListCreate(-1); - if (ret->extElemNamespaces == NULL) - goto internal_err; - - return(ret); - -internal_err: - - return(NULL); -} - -#endif - -/** - * xsltNewStylesheet: - * - * Create a new XSLT Stylesheet - * - * Returns the newly allocated xsltStylesheetPtr or NULL in case of error - */ -xsltStylesheetPtr -xsltNewStylesheet(void) { - xsltStylesheetPtr ret = NULL; - - ret = (xsltStylesheetPtr) xmlMalloc(sizeof(xsltStylesheet)); - if (ret == NULL) { - xsltTransformError(NULL, NULL, NULL, - "xsltNewStylesheet : malloc failed\n"); - goto internal_err; - } - memset(ret, 0, sizeof(xsltStylesheet)); - - ret->omitXmlDeclaration = -1; - ret->standalone = -1; - ret->decimalFormat = xsltNewDecimalFormat(NULL); - ret->indent = -1; - ret->errors = 0; - ret->warnings = 0; - ret->exclPrefixNr = 0; - ret->exclPrefixMax = 0; - ret->exclPrefixTab = NULL; - ret->extInfos = NULL; - ret->extrasNr = 0; - ret->internalized = 1; - ret->literal_result = 0; - ret->dict = xmlDictCreate(); -#ifdef WITH_XSLT_DEBUG - xsltGenericDebug(xsltGenericDebugContext, - "creating dictionary for stylesheet\n"); -#endif - - xsltInit(); - - return(ret); - -internal_err: - if (ret != NULL) - xsltFreeStylesheet(ret); - return(NULL); -} - -/** - * xsltAllocateExtra: - * @style: an XSLT stylesheet - * - * Allocate an extra runtime information slot statically while compiling - * the stylesheet and return its number - * - * Returns the number of the slot - */ -int -xsltAllocateExtra(xsltStylesheetPtr style) -{ - return(style->extrasNr++); -} - -/** - * xsltAllocateExtraCtxt: - * @ctxt: an XSLT transformation context - * - * Allocate an extra runtime information slot at run-time - * and return its number - * This make sure there is a slot ready in the transformation context - * - * Returns the number of the slot - */ -int -xsltAllocateExtraCtxt(xsltTransformContextPtr ctxt) -{ - if (ctxt->extrasNr >= ctxt->extrasMax) { - int i; - if (ctxt->extrasNr == 0) { - ctxt->extrasMax = 20; - ctxt->extras = (xsltRuntimeExtraPtr) - xmlMalloc(ctxt->extrasMax * sizeof(xsltRuntimeExtra)); - if (ctxt->extras == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xsltAllocateExtraCtxt: out of memory\n"); - ctxt->state = XSLT_STATE_ERROR; - return(0); - } - for (i = 0;i < ctxt->extrasMax;i++) { - ctxt->extras[i].info = NULL; - ctxt->extras[i].deallocate = NULL; - ctxt->extras[i].val.ptr = NULL; - } - - } else { - xsltRuntimeExtraPtr tmp; - - ctxt->extrasMax += 100; - tmp = (xsltRuntimeExtraPtr) xmlRealloc(ctxt->extras, - ctxt->extrasMax * sizeof(xsltRuntimeExtra)); - if (tmp == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xsltAllocateExtraCtxt: out of memory\n"); - ctxt->state = XSLT_STATE_ERROR; - return(0); - } - ctxt->extras = tmp; - for (i = ctxt->extrasNr;i < ctxt->extrasMax;i++) { - ctxt->extras[i].info = NULL; - ctxt->extras[i].deallocate = NULL; - ctxt->extras[i].val.ptr = NULL; - } - } - } - return(ctxt->extrasNr++); -} - -/** - * xsltFreeStylesheetList: - * @style: an XSLT stylesheet list - * - * Free up the memory allocated by the list @style - */ -static void -xsltFreeStylesheetList(xsltStylesheetPtr style) { - xsltStylesheetPtr next; - - while (style != NULL) { - next = style->next; - xsltFreeStylesheet(style); - style = next; - } -} - -/** - * xsltCleanupStylesheetTree: - * - * @doc: the document-node - * @node: the element where the stylesheet is rooted at - * - * Actually @node need not be the document-element, but - * currently Libxslt does not support embedeed stylesheets. - * - * Returns 0 if OK, -1 on API or internal errors. - */ -static int -xsltCleanupStylesheetTree(xmlDocPtr doc, xmlNodePtr rootElem) -{ -#if 0 /* TODO: Currently disabled, since probably not needed. */ - xmlNodePtr cur; - - if ((doc == NULL) || (rootElem == NULL) || - (rootElem->type != XML_ELEMENT_NODE) || - (doc != rootElem->doc)) - return(-1); - - /* - * Cleanup was suggested by Aleksey Sanin: - * Clear the PSVI field to avoid problems if the - * node-tree of the stylesheet is intended to be used for - * further processing by the user (e.g. for compiling it - * once again - although not recommended). - */ - - cur = rootElem; - while (cur != NULL) { - if (cur->type == XML_ELEMENT_NODE) { - /* - * Clear the PSVI field. - */ - cur->psvi = NULL; - if (cur->children) { - cur = cur->children; - continue; - } - } - -leave_node: - if (cur == rootElem) - break; - if (cur->next != NULL) - cur = cur->next; - else { - cur = cur->parent; - if (cur == NULL) - break; - goto leave_node; - } - } -#endif /* #if 0 */ - return(0); -} - -/** - * xsltFreeStylesheet: - * @style: an XSLT stylesheet - * - * Free up the memory allocated by @style - */ -void -xsltFreeStylesheet(xsltStylesheetPtr style) -{ - if (style == NULL) - return; - -#ifdef XSLT_REFACTORED - /* - * Start with a cleanup of the main stylesheet's doc. - */ - if ((style->principal == style) && (style->doc)) - xsltCleanupStylesheetTree(style->doc, - xmlDocGetRootElement(style->doc)); -#ifdef XSLT_REFACTORED_XSLT_NSCOMP - /* - * Restore changed ns-decls before freeing the document. - */ - if ((style->doc != NULL) && - XSLT_HAS_INTERNAL_NSMAP(style)) - { - xsltRestoreDocumentNamespaces(XSLT_GET_INTERNAL_NSMAP(style), - style->doc); - } -#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ -#else - /* - * Start with a cleanup of the main stylesheet's doc. - */ - if ((style->parent == NULL) && (style->doc)) - xsltCleanupStylesheetTree(style->doc, - xmlDocGetRootElement(style->doc)); -#endif /* XSLT_REFACTORED */ - - xsltFreeKeys(style); - xsltFreeExts(style); - xsltFreeTemplateHashes(style); - xsltFreeDecimalFormatList(style); - xsltFreeTemplateList(style->templates); - xsltFreeAttributeSetsHashes(style); - xsltFreeNamespaceAliasHashes(style); - xsltFreeStylePreComps(style); - /* - * Free documents of all included stylsheet modules of this - * stylesheet level. - */ - xsltFreeStyleDocuments(style); - /* - * TODO: Best time to shutdown extension stuff? - */ - xsltShutdownExts(style); - - if (style->variables != NULL) - xsltFreeStackElemList(style->variables); - if (style->cdataSection != NULL) - xmlHashFree(style->cdataSection, NULL); - if (style->stripSpaces != NULL) - xmlHashFree(style->stripSpaces, NULL); - if (style->nsHash != NULL) - xmlHashFree(style->nsHash, NULL); - if (style->exclPrefixTab != NULL) - xmlFree(style->exclPrefixTab); - if (style->method != NULL) - xmlFree(style->method); - if (style->methodURI != NULL) - xmlFree(style->methodURI); - if (style->version != NULL) - xmlFree(style->version); - if (style->encoding != NULL) - xmlFree(style->encoding); - if (style->doctypePublic != NULL) - xmlFree(style->doctypePublic); - if (style->doctypeSystem != NULL) - xmlFree(style->doctypeSystem); - if (style->mediaType != NULL) - xmlFree(style->mediaType); - if (style->attVTs) - xsltFreeAVTList(style->attVTs); - if (style->imports != NULL) - xsltFreeStylesheetList(style->imports); - -#ifdef XSLT_REFACTORED - /* - * If this is the principal stylesheet, then - * free its internal data. - */ - if (style->principal == style) { - if (style->principalData) { - xsltFreePrincipalStylesheetData(style->principalData); - style->principalData = NULL; - } - } -#endif - /* - * Better to free the main document of this stylesheet level - * at the end - so here. - */ - if (style->doc != NULL) { - xmlFreeDoc(style->doc); - } - -#ifdef WITH_XSLT_DEBUG - xsltGenericDebug(xsltGenericDebugContext, - "freeing dictionary from stylesheet\n"); -#endif - xmlDictFree(style->dict); - - memset(style, -1, sizeof(xsltStylesheet)); - xmlFree(style); -} - -/************************************************************************ - * * - * Parsing of an XSLT Stylesheet * - * * - ************************************************************************/ - -#ifdef XSLT_REFACTORED - /* - * This is now performed in an optimized way in xsltParseXSLTTemplate. - */ -#else -/** - * xsltGetInheritedNsList: - * @style: the stylesheet - * @template: the template - * @node: the current node - * - * Search all the namespace applying to a given element except the ones - * from excluded output prefixes currently in scope. Initialize the - * template inheritedNs list with it. - * - * Returns the number of entries found - */ -static int -xsltGetInheritedNsList(xsltStylesheetPtr style, - xsltTemplatePtr template, - xmlNodePtr node) -{ - xmlNsPtr cur; - xmlNsPtr *ret = NULL; - int nbns = 0; - int maxns = 10; - int i; - - /* - * TODO: This will gather the ns-decls of elements even if - * outside xsl:stylesheet. Example: - * - * - * Will have foo="urn:test:foo" in the list. - * Is this OK? - */ - - if ((style == NULL) || (template == NULL) || (node == NULL) || - (template->inheritedNsNr != 0) || (template->inheritedNs != NULL)) - return(0); - while (node != NULL) { - if (node->type == XML_ELEMENT_NODE) { - cur = node->nsDef; - while (cur != NULL) { - if (xmlStrEqual(cur->href, XSLT_NAMESPACE)) - goto skip_ns; - - if ((cur->prefix != NULL) && - (xsltCheckExtPrefix(style, cur->prefix))) - goto skip_ns; - /* - * Check if this namespace was excluded. - * Note that at this point only the exclusions defined - * on the topmost stylesheet element are in the exclusion-list. - */ - for (i = 0;i < style->exclPrefixNr;i++) { - if (xmlStrEqual(cur->href, style->exclPrefixTab[i])) - goto skip_ns; - } - if (ret == NULL) { - ret = - (xmlNsPtr *) xmlMalloc((maxns + 1) * - sizeof(xmlNsPtr)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xsltGetInheritedNsList : out of memory!\n"); - return(0); - } - ret[nbns] = NULL; - } - /* - * Skip shadowed namespace bindings. - */ - for (i = 0; i < nbns; i++) { - if ((cur->prefix == ret[i]->prefix) || - (xmlStrEqual(cur->prefix, ret[i]->prefix))) - break; - } - if (i >= nbns) { - if (nbns >= maxns) { - maxns *= 2; - ret = (xmlNsPtr *) xmlRealloc(ret, - (maxns + - 1) * - sizeof(xmlNsPtr)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xsltGetInheritedNsList : realloc failed!\n"); - return(0); - } - } - ret[nbns++] = cur; - ret[nbns] = NULL; - } -skip_ns: - cur = cur->next; - } - } - node = node->parent; - } - if (nbns != 0) { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "template has %d inherited namespaces\n", nbns); -#endif - template->inheritedNsNr = nbns; - template->inheritedNs = ret; - } - return (nbns); -} -#endif /* else of XSLT_REFACTORED */ - -/** - * xsltParseStylesheetOutput: - * @style: the XSLT stylesheet - * @cur: the "output" element - * - * parse an XSLT stylesheet output element and record - * information related to the stylesheet output - */ - -void -xsltParseStylesheetOutput(xsltStylesheetPtr style, xmlNodePtr cur) -{ - xmlChar *elements, - *prop; - xmlChar *element, - *end; - - if ((cur == NULL) || (style == NULL)) - return; - - prop = xmlGetNsProp(cur, (const xmlChar *) "version", NULL); - if (prop != NULL) { - if (style->version != NULL) - xmlFree(style->version); - style->version = prop; - } - - prop = xmlGetNsProp(cur, (const xmlChar *) "encoding", NULL); - if (prop != NULL) { - if (style->encoding != NULL) - xmlFree(style->encoding); - style->encoding = prop; - } - - /* relaxed to support xt:document - * TODO KB: What does "relaxed to support xt:document" mean? - */ - prop = xmlGetNsProp(cur, (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; - - /* - * TODO: Don't use xsltGetQNameURI(). - */ - URI = xsltGetQNameURI(cur, &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(NULL, style, cur, - "invalid value for method: %s\n", prop); - if (style != NULL) style->warnings++; - } - } else { - style->method = prop; - style->methodURI = xmlStrdup(URI); - } - } - - prop = xmlGetNsProp(cur, (const xmlChar *) "doctype-system", NULL); - if (prop != NULL) { - if (style->doctypeSystem != NULL) - xmlFree(style->doctypeSystem); - style->doctypeSystem = prop; - } - - prop = xmlGetNsProp(cur, (const xmlChar *) "doctype-public", NULL); - if (prop != NULL) { - if (style->doctypePublic != NULL) - xmlFree(style->doctypePublic); - style->doctypePublic = prop; - } - - prop = xmlGetNsProp(cur, (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(NULL, style, cur, - "invalid value for standalone: %s\n", prop); - style->errors++; - } - xmlFree(prop); - } - - prop = xmlGetNsProp(cur, (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(NULL, style, cur, - "invalid value for indent: %s\n", prop); - style->errors++; - } - xmlFree(prop); - } - - prop = xmlGetNsProp(cur, (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(NULL, style, cur, - "invalid value for omit-xml-declaration: %s\n", - prop); - style->errors++; - } - xmlFree(prop); - } - - elements = xmlGetNsProp(cur, (const xmlChar *) "cdata-section-elements", - NULL); - if (elements != NULL) { - if (style->cdataSection == NULL) - style->cdataSection = xmlHashCreate(10); - if (style->cdataSection == NULL) - return; - - element = elements; - while (*element != 0) { - while (IS_BLANK(*element)) - element++; - if (*element == 0) - break; - end = element; - while ((*end != 0) && (!IS_BLANK(*end))) - end++; - element = xmlStrndup(element, end - element); - if (element) { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "add cdata section output element %s\n", - element); -#endif - if (xmlValidateQName(BAD_CAST element, 0) != 0) { - xsltTransformError(NULL, style, cur, - "Attribute 'cdata-section-elements': The value " - "'%s' is not a valid QName.\n", element); - xmlFree(element); - style->errors++; - } else { - const xmlChar *URI; - - /* - * TODO: Don't use xsltGetQNameURI(). - */ - URI = xsltGetQNameURI(cur, &element); - if (element == NULL) { - /* - * TODO: We'll report additionally an error - * via the stylesheet's error handling. - */ - xsltTransformError(NULL, style, cur, - "Attribute 'cdata-section-elements': The value " - "'%s' is not a valid QName.\n", element); - style->errors++; - } else { - xmlNsPtr ns; - - /* - * XSLT-1.0 "Each QName is expanded into an - * expanded-name using the namespace declarations in - * effect on the xsl:output element in which the QName - * occurs; if there is a default namespace, it is used - * for QNames that do not have a prefix" - * NOTE: Fix of bug #339570. - */ - if (URI == NULL) { - ns = xmlSearchNs(style->doc, cur, NULL); - if (ns != NULL) - URI = ns->href; - } - xmlHashAddEntry2(style->cdataSection, element, URI, - (void *) "cdata"); - xmlFree(element); - } - } - } - element = end; - } - xmlFree(elements); - } - - prop = xmlGetNsProp(cur, (const xmlChar *) "media-type", NULL); - if (prop != NULL) { - if (style->mediaType) - xmlFree(style->mediaType); - style->mediaType = prop; - } - if (cur->children != NULL) { - xsltParseContentError(style, cur->children); - } -} - -/** - * xsltParseStylesheetDecimalFormat: - * @style: the XSLT stylesheet - * @cur: the "decimal-format" element - * - * - * - * - * parse an XSLT stylesheet decimal-format element and - * and record the formatting characteristics - */ -static void -xsltParseStylesheetDecimalFormat(xsltStylesheetPtr style, xmlNodePtr cur) -{ - xmlChar *prop; - xsltDecimalFormatPtr format; - xsltDecimalFormatPtr iter; - - if ((cur == NULL) || (style == NULL)) - return; - - format = style->decimalFormat; - - prop = xmlGetNsProp(cur, BAD_CAST("name"), NULL); - if (prop != NULL) { - format = xsltDecimalFormatGetByName(style, prop); - if (format != NULL) { - xsltTransformError(NULL, style, cur, - "xsltParseStylestyleDecimalFormat: %s already exists\n", prop); - if (style != NULL) style->warnings++; - return; - } - format = xsltNewDecimalFormat(prop); - if (format == NULL) { - xsltTransformError(NULL, style, cur, - "xsltParseStylestyleDecimalFormat: failed creating new decimal-format\n"); - if (style != NULL) style->errors++; - return; - } - /* Append new decimal-format structure */ - for (iter = style->decimalFormat; iter->next; iter = iter->next) - ; - if (iter) - iter->next = format; - } - - prop = xmlGetNsProp(cur, (const xmlChar *)"decimal-separator", NULL); - if (prop != NULL) { - if (format->decimalPoint != NULL) xmlFree(format->decimalPoint); - format->decimalPoint = prop; - } - - prop = xmlGetNsProp(cur, (const xmlChar *)"grouping-separator", NULL); - if (prop != NULL) { - if (format->grouping != NULL) xmlFree(format->grouping); - format->grouping = prop; - } - - prop = xmlGetNsProp(cur, (const xmlChar *)"infinity", NULL); - if (prop != NULL) { - if (format->infinity != NULL) xmlFree(format->infinity); - format->infinity = prop; - } - - prop = xmlGetNsProp(cur, (const xmlChar *)"minus-sign", NULL); - if (prop != NULL) { - if (format->minusSign != NULL) xmlFree(format->minusSign); - format->minusSign = prop; - } - - prop = xmlGetNsProp(cur, (const xmlChar *)"NaN", NULL); - if (prop != NULL) { - if (format->noNumber != NULL) xmlFree(format->noNumber); - format->noNumber = prop; - } - - prop = xmlGetNsProp(cur, (const xmlChar *)"percent", NULL); - if (prop != NULL) { - if (format->percent != NULL) xmlFree(format->percent); - format->percent = prop; - } - - prop = xmlGetNsProp(cur, (const xmlChar *)"per-mille", NULL); - if (prop != NULL) { - if (format->permille != NULL) xmlFree(format->permille); - format->permille = prop; - } - - prop = xmlGetNsProp(cur, (const xmlChar *)"zero-digit", NULL); - if (prop != NULL) { - if (format->zeroDigit != NULL) xmlFree(format->zeroDigit); - format->zeroDigit = prop; - } - - prop = xmlGetNsProp(cur, (const xmlChar *)"digit", NULL); - if (prop != NULL) { - if (format->digit != NULL) xmlFree(format->digit); - format->digit = prop; - } - - prop = xmlGetNsProp(cur, (const xmlChar *)"pattern-separator", NULL); - if (prop != NULL) { - if (format->patternSeparator != NULL) xmlFree(format->patternSeparator); - format->patternSeparator = prop; - } - if (cur->children != NULL) { - xsltParseContentError(style, cur->children); - } -} - -/** - * xsltParseStylesheetPreserveSpace: - * @style: the XSLT stylesheet - * @cur: the "preserve-space" element - * - * parse an XSLT stylesheet preserve-space element and record - * elements needing preserving - */ - -static void -xsltParseStylesheetPreserveSpace(xsltStylesheetPtr style, xmlNodePtr cur) { - xmlChar *elements; - xmlChar *element, *end; - - if ((cur == NULL) || (style == NULL)) - return; - - elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL); - if (elements == NULL) { - xsltTransformError(NULL, style, cur, - "xsltParseStylesheetPreserveSpace: missing elements attribute\n"); - if (style != NULL) style->warnings++; - return; - } - - if (style->stripSpaces == NULL) - style->stripSpaces = xmlHashCreate(10); - if (style->stripSpaces == NULL) - return; - - element = elements; - while (*element != 0) { - while (IS_BLANK(*element)) element++; - if (*element == 0) - break; - end = element; - while ((*end != 0) && (!IS_BLANK(*end))) end++; - element = xmlStrndup(element, end - element); - if (element) { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "add preserved space element %s\n", element); -#endif - if (xmlStrEqual(element, (const xmlChar *)"*")) { - style->stripAll = -1; - } else { - const xmlChar *URI; - - /* - * TODO: Don't use xsltGetQNameURI(). - */ - URI = xsltGetQNameURI(cur, &element); - - xmlHashAddEntry2(style->stripSpaces, element, URI, - (xmlChar *) "preserve"); - } - xmlFree(element); - } - element = end; - } - xmlFree(elements); - if (cur->children != NULL) { - xsltParseContentError(style, cur->children); - } -} - -#ifdef XSLT_REFACTORED -#else -/** - * xsltParseStylesheetExtPrefix: - * @style: the XSLT stylesheet - * @template: the "extension-element-prefixes" prefix - * - * parse an XSLT stylesheet's "extension-element-prefix" attribute value - * and register the namespaces of extension instruction. - * SPEC "A namespace is designated as an extension namespace by using - * an extension-element-prefixes attribute on: - * 1) an xsl:stylesheet element - * 2) an xsl:extension-element-prefixes attribute on a - * literal result element - * 3) an extension instruction." - */ -static void -xsltParseStylesheetExtPrefix(xsltStylesheetPtr style, xmlNodePtr cur, - int isXsltElem) { - xmlChar *prefixes; - xmlChar *prefix, *end; - - if ((cur == NULL) || (style == NULL)) - return; - - if (isXsltElem) { - /* For xsl:stylesheet/xsl:transform. */ - prefixes = xmlGetNsProp(cur, - (const xmlChar *)"extension-element-prefixes", NULL); - } else { - /* For literal result elements and extension instructions. */ - prefixes = xmlGetNsProp(cur, - (const xmlChar *)"extension-element-prefixes", XSLT_NAMESPACE); - } - if (prefixes == NULL) { - return; - } - - prefix = prefixes; - while (*prefix != 0) { - while (IS_BLANK(*prefix)) prefix++; - if (*prefix == 0) - break; - end = prefix; - while ((*end != 0) && (!IS_BLANK(*end))) end++; - prefix = xmlStrndup(prefix, end - prefix); - if (prefix) { - xmlNsPtr ns; - - if (xmlStrEqual(prefix, (const xmlChar *)"#default")) - ns = xmlSearchNs(style->doc, cur, NULL); - else - ns = xmlSearchNs(style->doc, cur, prefix); - if (ns == NULL) { - xsltTransformError(NULL, style, cur, - "xsl:extension-element-prefix : undefined namespace %s\n", - prefix); - if (style != NULL) style->warnings++; - } else { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "add extension prefix %s\n", prefix); -#endif - xsltRegisterExtPrefix(style, prefix, ns->href); - } - xmlFree(prefix); - } - prefix = end; - } - xmlFree(prefixes); -} -#endif /* else of XSLT_REFACTORED */ - -/** - * xsltParseStylesheetStripSpace: - * @style: the XSLT stylesheet - * @cur: the "strip-space" element - * - * parse an XSLT stylesheet's strip-space element and record - * the elements needing stripping - */ - -static void -xsltParseStylesheetStripSpace(xsltStylesheetPtr style, xmlNodePtr cur) { - xmlChar *elements; - xmlChar *element, *end; - - if ((cur == NULL) || (style == NULL)) - return; - - elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL); - if (elements == NULL) { - xsltTransformError(NULL, style, cur, - "xsltParseStylesheetStripSpace: missing elements attribute\n"); - if (style != NULL) style->warnings++; - return; - } - - if (style->stripSpaces == NULL) - style->stripSpaces = xmlHashCreate(10); - if (style->stripSpaces == NULL) - return; - - element = elements; - while (*element != 0) { - while (IS_BLANK(*element)) element++; - if (*element == 0) - break; - end = element; - while ((*end != 0) && (!IS_BLANK(*end))) end++; - element = xmlStrndup(element, end - element); - if (element) { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "add stripped space element %s\n", element); -#endif - if (xmlStrEqual(element, (const xmlChar *)"*")) { - style->stripAll = 1; - } else { - const xmlChar *URI; - - /* - * TODO: Don't use xsltGetQNameURI(). - */ - URI = xsltGetQNameURI(cur, &element); - - xmlHashAddEntry2(style->stripSpaces, element, URI, - (xmlChar *) "strip"); - } - xmlFree(element); - } - element = end; - } - xmlFree(elements); - if (cur->children != NULL) { - xsltParseContentError(style, cur->children); - } -} - -#ifdef XSLT_REFACTORED -#else -/** - * xsltParseStylesheetExcludePrefix: - * @style: the XSLT stylesheet - * @cur: the current point in the stylesheet - * - * parse an XSLT stylesheet exclude prefix and record - * namespaces needing stripping - * - * Returns the number of Excluded prefixes added at that level - */ - -static int -xsltParseStylesheetExcludePrefix(xsltStylesheetPtr style, xmlNodePtr cur, - int isXsltElem) -{ - int nb = 0; - xmlChar *prefixes; - xmlChar *prefix, *end; - - if ((cur == NULL) || (style == NULL)) - return(0); - - if (isXsltElem) - prefixes = xmlGetNsProp(cur, - (const xmlChar *)"exclude-result-prefixes", NULL); - else - prefixes = xmlGetNsProp(cur, - (const xmlChar *)"exclude-result-prefixes", XSLT_NAMESPACE); - - if (prefixes == NULL) { - return(0); - } - - prefix = prefixes; - while (*prefix != 0) { - while (IS_BLANK(*prefix)) prefix++; - if (*prefix == 0) - break; - end = prefix; - while ((*end != 0) && (!IS_BLANK(*end))) end++; - prefix = xmlStrndup(prefix, end - prefix); - if (prefix) { - xmlNsPtr ns; - - if (xmlStrEqual(prefix, (const xmlChar *)"#default")) - ns = xmlSearchNs(style->doc, cur, NULL); - else - ns = xmlSearchNs(style->doc, cur, prefix); - if (ns == NULL) { - xsltTransformError(NULL, style, cur, - "xsl:exclude-result-prefixes : undefined namespace %s\n", - prefix); - if (style != NULL) style->warnings++; - } else { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "exclude result prefix %s\n", prefix); -#endif - exclPrefixPush(style, (xmlChar *) ns->href); - nb++; - } - xmlFree(prefix); - } - prefix = end; - } - xmlFree(prefixes); - return(nb); -} -#endif /* else of XSLT_REFACTORED */ - -#ifdef XSLT_REFACTORED - -/* -* xsltTreeEnsureXMLDecl: -* @doc: the doc -* -* BIG NOTE: -* This was copy&pasted from Libxml2's xmlTreeEnsureXMLDecl() in "tree.c". -* Ensures that there is an XML namespace declaration on the doc. -* -* Returns the XML ns-struct or NULL on API and internal errors. -*/ -static xmlNsPtr -xsltTreeEnsureXMLDecl(xmlDocPtr doc) -{ - if (doc == NULL) - return (NULL); - if (doc->oldNs != NULL) - return (doc->oldNs); - { - xmlNsPtr ns; - ns = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); - if (ns == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xsltTreeEnsureXMLDecl: Failed to allocate " - "the XML namespace.\n"); - return (NULL); - } - memset(ns, 0, sizeof(xmlNs)); - ns->type = XML_LOCAL_NAMESPACE; - /* - * URGENT TODO: revisit this. - */ -#ifdef LIBXML_NAMESPACE_DICT - if (doc->dict) - ns->href = xmlDictLookup(doc->dict, XML_XML_NAMESPACE, -1); - else - ns->href = xmlStrdup(XML_XML_NAMESPACE); -#else - ns->href = xmlStrdup(XML_XML_NAMESPACE); -#endif - ns->prefix = xmlStrdup((const xmlChar *)"xml"); - doc->oldNs = ns; - return (ns); - } -} - -/* -* xsltTreeAcquireStoredNs: -* @doc: the doc -* @nsName: the namespace name -* @prefix: the prefix -* -* BIG NOTE: -* This was copy&pasted from Libxml2's xmlDOMWrapStoreNs() in "tree.c". -* Creates or reuses an xmlNs struct on doc->oldNs with -* the given prefix and namespace name. -* -* Returns the aquired ns struct or NULL in case of an API -* or internal error. -*/ -static xmlNsPtr -xsltTreeAcquireStoredNs(xmlDocPtr doc, - const xmlChar *nsName, - const xmlChar *prefix) -{ - xmlNsPtr ns; - - if (doc == NULL) - return (NULL); - if (doc->oldNs != NULL) - ns = doc->oldNs; - else - ns = xsltTreeEnsureXMLDecl(doc); - if (ns == NULL) - return (NULL); - if (ns->next != NULL) { - /* Reuse. */ - ns = ns->next; - while (ns != NULL) { - if ((ns->prefix == NULL) != (prefix == NULL)) { - /* NOP */ - } else if (prefix == NULL) { - if (xmlStrEqual(ns->href, nsName)) - return (ns); - } else { - if ((ns->prefix[0] == prefix[0]) && - xmlStrEqual(ns->prefix, prefix) && - xmlStrEqual(ns->href, nsName)) - return (ns); - - } - if (ns->next == NULL) - break; - ns = ns->next; - } - } - /* Create. */ - ns->next = xmlNewNs(NULL, nsName, prefix); - return (ns->next); -} - -/** - * xsltLREBuildEffectiveNs: - * - * Apply ns-aliasing on the namespace of the given @elem and - * its attributes. - */ -static int -xsltLREBuildEffectiveNs(xsltCompilerCtxtPtr cctxt, - xmlNodePtr elem) -{ - xmlNsPtr ns; - xsltNsAliasPtr alias; - - if ((cctxt == NULL) || (elem == NULL)) - return(-1); - if ((cctxt->nsAliases == NULL) || (! cctxt->hasNsAliases)) - return(0); - - alias = cctxt->nsAliases; - while (alias != NULL) { - if ( /* If both namespaces are NULL... */ - ( (elem->ns == NULL) && - ((alias->literalNs == NULL) || - (alias->literalNs->href == NULL)) ) || - /* ... or both namespace are equal */ - ( (elem->ns != NULL) && - (alias->literalNs != NULL) && - xmlStrEqual(elem->ns->href, alias->literalNs->href) ) ) - { - if ((alias->targetNs != NULL) && - (alias->targetNs->href != NULL)) - { - /* - * Convert namespace. - */ - if (elem->doc == alias->docOfTargetNs) { - /* - * This is the nice case: same docs. - * This will eventually assign a ns-decl which - * is shadowed, but this has no negative effect on - * the generation of the result tree. - */ - elem->ns = alias->targetNs; - } else { - /* - * This target xmlNs originates from a different - * stylesheet tree. Try to locate it in the - * in-scope namespaces. - * OPTIMIZE TODO: Use the compiler-node-info inScopeNs. - */ - ns = xmlSearchNs(elem->doc, elem, - alias->targetNs->prefix); - /* - * If no matching ns-decl found, then assign a - * ns-decl stored in xmlDoc. - */ - if ((ns == NULL) || - (! xmlStrEqual(ns->href, alias->targetNs->href))) - { - /* - * BIG NOTE: The use of xsltTreeAcquireStoredNs() - * is not very efficient, but currently I don't - * see an other way of *safely* changing a node's - * namespace, since the xmlNs struct in - * alias->targetNs might come from an other - * stylesheet tree. So we need to anchor it in the - * current document, without adding it to the tree, - * which would otherwise change the in-scope-ns - * semantic of the tree. - */ - ns = xsltTreeAcquireStoredNs(elem->doc, - alias->targetNs->href, - alias->targetNs->prefix); - - if (ns == NULL) { - xsltTransformError(NULL, cctxt->style, elem, - "Internal error in " - "xsltLREBuildEffectiveNs(): " - "failed to acquire a stored " - "ns-declaration.\n"); - cctxt->style->errors++; - return(-1); - - } - } - elem->ns = ns; - } - } else { - /* - * Move into or leave in the NULL namespace. - */ - elem->ns = NULL; - } - break; - } - alias = alias->next; - } - /* - * Same with attributes of literal result elements. - */ - if (elem->properties != NULL) { - xmlAttrPtr attr = elem->properties; - - while (attr != NULL) { - if (attr->ns == NULL) { - attr = attr->next; - continue; - } - alias = cctxt->nsAliases; - while (alias != NULL) { - if ( /* If both namespaces are NULL... */ - ( (elem->ns == NULL) && - ((alias->literalNs == NULL) || - (alias->literalNs->href == NULL)) ) || - /* ... or both namespace are equal */ - ( (elem->ns != NULL) && - (alias->literalNs != NULL) && - xmlStrEqual(elem->ns->href, alias->literalNs->href) ) ) - { - if ((alias->targetNs != NULL) && - (alias->targetNs->href != NULL)) - { - if (elem->doc == alias->docOfTargetNs) { - elem->ns = alias->targetNs; - } else { - ns = xmlSearchNs(elem->doc, elem, - alias->targetNs->prefix); - if ((ns == NULL) || - (! xmlStrEqual(ns->href, alias->targetNs->href))) - { - ns = xsltTreeAcquireStoredNs(elem->doc, - alias->targetNs->href, - alias->targetNs->prefix); - - if (ns == NULL) { - xsltTransformError(NULL, cctxt->style, elem, - "Internal error in " - "xsltLREBuildEffectiveNs(): " - "failed to acquire a stored " - "ns-declaration.\n"); - cctxt->style->errors++; - return(-1); - - } - } - elem->ns = ns; - } - } else { - /* - * Move into or leave in the NULL namespace. - */ - elem->ns = NULL; - } - break; - } - alias = alias->next; - } - - attr = attr->next; - } - } - return(0); -} - -/** - * xsltLREBuildEffectiveNsNodes: - * - * Computes the effective namespaces nodes for a literal result - * element. - * @effectiveNs is the set of effective ns-nodes - * on the literal result element, which will be added to the result - * element if not already existing in the result tree. - * This means that excluded namespaces (via exclude-result-prefixes, - * extension-element-prefixes and the XSLT namespace) not added - * to the set. - * Namespace-aliasing was applied on the @effectiveNs. - */ -static int -xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt, - xsltStyleItemLRElementInfoPtr item, - xmlNodePtr elem, - int isLRE) -{ - xmlNsPtr ns, tmpns; - xsltEffectiveNsPtr effNs, lastEffNs = NULL; - int i, j, holdByElem; - xsltPointerListPtr extElemNs = cctxt->inode->extElemNs; - xsltPointerListPtr exclResultNs = cctxt->inode->exclResultNs; - - if ((cctxt == NULL) || (cctxt->inode == NULL) || (elem == NULL) || - (item == NULL) || (item->effectiveNs != NULL)) - return(-1); - - if (item->inScopeNs == NULL) - return(0); - - extElemNs = cctxt->inode->extElemNs; - exclResultNs = cctxt->inode->exclResultNs; - - for (i = 0; i < item->inScopeNs->totalNumber; i++) { - ns = item->inScopeNs->list[i]; - /* - * Skip namespaces designated as excluded namespaces - * ------------------------------------------------- - * - * XSLT-20 TODO: In XSLT 2.0 we need to keep namespaces - * which are target namespaces of namespace-aliases - * regardless if designated as excluded. - * - * Exclude the XSLT namespace. - */ - if (xmlStrEqual(ns->href, XSLT_NAMESPACE)) - goto skip_ns; - - /* - * Exclude excluded result namespaces. - */ - if (exclResultNs) { - for (j = 0; j < exclResultNs->number; j++) - if (xmlStrEqual(ns->href, BAD_CAST exclResultNs->items[j])) - goto skip_ns; - } - /* - * Exclude extension-element namespaces. - */ - if (extElemNs) { - for (j = 0; j < extElemNs->number; j++) - if (xmlStrEqual(ns->href, BAD_CAST extElemNs->items[j])) - goto skip_ns; - } - /* - * OPTIMIZE TODO: This information may not be needed. - */ - if (isLRE && (elem->nsDef != NULL)) { - holdByElem = 0; - tmpns = elem->nsDef; - do { - if (tmpns == ns) { - holdByElem = 1; - break; - } - tmpns = tmpns->next; - } while (tmpns != NULL); - } else - holdByElem = 0; - /* - * Apply namespace aliasing - * ------------------------ - * - * NOTE: The ns-aliasing machanism is non-cascading. - * (checked with Saxon, Xalan and MSXML .NET). - * URGENT TODO: is style->nsAliases the effective list of - * ns-aliases, or do we need to lookup the whole - * import-tree? - * TODO: Get rid of import-tree lookup. - */ - if (cctxt->hasNsAliases) { - xsltNsAliasPtr alias = cctxt->nsAliases; - do { - /* - * TODO: What to do with xmlns="" ? - */ - if ((alias->literalNs != NULL) && - (xmlStrEqual(alias->literalNs->href, ns->href))) - { - /* - * Recognized as an namespace alias; convert it to - * the target namespace. - */ - if (alias->targetNs != NULL) - ns = alias->literalNs; - else { - /* - * The target is the NULL namespace. - */ - goto skip_ns; - } - break; - } - alias = alias->next; - } while (alias != NULL); - } - - /* - * Add the effective namespace declaration. - */ - effNs = (xsltEffectiveNsPtr) xmlMalloc(sizeof(xsltEffectiveNs)); - if (effNs == NULL) { - xsltTransformError(NULL, cctxt->style, elem, - "Internal error in xsltLREBuildEffectiveNs(): " - "failed to allocate memory.\n"); - cctxt->style->errors++; - return(-1); - } - if (cctxt->psData->effectiveNs == NULL) { - cctxt->psData->effectiveNs = effNs; - effNs->nextInStore = NULL; - } else { - effNs->nextInStore = cctxt->psData->effectiveNs; - cctxt->psData->effectiveNs = effNs; - } - - effNs->next = NULL; - effNs->prefix = ns->prefix; - effNs->nsName = ns->href; - effNs->holdByElem = holdByElem; - - if (lastEffNs == NULL) - item->effectiveNs = effNs; - else - lastEffNs->next = effNs; - lastEffNs = effNs; - -skip_ns: - {} - } - return(0); -} - - -/** - * xsltLREInfoCreate: - * - * @isLRE: indicates if the given @elem is a literal result element - * - * Creates a new info for a literal result element. - */ -static int -xsltLREInfoCreate(xsltCompilerCtxtPtr cctxt, - xmlNodePtr elem, - int isLRE) -{ - xsltStyleItemLRElementInfoPtr item; - - if ((cctxt == NULL) || (cctxt->inode == NULL)) - return(-1); - - item = (xsltStyleItemLRElementInfoPtr) - xmlMalloc(sizeof(xsltStyleItemLRElementInfo)); - if (item == NULL) { - xsltTransformError(NULL, cctxt->style, NULL, - "Internal error in xsltLREInfoCreate(): " - "memory allocation failed.\n"); - cctxt->style->errors++; - return(-1); - } - memset(item, 0, sizeof(xsltStyleItemLRElementInfo)); - item->type = XSLT_FUNC_LITERAL_RESULT_ELEMENT; - /* - * Store it in the stylesheet. - */ - item->next = cctxt->style->preComps; - cctxt->style->preComps = (xsltElemPreCompPtr) item; - /* - * @inScopeNs are used for execution of XPath expressions - * in AVTs. - */ - item->inScopeNs = cctxt->inode->inScopeNs; - - if (elem) - xsltLREBuildEffectiveNsNodes(cctxt, item, elem, isLRE); - - cctxt->inode->litResElemInfo = item; - cctxt->inode->nsChanged = 0; - cctxt->maxLREs++; - return(0); -} - -/* -* xsltCompilerNodePush: -* -* @cctxt: the compilation context -* @node: the node to be pushed (this can also be the doc-node) -* -* Returns the current node info structure or -* NULL in case of an internal error. -*/ -xsltCompilerNodeInfoPtr -xsltCompilerNodePush(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) -{ - xsltCompilerNodeInfoPtr inode, iprev; - - if ((cctxt->inode != NULL) && (cctxt->inode->next != NULL)) { - inode = cctxt->inode->next; - } else if ((cctxt->inode == NULL) && (cctxt->inodeList != NULL)) { - inode = cctxt->inodeList; - } else { - /* - * Create a new node-info. - */ - inode = (xsltCompilerNodeInfoPtr) - xmlMalloc(sizeof(xsltCompilerNodeInfo)); - if (inode == NULL) { - xsltTransformError(NULL, cctxt->style, NULL, - "xsltCompilerNodePush: malloc failed.\n"); - return(NULL); - } - memset(inode, 0, sizeof(xsltCompilerNodeInfo)); - if (cctxt->inodeList == NULL) - cctxt->inodeList = inode; - else { - cctxt->inodeLast->next = inode; - inode->prev = cctxt->inodeLast; - } - cctxt->inodeLast = inode; - cctxt->maxNodeInfos++; - if (cctxt->inode == NULL) { - cctxt->inode = inode; - /* - * Create an initial literal result element info for - * the root of the stylesheet. - */ - xsltLREInfoCreate(cctxt, NULL, 0); - } - } - cctxt->depth++; - cctxt->inode = inode; - /* - * REVISIT TODO: Keep the reset always complete. - * NOTE: Be carefull with the @node, since it might be - * a doc-node. - */ - inode->node = node; - inode->depth = cctxt->depth; - inode->templ = NULL; - inode->category = XSLT_ELEMENT_CATEGORY_XSLT; - inode->type = 0; - inode->item = NULL; - inode->curChildType = 0; - inode->extContentHandled = 0; - inode->isRoot = 0; - - if (inode->prev != NULL) { - iprev = inode->prev; - /* - * Inherit the following information: - * --------------------------------- - * - * In-scope namespaces - */ - inode->inScopeNs = iprev->inScopeNs; - /* - * Info for literal result elements - */ - inode->litResElemInfo = iprev->litResElemInfo; - inode->nsChanged = iprev->nsChanged; - /* - * Excluded result namespaces - */ - inode->exclResultNs = iprev->exclResultNs; - /* - * Extension instruction namespaces - */ - inode->extElemNs = iprev->extElemNs; - /* - * Whitespace preservation - */ - inode->preserveWhitespace = iprev->preserveWhitespace; - /* - * Forwards-compatible mode - */ - inode->forwardsCompat = iprev->forwardsCompat; - } else { - inode->inScopeNs = NULL; - inode->exclResultNs = NULL; - inode->extElemNs = NULL; - inode->preserveWhitespace = 0; - inode->forwardsCompat = 0; - } - - return(inode); -} - -/* -* xsltCompilerNodePop: -* -* @cctxt: the compilation context -* @node: the node to be pushed (this can also be the doc-node) -* -* Pops the current node info. -*/ -static void -xsltCompilerNodePop(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) -{ - if (cctxt->inode == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xsltCompilerNodePop: Top-node mismatch.\n"); - return; - } - /* - * NOTE: Be carefull with the @node, since it might be - * a doc-node. - */ - if (cctxt->inode->node != node) { - xmlGenericError(xmlGenericErrorContext, - "xsltCompilerNodePop: Node mismatch.\n"); - goto mismatch; - } - if (cctxt->inode->depth != cctxt->depth) { - xmlGenericError(xmlGenericErrorContext, - "xsltCompilerNodePop: Depth mismatch.\n"); - goto mismatch; - } - cctxt->depth--; - cctxt->inode = cctxt->inode->prev; - if (cctxt->inode != NULL) - cctxt->inode->curChildType = 0; - return; - -mismatch: - { - const xmlChar *nsName = NULL, *name = NULL; - const xmlChar *infnsName = NULL, *infname = NULL; - - if (node) { - if (node->type == XML_ELEMENT_NODE) { - name = node->name; - if (node->ns != NULL) - nsName = node->ns->href; - else - nsName = BAD_CAST ""; - } else { - name = BAD_CAST "#document"; - nsName = BAD_CAST ""; - } - } else - name = BAD_CAST "Not given"; - - if (cctxt->inode->node) { - if (node->type == XML_ELEMENT_NODE) { - infname = cctxt->inode->node->name; - if (cctxt->inode->node->ns != NULL) - infnsName = cctxt->inode->node->ns->href; - else - infnsName = BAD_CAST ""; - } else { - infname = BAD_CAST "#document"; - infnsName = BAD_CAST ""; - } - } else - infname = BAD_CAST "Not given"; - - - xmlGenericError(xmlGenericErrorContext, - "xsltCompilerNodePop: Given : '%s' URI '%s'\n", - name, nsName); - xmlGenericError(xmlGenericErrorContext, - "xsltCompilerNodePop: Expected: '%s' URI '%s'\n", - infname, infnsName); - } -} - -/* -* xsltCompilerBuildInScopeNsList: -* -* Create and store the list of in-scope namespaces for the given -* node in the stylesheet. If there are no changes in the in-scope -* namespaces then the last ns-info of the ancestor axis will be returned. -* Compilation-time only. -* -* Returns the ns-info or NULL if there are no namespaces in scope. -*/ -static xsltNsListContainerPtr -xsltCompilerBuildInScopeNsList(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) -{ - xsltNsListContainerPtr nsi = NULL; - xmlNsPtr *list = NULL, ns; - int i, maxns = 5; - /* - * Create a new ns-list for this position in the node-tree. - * xmlGetNsList() will return NULL, if there are no ns-decls in the - * tree. Note that the ns-decl for the XML namespace is not added - * to the resulting list; the XPath module handles the XML namespace - * internally. - */ - while (node != NULL) { - if (node->type == XML_ELEMENT_NODE) { - ns = node->nsDef; - while (ns != NULL) { - if (nsi == NULL) { - nsi = (xsltNsListContainerPtr) - xmlMalloc(sizeof(xsltNsListContainer)); - if (nsi == NULL) { - xsltTransformError(NULL, cctxt->style, NULL, - "xsltCompilerBuildInScopeNsList: " - "malloc failed!\n"); - goto internal_err; - } - memset(nsi, 0, sizeof(xsltNsListContainer)); - nsi->list = - (xmlNsPtr *) xmlMalloc(maxns * sizeof(xmlNsPtr)); - if (nsi->list == NULL) { - xsltTransformError(NULL, cctxt->style, NULL, - "xsltCompilerBuildInScopeNsList: " - "malloc failed!\n"); - goto internal_err; - } - nsi->list[0] = NULL; - } - /* - * Skip shadowed namespace bindings. - */ - for (i = 0; i < nsi->totalNumber; i++) { - if ((ns->prefix == nsi->list[i]->prefix) || - (xmlStrEqual(ns->prefix, nsi->list[i]->prefix))) - break; - } - if (i >= nsi->totalNumber) { - if (nsi->totalNumber >= maxns) { - maxns *= 2; - nsi->list = - (xmlNsPtr *) xmlRealloc(nsi->list, - maxns * sizeof(xmlNsPtr)); - if (nsi->list == NULL) { - xsltTransformError(NULL, cctxt->style, NULL, - "xsltCompilerBuildInScopeNsList: " - "realloc failed!\n"); - goto internal_err; - } - } - nsi->list[nsi->totalNumber++] = ns; - nsi->list[nsi->totalNumber] = NULL; - } - - ns = ns->next; - } - } - node = node->parent; - } - if (nsi == NULL) - return(NULL); - /* - * Move the default namespace to last position. - */ - nsi->xpathNumber = nsi->totalNumber; - for (i = 0; i < nsi->totalNumber; i++) { - if (nsi->list[i]->prefix == NULL) { - ns = nsi->list[i]; - nsi->list[i] = nsi->list[nsi->totalNumber-1]; - nsi->list[nsi->totalNumber-1] = ns; - nsi->xpathNumber--; - break; - } - } - /* - * Store the ns-list in the stylesheet. - */ - if (xsltPointerListAddSize( - (xsltPointerListPtr)cctxt->psData->inScopeNamespaces, - (void *) nsi, 5) == -1) - { - xmlFree(nsi); - nsi = NULL; - xsltTransformError(NULL, cctxt->style, NULL, - "xsltCompilerBuildInScopeNsList: failed to add ns-info.\n"); - goto internal_err; - } - /* - * Notify of change in status wrt namespaces. - */ - if (cctxt->inode != NULL) - cctxt->inode->nsChanged = 1; - - return(nsi); - -internal_err: - if (list != NULL) - xmlFree(list); - cctxt->style->errors++; - return(NULL); -} - -static int -xsltParseNsPrefixList(xsltCompilerCtxtPtr cctxt, - xsltPointerListPtr list, - xmlNodePtr node, - const xmlChar *value) -{ - xmlChar *cur, *end; - xmlNsPtr ns; - - if ((cctxt == NULL) || (value == NULL) || (list == NULL)) - return(-1); - - list->number = 0; - - cur = (xmlChar *) value; - while (*cur != 0) { - while (IS_BLANK(*cur)) cur++; - if (*cur == 0) - break; - end = cur; - while ((*end != 0) && (!IS_BLANK(*end))) end++; - cur = xmlStrndup(cur, end - cur); - if (cur == NULL) { - cur = end; - continue; - } - /* - * TODO: Export and use xmlSearchNsByPrefixStrict() - * in Libxml2, tree.c, since xmlSearchNs() is in most - * cases not efficient and in some cases not correct. - * - * XSLT-2 TODO: XSLT 2.0 allows an additional "#all" value. - */ - if ((cur[0] == '#') && - xmlStrEqual(cur, (const xmlChar *)"#default")) - ns = xmlSearchNs(cctxt->style->doc, node, NULL); - else - ns = xmlSearchNs(cctxt->style->doc, node, cur); - - if (ns == NULL) { - /* - * TODO: Better to report the attr-node, otherwise - * the user won't know which attribute was invalid. - */ - xsltTransformError(NULL, cctxt->style, node, - "No namespace binding in scope for prefix '%s'.\n", cur); - /* - * XSLT-1.0: "It is an error if there is no namespace - * bound to the prefix on the element bearing the - * exclude-result-prefixes or xsl:exclude-result-prefixes - * attribute." - */ - cctxt->style->errors++; - } else { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "resolved prefix '%s'\n", cur); -#endif - /* - * Note that we put the namespace name into the dict. - */ - if (xsltPointerListAddSize(list, - (void *) xmlDictLookup(cctxt->style->dict, - ns->href, -1), 5) == -1) - { - xmlFree(cur); - goto internal_err; - } - } - xmlFree(cur); - - cur = end; - } - return(0); - -internal_err: - cctxt->style->errors++; - return(-1); -} - -/** - * xsltCompilerUtilsCreateMergedList: - * @dest: the destination list (optional) - * @first: the first list - * @second: the second list (optional) - * - * Appends the content of @second to @first into @destination. - * If @destination is NULL a new list will be created. - * - * Returns the merged list of items or NULL if there's nothing to merge. - */ -static xsltPointerListPtr -xsltCompilerUtilsCreateMergedList(xsltPointerListPtr first, - xsltPointerListPtr second) -{ - xsltPointerListPtr ret; - size_t num; - - if (first) - num = first->number; - else - num = 0; - if (second) - num += second->number; - if (num == 0) - return(NULL); - ret = xsltPointerListCreate(num); - if (ret == NULL) - return(NULL); - /* - * Copy contents. - */ - if ((first != NULL) && (first->number != 0)) { - memcpy(ret->items, first->items, - first->number * sizeof(void *)); - if ((second != NULL) && (second->number != 0)) - memcpy(ret->items + first->number, second->items, - second->number * sizeof(void *)); - } else if ((second != NULL) && (second->number != 0)) - memcpy(ret->items, (void *) second->items, - second->number * sizeof(void *)); - ret->number = num; - return(ret); -} - -/* -* xsltParseExclResultPrefixes: -* -* Create and store the list of in-scope namespaces for the given -* node in the stylesheet. If there are no changes in the in-scope -* namespaces then the last ns-info of the ancestor axis will be returned. -* Compilation-time only. -* -* Returns the ns-info or NULL if there are no namespaces in scope. -*/ -static xsltPointerListPtr -xsltParseExclResultPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, - xsltPointerListPtr def, - int instrCategory) -{ - xsltPointerListPtr list = NULL; - xmlChar *value; - xmlAttrPtr attr; - - if ((cctxt == NULL) || (node == NULL)) - return(NULL); - - if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT) - attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes", NULL); - else - attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes", - XSLT_NAMESPACE); - if (attr == NULL) - return(def); - - if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) { - /* - * Mark the XSLT attr. - */ - attr->psvi = (void *) xsltXSLTAttrMarker; - } - - if ((attr->children != NULL) && - (attr->children->content != NULL)) - value = attr->children->content; - else { - xsltTransformError(NULL, cctxt->style, node, - "Attribute 'exclude-result-prefixes': Invalid value.\n"); - cctxt->style->errors++; - return(def); - } - - if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node, - BAD_CAST value) != 0) - goto exit; - if (cctxt->tmpList->number == 0) - goto exit; - /* - * Merge the list with the inherited list. - */ - list = xsltCompilerUtilsCreateMergedList(def, cctxt->tmpList); - if (list == NULL) - goto exit; - /* - * Store the list in the stylesheet/compiler context. - */ - if (xsltPointerListAddSize( - cctxt->psData->exclResultNamespaces, list, 5) == -1) - { - xsltPointerListFree(list); - list = NULL; - goto exit; - } - /* - * Notify of change in status wrt namespaces. - */ - if (cctxt->inode != NULL) - cctxt->inode->nsChanged = 1; - -exit: - if (list != NULL) - return(list); - else - return(def); -} - -/* -* xsltParseExtElemPrefixes: -* -* Create and store the list of in-scope namespaces for the given -* node in the stylesheet. If there are no changes in the in-scope -* namespaces then the last ns-info of the ancestor axis will be returned. -* Compilation-time only. -* -* Returns the ns-info or NULL if there are no namespaces in scope. -*/ -static xsltPointerListPtr -xsltParseExtElemPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, - xsltPointerListPtr def, - int instrCategory) -{ - xsltPointerListPtr list = NULL; - xmlAttrPtr attr; - xmlChar *value; - int i; - - if ((cctxt == NULL) || (node == NULL)) - return(NULL); - - if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT) - attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes", NULL); - else - attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes", - XSLT_NAMESPACE); - if (attr == NULL) - return(def); - - if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) { - /* - * Mark the XSLT attr. - */ - attr->psvi = (void *) xsltXSLTAttrMarker; - } - - if ((attr->children != NULL) && - (attr->children->content != NULL)) - value = attr->children->content; - else { - xsltTransformError(NULL, cctxt->style, node, - "Attribute 'extension-element-prefixes': Invalid value.\n"); - cctxt->style->errors++; - return(def); - } - - - if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node, - BAD_CAST value) != 0) - goto exit; - - if (cctxt->tmpList->number == 0) - goto exit; - /* - * REVISIT: Register the extension namespaces. - */ - for (i = 0; i < cctxt->tmpList->number; i++) - xsltRegisterExtPrefix(cctxt->style, NULL, - BAD_CAST cctxt->tmpList->items[i]); - /* - * Merge the list with the inherited list. - */ - list = xsltCompilerUtilsCreateMergedList(def, cctxt->tmpList); - if (list == NULL) - goto exit; - /* - * Store the list in the stylesheet. - */ - if (xsltPointerListAddSize( - cctxt->psData->extElemNamespaces, list, 5) == -1) - { - xsltPointerListFree(list); - list = NULL; - goto exit; - } - /* - * Notify of change in status wrt namespaces. - */ - if (cctxt->inode != NULL) - cctxt->inode->nsChanged = 1; - -exit: - if (list != NULL) - return(list); - else - return(def); -} - -/* -* xsltParseAttrXSLTVersion: -* -* @cctxt: the compilation context -* @node: the element-node -* @isXsltElem: whether this is an XSLT element -* -* Parses the attribute xsl:version. -* -* Returns 1 if there was such an attribute, 0 if not and -* -1 if an internal or API error occured. -*/ -static int -xsltParseAttrXSLTVersion(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, - int instrCategory) -{ - xmlChar *value; - xmlAttrPtr attr; - - if ((cctxt == NULL) || (node == NULL)) - return(-1); - - if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT) - attr = xmlHasNsProp(node, BAD_CAST "version", NULL); - else - attr = xmlHasNsProp(node, BAD_CAST "version", XSLT_NAMESPACE); - - if (attr == NULL) - return(0); - - attr->psvi = (void *) xsltXSLTAttrMarker; - - if ((attr->children != NULL) && - (attr->children->content != NULL)) - value = attr->children->content; - else { - xsltTransformError(NULL, cctxt->style, node, - "Attribute 'version': Invalid value.\n"); - cctxt->style->errors++; - return(1); - } - - if (! xmlStrEqual(value, (const xmlChar *)"1.0")) { - cctxt->inode->forwardsCompat = 1; - /* - * TODO: To what extent do we support the - * forwards-compatible mode? - */ - /* - * Report this only once per compilation episode. - */ - if (! cctxt->hasForwardsCompat) { - cctxt->hasForwardsCompat = 1; - cctxt->errSeverity = XSLT_ERROR_SEVERITY_WARNING; - xsltTransformError(NULL, cctxt->style, node, - "Warning: the attribute xsl:version specifies a value " - "different from '1.0'. Switching to forwards-compatible " - "mode. Only features of XSLT 1.0 are supported by this " - "processor.\n"); - cctxt->style->warnings++; - cctxt->errSeverity = XSLT_ERROR_SEVERITY_ERROR; - } - } else { - cctxt->inode->forwardsCompat = 0; - } - - if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) { - /* - * Set a marker on XSLT attributes. - */ - attr->psvi = (void *) xsltXSLTAttrMarker; - } - return(1); -} - -static int -xsltParsePreprocessStylesheetTree(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) -{ - xmlNodePtr deleteNode, cur, txt, textNode = NULL; - xmlDocPtr doc; - xsltStylesheetPtr style; - int internalize = 0, findSpaceAttr; - int xsltStylesheetElemDepth; - xmlAttrPtr attr; - xmlChar *value; - const xmlChar *name, *nsNameXSLT = NULL; - int strictWhitespace, inXSLText = 0; -#ifdef XSLT_REFACTORED_XSLT_NSCOMP - xsltNsMapPtr nsMapItem; -#endif - - if ((cctxt == NULL) || (cctxt->style == NULL) || - (node == NULL) || (node->type != XML_ELEMENT_NODE)) - return(-1); - - doc = node->doc; - if (doc == NULL) - goto internal_err; - - style = cctxt->style; - if ((style->dict != NULL) && (doc->dict == style->dict)) - internalize = 1; - else - style->internalized = 0; - - /* - * Init value of xml:space. Since this might be an embedded - * stylesheet, this is needed to be performed on the element - * where the stylesheet is rooted at, taking xml:space of - * ancestors into account. - */ - if (! cctxt->simplified) - xsltStylesheetElemDepth = cctxt->depth +1; - else - xsltStylesheetElemDepth = 0; - - if (xmlNodeGetSpacePreserve(node) != 1) - cctxt->inode->preserveWhitespace = 0; - else - cctxt->inode->preserveWhitespace = 1; - - /* - * Eval if we should keep the old incorrect behaviour. - */ - strictWhitespace = (cctxt->strict != 0) ? 1 : 0; - - nsNameXSLT = xsltConstNamespaceNameXSLT; - - deleteNode = NULL; - cur = node; - while (cur != NULL) { - if (deleteNode != NULL) { - -#ifdef WITH_XSLT_DEBUG_BLANKS - xsltGenericDebug(xsltGenericDebugContext, - "xsltParsePreprocessStylesheetTree: removing node\n"); -#endif - xmlUnlinkNode(deleteNode); - xmlFreeNode(deleteNode); - deleteNode = NULL; - } - if (cur->type == XML_ELEMENT_NODE) { - - /* - * Clear the PSVI field. - */ - cur->psvi = NULL; - - xsltCompilerNodePush(cctxt, cur); - - inXSLText = 0; - textNode = NULL; - findSpaceAttr = 1; - cctxt->inode->stripWhitespace = 0; - /* - * TODO: I'd love to use a string pointer comparison here :-/ - */ - if (IS_XSLT_ELEM(cur)) { -#ifdef XSLT_REFACTORED_XSLT_NSCOMP - if (cur->ns->href != nsNameXSLT) { - nsMapItem = xsltNewNamespaceMapItem(cctxt, - doc, cur->ns, cur); - if (nsMapItem == NULL) - goto internal_err; - cur->ns->href = nsNameXSLT; - } -#endif - - if (cur->name == NULL) - goto process_attributes; - /* - * Mark the XSLT element for later recognition. - * TODO: Using the marker is still too dangerous, since if - * the parsing mechanism leaves out an XSLT element, then - * this might hit the transformation-mechanism, which - * will break if it doesn't expect such a marker. - */ - /* cur->psvi = (void *) xsltXSLTElemMarker; */ - - /* - * XSLT 2.0: "Any whitespace text node whose parent is - * one of the following elements is removed from the " - * tree, regardless of any xml:space attributes:..." - * xsl:apply-imports, - * xsl:apply-templates, - * xsl:attribute-set, - * xsl:call-template, - * xsl:choose, - * xsl:stylesheet, xsl:transform. - * XSLT 2.0: xsl:analyze-string, - * xsl:character-map, - * xsl:next-match - * - * TODO: I'd love to use a string pointer comparison here :-/ - */ - name = cur->name; - switch (*name) { - case 't': - if ((name[0] == 't') && (name[1] == 'e') && - (name[2] == 'x') && (name[3] == 't') && - (name[4] == 0)) - { - /* - * Process the xsl:text element. - * ---------------------------- - * Mark it for later recognition. - */ - cur->psvi = (void *) xsltXSLTTextMarker; - /* - * For stylesheets, the set of - * whitespace-preserving element names - * consists of just xsl:text. - */ - findSpaceAttr = 0; - cctxt->inode->preserveWhitespace = 1; - inXSLText = 1; - } - break; - case 'c': - if (xmlStrEqual(name, BAD_CAST "choose") || - xmlStrEqual(name, BAD_CAST "call-template")) - cctxt->inode->stripWhitespace = 1; - break; - case 'a': - if (xmlStrEqual(name, BAD_CAST "apply-templates") || - xmlStrEqual(name, BAD_CAST "apply-imports") || - xmlStrEqual(name, BAD_CAST "attribute-set")) - - cctxt->inode->stripWhitespace = 1; - break; - default: - if (xsltStylesheetElemDepth == cctxt->depth) { - /* - * This is a xsl:stylesheet/xsl:transform. - */ - cctxt->inode->stripWhitespace = 1; - break; - } - - if ((cur->prev != NULL) && - (cur->prev->type == XML_TEXT_NODE)) - { - /* - * XSLT 2.0 : "Any whitespace text node whose - * following-sibling node is an xsl:param or - * xsl:sort element is removed from the tree, - * regardless of any xml:space attributes." - */ - if (((*name == 'p') || (*name == 's')) && - (xmlStrEqual(name, BAD_CAST "param") || - xmlStrEqual(name, BAD_CAST "sort"))) - { - do { - if (IS_BLANK_NODE(cur->prev)) { - txt = cur->prev; - xmlUnlinkNode(txt); - xmlFreeNode(txt); - } else { - /* - * This will result in a content - * error, when hitting the parsing - * functions. - */ - break; - } - } while (cur->prev); - } - } - break; - } - } - -process_attributes: - /* - * Process attributes. - * ------------------ - */ - if (cur->properties != NULL) { - if (cur->children == NULL) - findSpaceAttr = 0; - attr = cur->properties; - do { -#ifdef XSLT_REFACTORED_XSLT_NSCOMP - if ((attr->ns) && (attr->ns->href != nsNameXSLT) && - xmlStrEqual(attr->ns->href, nsNameXSLT)) - { - nsMapItem = xsltNewNamespaceMapItem(cctxt, - doc, attr->ns, cur); - if (nsMapItem == NULL) - goto internal_err; - attr->ns->href = nsNameXSLT; - } -#endif - if (internalize) { - /* - * Internalize the attribute's value; the goal is to - * speed up operations and minimize used space by - * compiled stylesheets. - */ - txt = attr->children; - /* - * NOTE that this assumes only one - * text-node in the attribute's content. - */ - if ((txt != NULL) && (txt->content != NULL) && - (!xmlDictOwns(style->dict, txt->content))) - { - value = (xmlChar *) xmlDictLookup(style->dict, - txt->content, -1); - xmlNodeSetContent(txt, NULL); - txt->content = value; - } - } - /* - * Process xml:space attributes. - * ---------------------------- - */ - if ((findSpaceAttr != 0) && - (attr->ns != NULL) && - (attr->name != NULL) && - (attr->name[0] == 's') && - (attr->ns->prefix != NULL) && - (attr->ns->prefix[0] == 'x') && - (attr->ns->prefix[1] == 'm') && - (attr->ns->prefix[2] == 'l') && - (attr->ns->prefix[3] == 0)) - { - value = xmlGetNsProp(cur, BAD_CAST "space", - XML_XML_NAMESPACE); - if (value != NULL) { - if (xmlStrEqual(value, BAD_CAST "preserve")) { - cctxt->inode->preserveWhitespace = 1; - } else if (xmlStrEqual(value, BAD_CAST "default")) { - cctxt->inode->preserveWhitespace = 0; - } else { - /* Invalid value for xml:space. */ - xsltTransformError(NULL, style, cur, - "Attribute xml:space: Invalid value.\n"); - cctxt->style->warnings++; - } - findSpaceAttr = 0; - xmlFree(value); - } - - } - attr = attr->next; - } while (attr != NULL); - } - /* - * We'll descend into the children of element nodes only. - */ - if (cur->children != NULL) { - cur = cur->children; - continue; - } - } else if ((cur->type == XML_TEXT_NODE) || - (cur->type == XML_CDATA_SECTION_NODE)) - { - /* - * Merge adjacent text/CDATA-section-nodes - * --------------------------------------- - * In order to avoid breaking of existing stylesheets, - * if the old behaviour is wanted (strictWhitespace == 0), - * then we *won't* merge adjacent text-nodes - * (except in xsl:text); this will ensure that whitespace-only - * text nodes are (incorrectly) not stripped in some cases. - * - * Example: : zoo - * Corrent (strict) result: zoo - * Incorrect (old) result : zoo - * - * NOTE that we *will* merge adjacent text-nodes if - * they are in xsl:text. - * Example, the following: - * zoo - * will result in both cases in: - * zoo - */ - cur->type = XML_TEXT_NODE; - if ((strictWhitespace != 0) || (inXSLText != 0)) { - /* - * New behaviour; merge nodes. - */ - if (textNode == NULL) - textNode = cur; - else { - if (cur->content != NULL) - xmlNodeAddContent(textNode, cur->content); - deleteNode = cur; - } - if ((cur->next == NULL) || - (cur->next->type == XML_ELEMENT_NODE)) - goto end_of_text; - else - goto next_sibling; - } else { - /* - * Old behaviour. - */ - if (textNode == NULL) - textNode = cur; - goto end_of_text; - } - } else if ((cur->type == XML_COMMENT_NODE) || - (cur->type == XML_PI_NODE)) - { - /* - * Remove processing instructions and comments. - */ - deleteNode = cur; - if ((cur->next == NULL) || - (cur->next->type == XML_ELEMENT_NODE)) - goto end_of_text; - else - goto next_sibling; - } else { - textNode = NULL; - /* - * Invalid node-type for this data-model. - */ - xsltTransformError(NULL, style, cur, - "Invalid type of node for the XSLT data model.\n"); - cctxt->style->errors++; - goto next_sibling; - } - -end_of_text: - if (textNode) { - value = textNode->content; - /* - * At this point all adjacent text/CDATA-section nodes - * have been merged. - * - * Strip whitespace-only text-nodes. - * (cctxt->inode->stripWhitespace) - */ - if ((value == NULL) || (*value == 0) || - (((cctxt->inode->stripWhitespace) || - (! cctxt->inode->preserveWhitespace)) && - IS_BLANK(*value) && - xsltIsBlank(value))) - { - if (textNode != cur) { - xmlUnlinkNode(textNode); - xmlFreeNode(textNode); - } else - deleteNode = textNode; - textNode = NULL; - goto next_sibling; - } - /* - * Convert CDATA-section nodes to text-nodes. - * TODO: Can this produce problems? - */ - if (textNode->type != XML_TEXT_NODE) { - textNode->type = XML_TEXT_NODE; - textNode->name = xmlStringText; - } - if (internalize && - (textNode->content != NULL) && - (!xmlDictOwns(style->dict, textNode->content))) - { - /* - * Internalize the string. - */ - value = (xmlChar *) xmlDictLookup(style->dict, - textNode->content, -1); - xmlNodeSetContent(textNode, NULL); - textNode->content = value; - } - textNode = NULL; - /* - * Note that "disable-output-escaping" of the xsl:text - * element will be applied at a later level, when - * XSLT elements are processed. - */ - } - -next_sibling: - if (cur->type == XML_ELEMENT_NODE) { - xsltCompilerNodePop(cctxt, cur); - } - if (cur == node) - break; - if (cur->next != NULL) { - cur = cur->next; - } else { - cur = cur->parent; - inXSLText = 0; - goto next_sibling; - }; - } - if (deleteNode != NULL) { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltParsePreprocessStylesheetTree: removing node\n"); -#endif - xmlUnlinkNode(deleteNode); - xmlFreeNode(deleteNode); - } - return(0); - -internal_err: - return(-1); -} - -#endif /* XSLT_REFACTORED */ - -#ifdef XSLT_REFACTORED -#else -static void -xsltPrecomputeStylesheet(xsltStylesheetPtr style, xmlNodePtr cur) -{ - xmlNodePtr deleteNode; - int internalize = 0; - - if ((style == NULL) || (cur == NULL)) - return; - - if ((cur->doc != NULL) && (style->dict != NULL) && - (cur->doc->dict == style->dict)) - internalize = 1; - else - style->internalized = 0; - /* - * This content comes from the stylesheet - * For stylesheets, the set of whitespace-preserving - * element names consists of just xsl:text. - */ - deleteNode = NULL; - while (cur != NULL) { - if (deleteNode != NULL) { -#ifdef WITH_XSLT_DEBUG_BLANKS - xsltGenericDebug(xsltGenericDebugContext, - "xsltPrecomputeStylesheet: removing ignorable blank node\n"); -#endif - xmlUnlinkNode(deleteNode); - xmlFreeNode(deleteNode); - deleteNode = NULL; - } - if (cur->type == XML_ELEMENT_NODE) { - int exclPrefixes; - /* - * Internalize attributes values. - */ - if ((internalize) && (cur->properties != NULL)) { - xmlAttrPtr attr = cur->properties; - xmlNodePtr txt; - - while (attr != NULL) { - txt = attr->children; - if ((txt != NULL) && (txt->type == XML_TEXT_NODE) && - (txt->content != NULL) && - (!xmlDictOwns(style->dict, txt->content))) - { - xmlChar *tmp; - - /* - * internalize the text string, goal is to speed - * up operations and minimize used space by compiled - * stylesheets. - */ - tmp = (xmlChar *) xmlDictLookup(style->dict, - txt->content, -1); - if (tmp != txt->content) { - xmlNodeSetContent(txt, NULL); - txt->content = tmp; - } - } - attr = attr->next; - } - } - if (IS_XSLT_ELEM(cur)) { - exclPrefixes = 0; - xsltStylePreCompute(style, cur); - if (IS_XSLT_NAME(cur, "text")) { - for (;exclPrefixes > 0;exclPrefixes--) - exclPrefixPop(style); - goto skip_children; - } - } else { - exclPrefixes = xsltParseStylesheetExcludePrefix(style, cur, 0); - } - - if ((cur->nsDef != NULL) && (style->exclPrefixNr > 0)) { - xmlNsPtr ns = cur->nsDef, prev = NULL, next; - xmlNodePtr root = NULL; - int i, moved; - - root = xmlDocGetRootElement(cur->doc); - if ((root != NULL) && (root != cur)) { - while (ns != NULL) { - moved = 0; - next = ns->next; - for (i = 0;i < style->exclPrefixNr;i++) { - if ((ns->prefix != NULL) && - (xmlStrEqual(ns->href, - style->exclPrefixTab[i]))) { - /* - * Move the namespace definition on the root - * element to avoid duplicating it without - * loosing it. - */ - if (prev == NULL) { - cur->nsDef = ns->next; - } else { - prev->next = ns->next; - } - ns->next = root->nsDef; - root->nsDef = ns; - moved = 1; - break; - } - } - if (moved == 0) - prev = ns; - ns = next; - } - } - } - /* - * If we have prefixes locally, recurse and pop them up when - * going back - */ - if (exclPrefixes > 0) { - xsltPrecomputeStylesheet(style, cur->children); - for (;exclPrefixes > 0;exclPrefixes--) - exclPrefixPop(style); - goto skip_children; - } - } else if (cur->type == XML_TEXT_NODE) { - if (IS_BLANK_NODE(cur)) { - if (xmlNodeGetSpacePreserve(cur) != 1) { - deleteNode = cur; - } - } else if ((cur->content != NULL) && (internalize) && - (!xmlDictOwns(style->dict, cur->content))) { - xmlChar *tmp; - - /* - * internalize the text string, goal is to speed - * up operations and minimize used space by compiled - * stylesheets. - */ - tmp = (xmlChar *) xmlDictLookup(style->dict, cur->content, -1); - xmlNodeSetContent(cur, NULL); - cur->content = tmp; - } - } else if ((cur->type != XML_ELEMENT_NODE) && - (cur->type != XML_CDATA_SECTION_NODE)) { - deleteNode = cur; - goto skip_children; - } - - /* - * Skip to next node - */ - if (cur->children != NULL) { - if ((cur->children->type != XML_ENTITY_DECL) && - (cur->children->type != XML_ENTITY_REF_NODE) && - (cur->children->type != XML_ENTITY_NODE)) { - cur = cur->children; - continue; - } - } - -skip_children: - if (cur->next != NULL) { - cur = cur->next; - continue; - } - do { - - cur = cur->parent; - if (cur == NULL) - break; - if (cur == (xmlNodePtr) style->doc) { - cur = NULL; - break; - } - if (cur->next != NULL) { - cur = cur->next; - break; - } - } while (cur != NULL); - } - if (deleteNode != NULL) { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltPrecomputeStylesheet: removing ignorable blank node\n"); -#endif - xmlUnlinkNode(deleteNode); - xmlFreeNode(deleteNode); - } -} -#endif /* end of else XSLT_REFACTORED */ - -/** - * xsltGatherNamespaces: - * @style: the XSLT stylesheet - * - * Browse the stylesheet and build the namspace hash table which - * will be used for XPath interpretation. If needed do a bit of normalization - */ - -static void -xsltGatherNamespaces(xsltStylesheetPtr style) { - xmlNodePtr cur; - const xmlChar *URI; - - if (style == NULL) - return; - /* - * TODO: basically if the stylesheet uses the same prefix for different - * patterns, well they may be in problem, hopefully they will get - * a warning first. - */ - /* - * TODO: Eliminate the use of the hash for XPath expressions. - * An expression should be evaluated in the context of the in-scope - * namespaces; eliminate the restriction of an XML document to contain - * no duplicate prefixes for different namespace names. - * - */ - cur = xmlDocGetRootElement(style->doc); - while (cur != NULL) { - if (cur->type == XML_ELEMENT_NODE) { - xmlNsPtr ns = cur->nsDef; - while (ns != NULL) { - if (ns->prefix != NULL) { - if (style->nsHash == NULL) { - style->nsHash = xmlHashCreate(10); - if (style->nsHash == NULL) { - xsltTransformError(NULL, style, cur, - "xsltGatherNamespaces: failed to create hash table\n"); - style->errors++; - return; - } - } - URI = xmlHashLookup(style->nsHash, ns->prefix); - if ((URI != NULL) && (!xmlStrEqual(URI, ns->href))) { - xsltTransformError(NULL, style, cur, - "Namespaces prefix %s used for multiple namespaces\n",ns->prefix); - style->warnings++; - } else if (URI == NULL) { - xmlHashUpdateEntry(style->nsHash, ns->prefix, - (void *) ns->href, (xmlHashDeallocator)xmlFree); - -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "Added namespace: %s mapped to %s\n", ns->prefix, ns->href); -#endif - } - } - ns = ns->next; - } - } - - /* - * Skip to next node - */ - if (cur->children != NULL) { - if (cur->children->type != XML_ENTITY_DECL) { - cur = cur->children; - continue; - } - } - if (cur->next != NULL) { - cur = cur->next; - continue; - } - - do { - cur = cur->parent; - if (cur == NULL) - break; - if (cur == (xmlNodePtr) style->doc) { - cur = NULL; - break; - } - if (cur->next != NULL) { - cur = cur->next; - break; - } - } while (cur != NULL); - } -} - -#ifdef XSLT_REFACTORED - -static xsltStyleType -xsltGetXSLTElementTypeByNode(xsltCompilerCtxtPtr cctxt, - xmlNodePtr node) -{ - if ((node == NULL) || (node->type != XML_ELEMENT_NODE) || - (node->name == NULL)) - return(0); - - if (node->name[0] == 'a') { - if (IS_XSLT_NAME(node, "apply-templates")) - return(XSLT_FUNC_APPLYTEMPLATES); - else if (IS_XSLT_NAME(node, "attribute")) - return(XSLT_FUNC_ATTRIBUTE); - else if (IS_XSLT_NAME(node, "apply-imports")) - return(XSLT_FUNC_APPLYIMPORTS); - else if (IS_XSLT_NAME(node, "attribute-set")) - return(0); - - } else if (node->name[0] == 'c') { - if (IS_XSLT_NAME(node, "choose")) - return(XSLT_FUNC_CHOOSE); - else if (IS_XSLT_NAME(node, "copy")) - return(XSLT_FUNC_COPY); - else if (IS_XSLT_NAME(node, "copy-of")) - return(XSLT_FUNC_COPYOF); - else if (IS_XSLT_NAME(node, "call-template")) - return(XSLT_FUNC_CALLTEMPLATE); - else if (IS_XSLT_NAME(node, "comment")) - return(XSLT_FUNC_COMMENT); - - } else if (node->name[0] == 'd') { - if (IS_XSLT_NAME(node, "document")) - return(XSLT_FUNC_DOCUMENT); - else if (IS_XSLT_NAME(node, "decimal-format")) - return(0); - - } else if (node->name[0] == 'e') { - if (IS_XSLT_NAME(node, "element")) - return(XSLT_FUNC_ELEMENT); - - } else if (node->name[0] == 'f') { - if (IS_XSLT_NAME(node, "for-each")) - return(XSLT_FUNC_FOREACH); - else if (IS_XSLT_NAME(node, "fallback")) - return(XSLT_FUNC_FALLBACK); - - } else if (*(node->name) == 'i') { - if (IS_XSLT_NAME(node, "if")) - return(XSLT_FUNC_IF); - else if (IS_XSLT_NAME(node, "include")) - return(0); - else if (IS_XSLT_NAME(node, "import")) - return(0); - - } else if (*(node->name) == 'k') { - if (IS_XSLT_NAME(node, "key")) - return(0); - - } else if (*(node->name) == 'm') { - if (IS_XSLT_NAME(node, "message")) - return(XSLT_FUNC_MESSAGE); - - } else if (*(node->name) == 'n') { - if (IS_XSLT_NAME(node, "number")) - return(XSLT_FUNC_NUMBER); - else if (IS_XSLT_NAME(node, "namespace-alias")) - return(0); - - } else if (*(node->name) == 'o') { - if (IS_XSLT_NAME(node, "otherwise")) - return(XSLT_FUNC_OTHERWISE); - else if (IS_XSLT_NAME(node, "output")) - return(0); - - } else if (*(node->name) == 'p') { - if (IS_XSLT_NAME(node, "param")) - return(XSLT_FUNC_PARAM); - else if (IS_XSLT_NAME(node, "processing-instruction")) - return(XSLT_FUNC_PI); - else if (IS_XSLT_NAME(node, "preserve-space")) - return(0); - - } else if (*(node->name) == 's') { - if (IS_XSLT_NAME(node, "sort")) - return(XSLT_FUNC_SORT); - else if (IS_XSLT_NAME(node, "strip-space")) - return(0); - else if (IS_XSLT_NAME(node, "stylesheet")) - return(0); - - } else if (node->name[0] == 't') { - if (IS_XSLT_NAME(node, "text")) - return(XSLT_FUNC_TEXT); - else if (IS_XSLT_NAME(node, "template")) - return(0); - else if (IS_XSLT_NAME(node, "transform")) - return(0); - - } else if (*(node->name) == 'v') { - if (IS_XSLT_NAME(node, "value-of")) - return(XSLT_FUNC_VALUEOF); - else if (IS_XSLT_NAME(node, "variable")) - return(XSLT_FUNC_VARIABLE); - - } else if (*(node->name) == 'w') { - if (IS_XSLT_NAME(node, "when")) - return(XSLT_FUNC_WHEN); - if (IS_XSLT_NAME(node, "with-param")) - return(XSLT_FUNC_WITHPARAM); - } - return(0); -} - -int -xsltParseAnyXSLTElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr elem) -{ - if ((cctxt == NULL) || (elem == NULL) || - (elem->type != XML_ELEMENT_NODE)) - return(-1); - - elem->psvi = NULL; - - if (! (IS_XSLT_ELEM_FAST(elem))) - return(-1); - /* - * Detection of handled content of extension instructions. - */ - if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { - cctxt->inode->extContentHandled = 1; - } - - xsltCompilerNodePush(cctxt, elem); - /* - * URGENT TODO: Find a way to speed up this annoying redundant - * textual node-name and namespace comparison. - */ - if (cctxt->inode->prev->curChildType != 0) - cctxt->inode->type = cctxt->inode->prev->curChildType; - else - cctxt->inode->type = xsltGetXSLTElementTypeByNode(cctxt, elem); - /* - * Update the in-scope namespaces if needed. - */ - if (elem->nsDef != NULL) - cctxt->inode->inScopeNs = - xsltCompilerBuildInScopeNsList(cctxt, elem); - /* - * xsltStylePreCompute(): Precompute the XSLT-instruction. - * This will compile the information found on the current - * element's attributes. NOTE that this won't process the - * children of the current element. - */ - xsltStylePreCompute(cctxt->style, elem); - /* - * Validate the content model of the XSLT-element. - */ - switch (cctxt->inode->type) { - case XSLT_FUNC_APPLYIMPORTS: - /* EMPTY */ - goto empty_content; - case XSLT_FUNC_APPLYTEMPLATES: - /* */ - goto apply_templates; - case XSLT_FUNC_ATTRIBUTE: - /* */ - goto sequence_constructor; - case XSLT_FUNC_CALLTEMPLATE: - /* */ - goto call_template; - case XSLT_FUNC_CHOOSE: - /* */ - goto choose; - case XSLT_FUNC_COMMENT: - /* */ - goto sequence_constructor; - case XSLT_FUNC_COPY: - /* */ - goto sequence_constructor; - case XSLT_FUNC_COPYOF: - /* EMPTY */ - goto empty_content; - case XSLT_FUNC_DOCUMENT: /* Extra one */ - /* ?? template ?? */ - goto sequence_constructor; - case XSLT_FUNC_ELEMENT: - /* */ - goto sequence_constructor; - case XSLT_FUNC_FALLBACK: - /* */ - goto sequence_constructor; - case XSLT_FUNC_FOREACH: - /* */ - goto for_each; - case XSLT_FUNC_IF: - /* */ - goto sequence_constructor; - case XSLT_FUNC_OTHERWISE: - /* */ - goto sequence_constructor; - case XSLT_FUNC_MESSAGE: - /* */ - goto sequence_constructor; - case XSLT_FUNC_NUMBER: - /* EMPTY */ - goto empty_content; - case XSLT_FUNC_PARAM: - /* */ - goto sequence_constructor; - case XSLT_FUNC_PI: - /* */ - goto sequence_constructor; - case XSLT_FUNC_SORT: - /* EMPTY */ - goto empty_content; - case XSLT_FUNC_TEXT: - /* */ - goto text; - case XSLT_FUNC_VALUEOF: - /* EMPTY */ - goto empty_content; - case XSLT_FUNC_VARIABLE: - /* */ - goto sequence_constructor; - case XSLT_FUNC_WHEN: - /* */ - goto sequence_constructor; - case XSLT_FUNC_WITHPARAM: - /* */ - goto sequence_constructor; - default: -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltParseXSLTNode: Unhandled XSLT element '%s'.\n", - elem->name); -#endif - xsltTransformError(NULL, cctxt->style, elem, - "xsltParseXSLTNode: Internal error; " - "unhandled XSLT element '%s'.\n", elem->name); - cctxt->style->errors++; - goto internal_err; - } - -apply_templates: - /* */ - if (elem->children != NULL) { - xmlNodePtr child = elem->children; - do { - if (child->type == XML_ELEMENT_NODE) { - if (IS_XSLT_ELEM_FAST(child)) { - if (xmlStrEqual(child->name, BAD_CAST "with-param")) { - cctxt->inode->curChildType = XSLT_FUNC_WITHPARAM; - xsltParseAnyXSLTElem(cctxt, child); - } else if (xmlStrEqual(child->name, BAD_CAST "sort")) { - cctxt->inode->curChildType = XSLT_FUNC_SORT; - xsltParseAnyXSLTElem(cctxt, child); - } else - xsltParseContentError(cctxt->style, child); - } else - xsltParseContentError(cctxt->style, child); - } - child = child->next; - } while (child != NULL); - } - goto exit; - -call_template: - /* */ - if (elem->children != NULL) { - xmlNodePtr child = elem->children; - do { - if (child->type == XML_ELEMENT_NODE) { - if (IS_XSLT_ELEM_FAST(child)) { - xsltStyleType type; - - type = xsltGetXSLTElementTypeByNode(cctxt, child); - if (type == XSLT_FUNC_WITHPARAM) { - cctxt->inode->curChildType = XSLT_FUNC_WITHPARAM; - xsltParseAnyXSLTElem(cctxt, child); - } else { - xsltParseContentError(cctxt->style, child); - } - } else - xsltParseContentError(cctxt->style, child); - } - child = child->next; - } while (child != NULL); - } - goto exit; - -text: - if (elem->children != NULL) { - xmlNodePtr child = elem->children; - do { - if ((child->type != XML_TEXT_NODE) && - (child->type != XML_CDATA_SECTION_NODE)) - { - xsltTransformError(NULL, cctxt->style, elem, - "The XSLT 'text' element must have only character " - "data as content.\n"); - } - child = child->next; - } while (child != NULL); - } - goto exit; - -empty_content: - if (elem->children != NULL) { - xmlNodePtr child = elem->children; - /* - * Relaxed behaviour: we will allow whitespace-only text-nodes. - */ - do { - if (((child->type != XML_TEXT_NODE) && - (child->type != XML_CDATA_SECTION_NODE)) || - (! IS_BLANK_NODE(child))) - { - xsltTransformError(NULL, cctxt->style, elem, - "This XSLT element must have no content.\n"); - cctxt->style->errors++; - break; - } - child = child->next; - } while (child != NULL); - } - goto exit; - -choose: - /* */ - /* - * TODO: text-nodes in between are *not* allowed in XSLT 1.0. - * The old behaviour did not check this. - * NOTE: In XSLT 2.0 they are stripped beforehand - * if whitespace-only (regardless of xml:space). - */ - if (elem->children != NULL) { - xmlNodePtr child = elem->children; - int nbWhen = 0, nbOtherwise = 0, err = 0; - do { - if (child->type == XML_ELEMENT_NODE) { - if (IS_XSLT_ELEM_FAST(child)) { - xsltStyleType type; - - type = xsltGetXSLTElementTypeByNode(cctxt, child); - if (type == XSLT_FUNC_WHEN) { - nbWhen++; - if (nbOtherwise) { - xsltParseContentError(cctxt->style, child); - err = 1; - break; - } - cctxt->inode->curChildType = XSLT_FUNC_WHEN; - xsltParseAnyXSLTElem(cctxt, child); - } else if (type == XSLT_FUNC_OTHERWISE) { - if (! nbWhen) { - xsltParseContentError(cctxt->style, child); - err = 1; - break; - } - if (nbOtherwise) { - xsltTransformError(NULL, cctxt->style, elem, - "The XSLT 'choose' element must not contain " - "more than one XSLT 'otherwise' element.\n"); - cctxt->style->errors++; - err = 1; - break; - } - nbOtherwise++; - cctxt->inode->curChildType = XSLT_FUNC_OTHERWISE; - xsltParseAnyXSLTElem(cctxt, child); - } else - xsltParseContentError(cctxt->style, child); - } else - xsltParseContentError(cctxt->style, child); - } - /* - else - xsltParseContentError(cctxt, child); - */ - child = child->next; - } while (child != NULL); - if ((! err) && (! nbWhen)) { - xsltTransformError(NULL, cctxt->style, elem, - "The XSLT element 'choose' must contain at least one " - "XSLT element 'when'.\n"); - cctxt->style->errors++; - } - } - goto exit; - -for_each: - /* */ - /* - * NOTE: Text-nodes before xsl:sort are *not* allowed in XSLT 1.0. - * The old behaviour did not allow this, but it catched this - * only at transformation-time. - * In XSLT 2.0 they are stripped beforehand if whitespace-only - * (regardless of xml:space). - */ - if (elem->children != NULL) { - xmlNodePtr child = elem->children; - /* - * Parse xsl:sort first. - */ - do { - if ((child->type == XML_ELEMENT_NODE) && - IS_XSLT_ELEM_FAST(child)) - { - if (xsltGetXSLTElementTypeByNode(cctxt, child) == - XSLT_FUNC_SORT) - { - cctxt->inode->curChildType = XSLT_FUNC_SORT; - xsltParseAnyXSLTElem(cctxt, child); - } else - break; - } else - break; - child = child->next; - } while (child != NULL); - /* - * Parse the sequece constructor. - */ - if (child != NULL) - xsltParseSequenceConstructor(cctxt, child); - } - goto exit; - -sequence_constructor: - if (elem->children != NULL) - xsltParseSequenceConstructor(cctxt, elem->children); - -exit: - xsltCompilerNodePop(cctxt, elem); - return(0); - -internal_err: - xsltCompilerNodePop(cctxt, elem); - return(-1); -} - -static xsltStyleItemUknownPtr -xsltForwardsCompatUnkownItemCreate(xsltCompilerCtxtPtr cctxt) -{ - xsltStyleItemUknownPtr item; - - item = (xsltStyleItemUknownPtr) xmlMalloc(sizeof(xsltStyleItemUknown)); - if (item == NULL) { - xsltTransformError(NULL, cctxt->style, NULL, - "Internal error in xsltForwardsCompatUnkownItemCreate(): " - "Failed to allocate memory.\n"); - cctxt->style->errors++; - return(NULL); - } - memset(item, 0, sizeof(xsltStyleItemUknown)); - item->type = XSLT_FUNC_UNKOWN_FORWARDS_COMPAT; - /* - * Store it in the stylesheet. - */ - item->next = cctxt->style->preComps; - cctxt->style->preComps = (xsltElemPreCompPtr) item; - return(item); -} - -static int -xsltParseUnknownXSLTElem(xsltCompilerCtxtPtr cctxt, - xmlNodePtr node) -{ - if ((cctxt == NULL) || (node == NULL)) - return(-1); - - /* - * Detection of handled content of extension instructions. - */ - if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { - cctxt->inode->extContentHandled = 1; - } - if (cctxt->inode->forwardsCompat == 0) { - /* - * We are not in forwards-compatible mode, so raise an error. - */ - xsltTransformError(NULL, cctxt->style, node, - "Unknown XSLT element '%s'.\n", node->name); - cctxt->style->errors++; - return(0); - } - /* - * Forwards-compatible mode. - * ------------------------ - * - * Parse/compile xsl:fallback elements. - * - * QUESTION: Do we have to raise an error if there's no xsl:fallback? - * ANSWER: No, since in the stylesheet the fallback behaviour might - * also be provided by using the XSLT function "element-available". - */ - if (cctxt->unknownItem == NULL) { - /* - * Create a singleton for all unknown XSLT instructions. - */ - cctxt->unknownItem = xsltForwardsCompatUnkownItemCreate(cctxt); - if (cctxt->unknownItem == NULL) { - node->psvi = NULL; - return(-1); - } - } - node->psvi = cctxt->unknownItem; - if (node->children == NULL) - return(0); - else { - xmlNodePtr child = node->children; - - xsltCompilerNodePush(cctxt, node); - /* - * Update the in-scope namespaces if needed. - */ - if (node->nsDef != NULL) - cctxt->inode->inScopeNs = - xsltCompilerBuildInScopeNsList(cctxt, node); - /* - * Parse all xsl:fallback children. - */ - do { - if ((child->type == XML_ELEMENT_NODE) && - IS_XSLT_ELEM_FAST(child) && - IS_XSLT_NAME(child, "fallback")) - { - cctxt->inode->curChildType = XSLT_FUNC_FALLBACK; - xsltParseAnyXSLTElem(cctxt, child); - } - child = child->next; - } while (child != NULL); - - xsltCompilerNodePop(cctxt, node); - } - return(0); -} -/** - * xsltParseSequenceConstructor: - * - * @cctxt: the compilation context - * @cur: the start-node of the content to be parsed - * - * Parses a "template" content (or "sequence constructor" in XSLT 2.0 terms). - * This will additionally remove xsl:text elements from the tree. - */ -void -xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) -{ - xsltStyleType type; - xmlNodePtr deleteNode = NULL; - - if (cctxt == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xsltParseSequenceConstructor: Bad arguments\n"); - cctxt->style->errors++; - return; - } - /* - * Detection of handled content of extension instructions. - */ - if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { - cctxt->inode->extContentHandled = 1; - } - if (cur == NULL) - return; - /* - * This is the content reffered to as a "template". - * E.g. an xsl:element has such content model: - * - * - * - * NOTE that in XSLT-2 the term "template" was abandoned due to - * confusion with xsl:template and the term "sequence constructor" - * was introduced instead. - * - * The following XSLT-instructions are allowed to appear: - * xsl:apply-templates, xsl:call-template, xsl:apply-imports, - * xsl:for-each, xsl:value-of, xsl:copy-of, xsl:number, - * xsl:choose, xsl:if, xsl:text, xsl:copy, xsl:variable, - * xsl:message, xsl:fallback, - * xsl:processing-instruction, xsl:comment, xsl:element - * xsl:attribute. - * Additional allowed content: - * 1) extension instructions - * 2) literal result elements - * 3) PCDATA - * - * NOTE that this content model does *not* allow xsl:param. - */ - while (cur != NULL) { - if (deleteNode != NULL) { -#ifdef WITH_XSLT_DEBUG_BLANKS - xsltGenericDebug(xsltGenericDebugContext, - "xsltParseSequenceConstructor: removing xsl:text element\n"); -#endif - xmlUnlinkNode(deleteNode); - xmlFreeNode(deleteNode); - deleteNode = NULL; - } - if (cur->type == XML_ELEMENT_NODE) { - - if (cur->psvi == xsltXSLTTextMarker) { - /* - * xsl:text elements - * -------------------------------------------------------- - */ - xmlNodePtr tmp; - - cur->psvi = NULL; - /* - * Mark the xsl:text element for later deletion. - */ - deleteNode = cur; - /* - * Validate content. - */ - tmp = cur->children; - if (tmp) { - /* - * We don't expect more than one text-node in the - * content, since we already merged adjacent - * text/CDATA-nodes and eliminated PI/comment-nodes. - */ - if ((tmp->type == XML_TEXT_NODE) || - (tmp->next == NULL)) - { - /* - * Leave the contained text-node in the tree. - */ - xmlUnlinkNode(tmp); - xmlAddPrevSibling(cur, tmp); - } else { - tmp = NULL; - xsltTransformError(NULL, cctxt->style, cur, - "Element 'xsl:text': Invalid type " - "of node found in content.\n"); - cctxt->style->errors++; - } - } - if (cur->properties) { - xmlAttrPtr attr; - /* - * TODO: We need to report errors for - * invalid attrs. - */ - attr = cur->properties; - do { - if ((attr->ns == NULL) && - (attr->name != NULL) && - (attr->name[0] == 'd') && - xmlStrEqual(attr->name, - BAD_CAST "disable-output-escaping")) - { - /* - * Attr "disable-output-escaping". - * XSLT-2: This attribute is deprecated. - */ - if ((attr->children != NULL) && - xmlStrEqual(attr->children->content, - BAD_CAST "yes")) - { - /* - * Disable output escaping for this - * text node. - */ - if (tmp) - tmp->name = xmlStringTextNoenc; - } else if ((attr->children == NULL) || - (attr->children->content == NULL) || - (!xmlStrEqual(attr->children->content, - BAD_CAST "no"))) - { - xsltTransformError(NULL, cctxt->style, - cur, - "Attribute 'disable-output-escaping': " - "Invalid value. Expected is " - "'yes' or 'no'.\n"); - cctxt->style->errors++; - } - break; - } - attr = attr->next; - } while (attr != NULL); - } - } else if (IS_XSLT_ELEM_FAST(cur)) { - /* - * TODO: Using the XSLT-marker is still not stable yet. - */ - /* if (cur->psvi == xsltXSLTElemMarker) { */ - /* - * XSLT instructions - * -------------------------------------------------------- - */ - cur->psvi = NULL; - type = xsltGetXSLTElementTypeByNode(cctxt, cur); - switch (type) { - case XSLT_FUNC_APPLYIMPORTS: - case XSLT_FUNC_APPLYTEMPLATES: - case XSLT_FUNC_ATTRIBUTE: - case XSLT_FUNC_CALLTEMPLATE: - case XSLT_FUNC_CHOOSE: - case XSLT_FUNC_COMMENT: - case XSLT_FUNC_COPY: - case XSLT_FUNC_COPYOF: - case XSLT_FUNC_DOCUMENT: /* Extra one */ - case XSLT_FUNC_ELEMENT: - case XSLT_FUNC_FALLBACK: - case XSLT_FUNC_FOREACH: - case XSLT_FUNC_IF: - case XSLT_FUNC_MESSAGE: - case XSLT_FUNC_NUMBER: - case XSLT_FUNC_PI: - case XSLT_FUNC_TEXT: - case XSLT_FUNC_VALUEOF: - case XSLT_FUNC_VARIABLE: - /* - * Parse the XSLT element. - */ - cctxt->inode->curChildType = type; - xsltParseAnyXSLTElem(cctxt, cur); - break; - default: - xsltParseUnknownXSLTElem(cctxt, cur); - cur = cur->next; - continue; - } - } else { - /* - * Non-XSLT elements - * ----------------- - */ - xsltCompilerNodePush(cctxt, cur); - /* - * Update the in-scope namespaces if needed. - */ - if (cur->nsDef != NULL) - cctxt->inode->inScopeNs = - xsltCompilerBuildInScopeNsList(cctxt, cur); - /* - * The current element is either a literal result element - * or an extension instruction. - * - * Process attr "xsl:extension-element-prefixes". - * FUTURE TODO: IIRC in XSLT 2.0 this attribute must be - * processed by the implementor of the extension function; - * i.e., it won't be handled by the XSLT processor. - */ - /* SPEC 1.0: - * "exclude-result-prefixes" is only allowed on literal - * result elements and "xsl:exclude-result-prefixes" - * on xsl:stylesheet/xsl:transform. - * SPEC 2.0: - * "There are a number of standard attributes - * that may appear on any XSLT element: specifically - * version, exclude-result-prefixes, - * extension-element-prefixes, xpath-default-namespace, - * default-collation, and use-when." - * - * SPEC 2.0: - * For literal result elements: - * "xsl:version, xsl:exclude-result-prefixes, - * xsl:extension-element-prefixes, - * xsl:xpath-default-namespace, - * xsl:default-collation, or xsl:use-when." - */ - if (cur->properties) - cctxt->inode->extElemNs = - xsltParseExtElemPrefixes(cctxt, - cur, cctxt->inode->extElemNs, - XSLT_ELEMENT_CATEGORY_LRE); - /* - * Eval if we have an extension instruction here. - */ - if ((cur->ns != NULL) && - (cctxt->inode->extElemNs != NULL) && - (xsltCheckExtPrefix(cctxt->style, cur->ns->href) == 1)) - { - /* - * Extension instructions - * ---------------------------------------------------- - * Mark the node information. - */ - cctxt->inode->category = XSLT_ELEMENT_CATEGORY_EXTENSION; - cctxt->inode->extContentHandled = 0; - if (cur->psvi != NULL) { - cur->psvi = NULL; - /* - * TODO: Temporary sanity check. - */ - xsltTransformError(NULL, cctxt->style, cur, - "Internal error in xsltParseSequenceConstructor(): " - "Occupied PSVI field.\n"); - cctxt->style->errors++; - cur = cur->next; - continue; - } - cur->psvi = (void *) - xsltPreComputeExtModuleElement(cctxt->style, cur); - - if (cur->psvi == NULL) { - /* - * OLD COMMENT: "Unknown element, maybe registered at the - * context level. Mark it for later recognition." - * QUESTION: What does the xsltExtMarker mean? - * ANSWER: It is used in xsltApplyOneTemplateInt() at - * transformation-time to look out for extension - * registered in the transformation context. - */ - cur->psvi = (void *) xsltExtMarker; - } - /* - * BIG NOTE: Now the ugly part. In previous versions - * of Libxslt (until 1.1.16), all the content of an - * extension instruction was processed and compiled without - * the need of the extension-author to explicitely call - * such a processing;.We now need to mimic this old - * behaviour in order to avoid breaking old code - * on the extension-author's side. - * The mechanism: - * 1) If the author does *not* set the - * compile-time-flag @extContentHandled, then we'll - * parse the content assuming that it's a "template" - * (or "sequence constructor in XSLT 2.0 terms). - * NOTE: If the extension is registered at - * transformation-time only, then there's no way of - * knowing that content shall be valid, and we'll - * process the content the same way. - * 2) If author *does* set the flag, then we'll assume - * that the author has handled the parsing him/herself - * (e.g. called xsltParseSequenceConstructor(), etc. - * explicitely in his/her code). - */ - if ((cur->children != NULL) && - (cctxt->inode->extContentHandled == 0)) - { - /* - * Default parsing of the content using the - * sequence-constructor model. - */ - xsltParseSequenceConstructor(cctxt, cur->children); - } - } else { - /* - * Literal result element - * ---------------------------------------------------- - * Allowed XSLT attributes: - * xsl:extension-element-prefixes CDATA #IMPLIED - * xsl:exclude-result-prefixes CDATA #IMPLIED - * TODO: xsl:use-attribute-sets %qnames; #IMPLIED - * xsl:version NMTOKEN #IMPLIED - */ - cur->psvi = NULL; - cctxt->inode->category = XSLT_ELEMENT_CATEGORY_LRE; - if (cur->properties != NULL) { - xmlAttrPtr attr = cur->properties; - /* - * Attribute "xsl:exclude-result-prefixes". - */ - cctxt->inode->exclResultNs = - xsltParseExclResultPrefixes(cctxt, cur, - cctxt->inode->exclResultNs, - XSLT_ELEMENT_CATEGORY_LRE); - /* - * Attribute "xsl:version". - */ - xsltParseAttrXSLTVersion(cctxt, cur, - XSLT_ELEMENT_CATEGORY_LRE); - /* - * Report invalid XSLT attributes. - * For XSLT 1.0 only xsl:use-attribute-sets is allowed - * next to xsl:version, xsl:exclude-result-prefixes and - * xsl:extension-element-prefixes. - * - * Mark all XSLT attributes, in order to skip such - * attributes when instantiating the LRE. - */ - do { - if ((attr->psvi != xsltXSLTAttrMarker) && - IS_XSLT_ATTR_FAST(attr)) - { - if (! xmlStrEqual(attr->name, - BAD_CAST "use-attribute-sets")) - { - xsltTransformError(NULL, cctxt->style, - cur, - "Unknown XSLT attribute '%s'.\n", - attr->name); - cctxt->style->errors++; - } else { - /* - * XSLT attr marker. - */ - attr->psvi = (void *) xsltXSLTAttrMarker; - } - } - attr = attr->next; - } while (attr != NULL); - } - /* - * Create/reuse info for the literal result element. - */ - if (cctxt->inode->nsChanged) - xsltLREInfoCreate(cctxt, cur, 1); - cur->psvi = cctxt->inode->litResElemInfo; - /* - * Apply ns-aliasing on the element and on its attributes. - */ - if (cctxt->hasNsAliases) - xsltLREBuildEffectiveNs(cctxt, cur); - /* - * Compile attribute value templates (AVT). - */ - if (cur->properties) { - xmlAttrPtr attr = cur->properties; - - while (attr != NULL) { - xsltCompileAttr(cctxt->style, attr); - attr = attr->next; - } - } - /* - * Parse the content, which is defined to be a "template" - * (or "sequence constructor" in XSLT 2.0 terms). - */ - if (cur->children != NULL) { - xsltParseSequenceConstructor(cctxt, cur->children); - } - } - /* - * Leave the non-XSLT element. - */ - xsltCompilerNodePop(cctxt, cur); - } - } - cur = cur->next; - } - if (deleteNode != NULL) { -#ifdef WITH_XSLT_DEBUG_BLANKS - xsltGenericDebug(xsltGenericDebugContext, - "xsltParseSequenceConstructor: removing xsl:text element\n"); -#endif - xmlUnlinkNode(deleteNode); - xmlFreeNode(deleteNode); - deleteNode = NULL; - } -} - -/** - * xsltParseTemplateContent: - * @style: the XSLT stylesheet - * @templ: the node containing the content to be parsed - * - * Parses and compiles the content-model of an xsl:template element. - * Note that this is *not* the "template" (or "sequence constructor" - * in XSLT 2.0) content model. Since it allows addional xsl:param - * elements as immediate children of @templ. - * - * Called by: - * exsltFuncFunctionComp() (EXSLT, functions.c) - * So this is intended to be called from extension functions. - */ -void -xsltParseTemplateContent(xsltStylesheetPtr style, xmlNodePtr templ) { - if ((style == NULL) || (templ == NULL)) - return; - - /* - * Detection of handled content of extension instructions. - */ - if (XSLT_CCTXT(style)->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { - XSLT_CCTXT(style)->inode->extContentHandled = 1; - } - - if (templ->children != NULL) { - xmlNodePtr child = templ->children; - /* - * Process xsl:param elements, which can only occur as the - * immediate children of xsl:template (well, and of any - * user-defined extension instruction if needed). - */ - do { - if ((child->type == XML_ELEMENT_NODE) && - IS_XSLT_ELEM_FAST(child) && - IS_XSLT_NAME(child, "param")) - { - XSLT_CCTXT(style)->inode->curChildType = XSLT_FUNC_PARAM; - xsltParseAnyXSLTElem(XSLT_CCTXT(style), child); - } else - break; - child = child->next; - } while (child != NULL); - /* - * Parse the content and register the pattern. - */ - xsltParseSequenceConstructor(XSLT_CCTXT(style), child); - } -} - -#else /* XSLT_REFACTORED */ - -/** - * xsltParseTemplateContent: - * @style: the XSLT stylesheet - * @templ: the container node (can be a document for literal results) - * - * parse a template content-model - * Clean-up the template content from unwanted ignorable blank nodes - * and process xslt:text - */ -void -xsltParseTemplateContent(xsltStylesheetPtr style, xmlNodePtr templ) { - xmlNodePtr cur, delete; - /* - * This content comes from the stylesheet - * For stylesheets, the set of whitespace-preserving - * element names consists of just xsl:text. - */ - cur = templ->children; - delete = NULL; - while (cur != NULL) { - if (delete != NULL) { -#ifdef WITH_XSLT_DEBUG_BLANKS - xsltGenericDebug(xsltGenericDebugContext, - "xsltParseTemplateContent: removing text\n"); -#endif - xmlUnlinkNode(delete); - xmlFreeNode(delete); - delete = NULL; - } - if (IS_XSLT_ELEM(cur)) { - if (IS_XSLT_NAME(cur, "text")) { - /* - * TODO: Processing of xsl:text should be moved to - * xsltPrecomputeStylesheet(), since otherwise this - * will be performed for every multiply included - * stylesheet; i.e. this here is not skipped with - * the use of the style->nopreproc flag. - */ - if (cur->children != NULL) { - xmlChar *prop; - xmlNodePtr text = cur->children, next; - int noesc = 0; - - prop = xmlGetNsProp(cur, - (const xmlChar *)"disable-output-escaping", - NULL); - if (prop != NULL) { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "Disable escaping: %s\n", text->content); -#endif - if (xmlStrEqual(prop, (const xmlChar *)"yes")) { - noesc = 1; - } else if (!xmlStrEqual(prop, - (const xmlChar *)"no")){ - xsltTransformError(NULL, style, cur, - "xsl:text: disable-output-escaping allows only yes or no\n"); - style->warnings++; - - } - xmlFree(prop); - } - - while (text != NULL) { - if (text->type == XML_COMMENT_NODE) { - text = text->next; - continue; - } - if ((text->type != XML_TEXT_NODE) && - (text->type != XML_CDATA_SECTION_NODE)) { - xsltTransformError(NULL, style, cur, - "xsltParseTemplateContent: xslt:text content problem\n"); - style->errors++; - break; - } - if ((noesc) && (text->type != XML_CDATA_SECTION_NODE)) - text->name = xmlStringTextNoenc; - text = text->next; - } - - /* - * replace xsl:text by the list of childs - */ - if (text == NULL) { - text = cur->children; - while (text != NULL) { - if ((style->internalized) && - (text->content != NULL) && - (!xmlDictOwns(style->dict, text->content))) { - - /* - * internalize the text string - */ - if (text->doc->dict != NULL) { - const xmlChar *tmp; - - tmp = xmlDictLookup(text->doc->dict, - text->content, -1); - if (tmp != text->content) { - xmlNodeSetContent(text, NULL); - text->content = (xmlChar *) tmp; - } - } - } - - next = text->next; - xmlUnlinkNode(text); - xmlAddPrevSibling(cur, text); - text = next; - } - } - } - delete = cur; - goto skip_children; - } - } - else if ((cur->ns != NULL) && (style->nsDefs != NULL) && - (xsltCheckExtPrefix(style, cur->ns->prefix))) - { - /* - * okay this is an extension element compile it too - */ - xsltStylePreCompute(style, cur); - } else { - /* - * This is an element which will be output as part of the - * template exectution, precompile AVT if found. - */ - if ((cur->ns == NULL) && (style->defaultAlias != NULL) && - (cur->type == XML_ELEMENT_NODE)) { - cur->ns = xmlSearchNsByHref(cur->doc, cur, - style->defaultAlias); - } - if (cur->properties != NULL) { - xmlAttrPtr attr = cur->properties; - - while (attr != NULL) { - xsltCompileAttr(style, attr); - attr = attr->next; - } - } - } - /* - * Skip to next node - */ - if (cur->children != NULL) { - if (cur->children->type != XML_ENTITY_DECL) { - cur = cur->children; - continue; - } - } -skip_children: - if (cur->next != NULL) { - cur = cur->next; - continue; - } - - do { - cur = cur->parent; - if (cur == NULL) - break; - if (cur == templ) { - cur = NULL; - break; - } - if (cur->next != NULL) { - cur = cur->next; - break; - } - } while (cur != NULL); - } - if (delete != NULL) { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltParseTemplateContent: removing text\n"); -#endif - xmlUnlinkNode(delete); - xmlFreeNode(delete); - delete = NULL; - } - - /* - * Skip the first params - */ - cur = templ->children; - while (cur != NULL) { - if ((IS_XSLT_ELEM(cur)) && (!(IS_XSLT_NAME(cur, "param")))) - break; - cur = cur->next; - } - - /* - * Browse the remainder of the template - */ - while (cur != NULL) { - if ((IS_XSLT_ELEM(cur)) && (IS_XSLT_NAME(cur, "param"))) { - xmlNodePtr param = cur; - - xsltTransformError(NULL, style, cur, - "xsltParseTemplateContent: ignoring misplaced param element\n"); - if (style != NULL) style->warnings++; - cur = cur->next; - xmlUnlinkNode(param); - xmlFreeNode(param); - } else - break; - } -} - -#endif /* else XSLT_REFACTORED */ - -/** - * xsltParseStylesheetKey: - * @style: the XSLT stylesheet - * @key: the "key" element - * - * - * - * - * parse an XSLT stylesheet key definition and register it - */ - -static void -xsltParseStylesheetKey(xsltStylesheetPtr style, xmlNodePtr key) { - xmlChar *prop = NULL; - xmlChar *use = NULL; - xmlChar *match = NULL; - xmlChar *name = NULL; - xmlChar *nameURI = NULL; - - if ((style == NULL) || (key == NULL)) - return; - - /* - * Get arguments - */ - prop = xmlGetNsProp(key, (const xmlChar *)"name", NULL); - if (prop != NULL) { - const xmlChar *URI; - - /* - * TODO: Don't use xsltGetQNameURI(). - */ - URI = xsltGetQNameURI(key, &prop); - if (prop == NULL) { - if (style != NULL) style->errors++; - goto error; - } else { - name = prop; - if (URI != NULL) - nameURI = xmlStrdup(URI); - } -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltParseStylesheetKey: name %s\n", name); -#endif - } else { - xsltTransformError(NULL, style, key, - "xsl:key : error missing name\n"); - if (style != NULL) style->errors++; - goto error; - } - - match = xmlGetNsProp(key, (const xmlChar *)"match", NULL); - if (match == NULL) { - xsltTransformError(NULL, style, key, - "xsl:key : error missing match\n"); - if (style != NULL) style->errors++; - goto error; - } - - use = xmlGetNsProp(key, (const xmlChar *)"use", NULL); - if (use == NULL) { - xsltTransformError(NULL, style, key, - "xsl:key : error missing use\n"); - if (style != NULL) style->errors++; - goto error; - } - - /* - * register the keys - */ - xsltAddKey(style, name, nameURI, match, use, key); - - -error: - if (use != NULL) - xmlFree(use); - if (match != NULL) - xmlFree(match); - if (name != NULL) - xmlFree(name); - if (nameURI != NULL) - xmlFree(nameURI); - - if (key->children != NULL) { - xsltParseContentError(style, key->children); - } -} - -#ifdef XSLT_REFACTORED -/** - * xsltParseXSLTTemplate: - * @style: the XSLT stylesheet - * @template: the "template" element - * - * parse an XSLT stylesheet template building the associated structures - * TODO: Is @style ever expected to be NULL? - * - * Called from: - * xsltParseXSLTStylesheet() - * xsltParseStylesheetTop() - */ - -static void -xsltParseXSLTTemplate(xsltCompilerCtxtPtr cctxt, xmlNodePtr templNode) { - xsltTemplatePtr templ; - xmlChar *prop; - double priority; - - if ((cctxt == NULL) || (templNode == NULL)) - return; - - /* - * Create and link the structure - */ - templ = xsltNewTemplate(); - if (templ == NULL) - return; - - xsltCompilerNodePush(cctxt, templNode); - if (templNode->nsDef != NULL) - cctxt->inode->inScopeNs = - xsltCompilerBuildInScopeNsList(cctxt, templNode); - - templ->next = cctxt->style->templates; - cctxt->style->templates = templ; - templ->style = cctxt->style; - - /* - * Attribute "mode". - */ - prop = xmlGetNsProp(templNode, (const xmlChar *)"mode", NULL); - if (prop != NULL) { - const xmlChar *modeURI; - - /* - * TODO: We need a standardized function for extraction - * of namespace names and local names from QNames. - * Don't use xsltGetQNameURI() as it cannot channeö - * reports through the context. - */ - modeURI = xsltGetQNameURI(templNode, &prop); - if (prop == NULL) { - cctxt->style->errors++; - goto error; - } - templ->mode = xmlDictLookup(cctxt->style->dict, prop, -1); - xmlFree(prop); - prop = NULL; - if (xmlValidateNCName(templ->mode, 0)) { - xsltTransformError(NULL, cctxt->style, templNode, - "xsl:template: Attribute 'mode': The local part '%s' " - "of the value is not a valid NCName.\n", templ->name); - cctxt->style->errors++; - goto error; - } - if (modeURI != NULL) - templ->modeURI = xmlDictLookup(cctxt->style->dict, modeURI, -1); -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltParseXSLTTemplate: mode %s\n", templ->mode); -#endif - } - /* - * Attribute "match". - */ - prop = xmlGetNsProp(templNode, (const xmlChar *)"match", NULL); - if (prop != NULL) { - templ->match = prop; - prop = NULL; - } - /* - * Attribute "priority". - */ - prop = xmlGetNsProp(templNode, (const xmlChar *)"priority", NULL); - if (prop != NULL) { - priority = xmlXPathStringEvalNumber(prop); - templ->priority = (float) priority; - xmlFree(prop); - prop = NULL; - } - /* - * Attribute "name". - */ - prop = xmlGetNsProp(templNode, (const xmlChar *)"name", NULL); - if (prop != NULL) { - const xmlChar *nameURI; - xsltTemplatePtr curTempl; - - /* - * TODO: Don't use xsltGetQNameURI(). - */ - nameURI = xsltGetQNameURI(templNode, &prop); - if (prop == NULL) { - cctxt->style->errors++; - goto error; - } - templ->name = xmlDictLookup(cctxt->style->dict, prop, -1); - xmlFree(prop); - prop = NULL; - if (xmlValidateNCName(templ->name, 0)) { - xsltTransformError(NULL, cctxt->style, templNode, - "xsl:template: Attribute 'name': The local part '%s' of " - "the value is not a valid NCName.\n", templ->name); - cctxt->style->errors++; - goto error; - } - if (nameURI != NULL) - templ->nameURI = xmlDictLookup(cctxt->style->dict, nameURI, -1); - curTempl = templ->next; - while (curTempl != NULL) { - if ((nameURI != NULL && xmlStrEqual(curTempl->name, templ->name) && - xmlStrEqual(curTempl->nameURI, nameURI) ) || - (nameURI == NULL && curTempl->nameURI == NULL && - xmlStrEqual(curTempl->name, templ->name))) - { - xsltTransformError(NULL, cctxt->style, templNode, - "xsl:template: error duplicate name '%s'\n", templ->name); - cctxt->style->errors++; - goto error; - } - curTempl = curTempl->next; - } - } - if (templNode->children != NULL) { - xsltParseTemplateContent(cctxt->style, templNode); - /* - * MAYBE TODO: Custom behaviour: In order to stay compatible with - * Xalan and MSXML(.NET), we could allow whitespace - * to appear before an xml:param element; this whitespace - * will additionally become part of the "template". - * NOTE that this is totally deviates from the spec, but - * is the de facto behaviour of Xalan and MSXML(.NET). - * Personally I wouldn't allow this, since if we have: - * - * - * - * - * ... the whitespace between every xsl:param would be - * added to the result tree. - */ - } - - templ->elem = templNode; - templ->content = templNode->children; - xsltAddTemplate(cctxt->style, templ, templ->mode, templ->modeURI); - -error: - xsltCompilerNodePop(cctxt, templNode); - return; -} - -#else /* XSLT_REFACTORED */ - -/** - * xsltParseStylesheetTemplate: - * @style: the XSLT stylesheet - * @template: the "template" element - * - * parse an XSLT stylesheet template building the associated structures - */ - -static void -xsltParseStylesheetTemplate(xsltStylesheetPtr style, xmlNodePtr template) { - xsltTemplatePtr ret; - xmlChar *prop; - xmlChar *mode = NULL; - xmlChar *modeURI = NULL; - double priority; - - if (template == NULL) - return; - - /* - * Create and link the structure - */ - ret = xsltNewTemplate(); - if (ret == NULL) - return; - ret->next = style->templates; - style->templates = ret; - ret->style = style; - - /* - * Get inherited namespaces - */ - /* - * TODO: Apply the optimized in-scope-namespace mechanism - * as for the other XSLT instructions. - */ - xsltGetInheritedNsList(style, ret, template); - - /* - * Get arguments - */ - prop = xmlGetNsProp(template, (const xmlChar *)"mode", NULL); - if (prop != NULL) { - const xmlChar *URI; - - /* - * TODO: Don't use xsltGetQNameURI(). - */ - URI = xsltGetQNameURI(template, &prop); - if (prop == NULL) { - if (style != NULL) style->errors++; - goto error; - } else { - mode = prop; - if (URI != NULL) - modeURI = xmlStrdup(URI); - } - ret->mode = xmlDictLookup(style->dict, mode, -1); - ret->modeURI = xmlDictLookup(style->dict, modeURI, -1); -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltParseStylesheetTemplate: mode %s\n", mode); -#endif - if (mode != NULL) xmlFree(mode); - if (modeURI != NULL) xmlFree(modeURI); - } - prop = xmlGetNsProp(template, (const xmlChar *)"match", NULL); - if (prop != NULL) { - if (ret->match != NULL) xmlFree(ret->match); - ret->match = prop; - } - - prop = xmlGetNsProp(template, (const xmlChar *)"priority", NULL); - if (prop != NULL) { - priority = xmlXPathStringEvalNumber(prop); - ret->priority = (float) priority; - xmlFree(prop); - } - - prop = xmlGetNsProp(template, (const xmlChar *)"name", NULL); - if (prop != NULL) { - const xmlChar *URI; - xsltTemplatePtr cur; - - /* - * TODO: Don't use xsltGetQNameURI(). - */ - URI = xsltGetQNameURI(template, &prop); - if (prop == NULL) { - if (style != NULL) style->errors++; - goto error; - } else { - if (xmlValidateNCName(prop,0)) { - xsltTransformError(NULL, style, template, - "xsl:template : error invalid name '%s'\n", prop); - if (style != NULL) style->errors++; - goto error; - } - ret->name = xmlDictLookup(style->dict, BAD_CAST prop, -1); - xmlFree(prop); - prop = NULL; - if (URI != NULL) - ret->nameURI = xmlDictLookup(style->dict, BAD_CAST URI, -1); - else - ret->nameURI = NULL; - cur = ret->next; - while (cur != NULL) { - if ((URI != NULL && xmlStrEqual(cur->name, ret->name) && - xmlStrEqual(cur->nameURI, URI) ) || - (URI == NULL && cur->nameURI == NULL && - xmlStrEqual(cur->name, ret->name))) { - xsltTransformError(NULL, style, template, - "xsl:template: error duplicate name '%s'\n", ret->name); - style->errors++; - goto error; - } - cur = cur->next; - } - } - } - - /* - * parse the content and register the pattern - */ - xsltParseTemplateContent(style, template); - ret->elem = template; - ret->content = template->children; - xsltAddTemplate(style, ret, ret->mode, ret->modeURI); - -error: - return; -} - -#endif /* else XSLT_REFACTORED */ - -#ifdef XSLT_REFACTORED - -/** - * xsltIncludeComp: - * @cctxt: the compilation contenxt - * @node: the xsl:include node - * - * Process the xslt include node on the source node - */ -static xsltStyleItemIncludePtr -xsltCompileXSLTIncludeElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) { - xsltStyleItemIncludePtr item; - - if ((cctxt == NULL) || (node == NULL)) - return(NULL); - - node->psvi = NULL; - item = (xsltStyleItemIncludePtr) xmlMalloc(sizeof(xsltStyleItemInclude)); - if (item == NULL) { - xsltTransformError(NULL, cctxt->style, node, - "xsltIncludeComp : malloc failed\n"); - cctxt->style->errors++; - return(NULL); - } - memset(item, 0, sizeof(xsltStyleItemInclude)); - - node->psvi = item; - item->inst = node; - item->type = XSLT_FUNC_INCLUDE; - - item->next = cctxt->style->preComps; - cctxt->style->preComps = (xsltElemPreCompPtr) item; - - return(item); -} - -/** - * xsltParseFindTopLevelElem: - */ -static int -xsltParseFindTopLevelElem(xsltCompilerCtxtPtr cctxt, - xmlNodePtr cur, - const xmlChar *name, - const xmlChar *namespaceURI, - int breakOnOtherElem, - xmlNodePtr *resultNode) -{ - if (name == NULL) - return(-1); - - *resultNode = NULL; - while (cur != NULL) { - if (cur->type == XML_ELEMENT_NODE) { - if ((cur->ns != NULL) && (cur->name != NULL)) { - if ((*(cur->name) == *name) && - xmlStrEqual(cur->name, name) && - xmlStrEqual(cur->ns->href, namespaceURI)) - { - *resultNode = cur; - return(1); - } - } - if (breakOnOtherElem) - break; - } - cur = cur->next; - } - *resultNode = cur; - return(0); -} - -static int -xsltParseTopLevelXSLTElem(xsltCompilerCtxtPtr cctxt, - xmlNodePtr node, - xsltStyleType type) -{ - int ret = 0; - - /* - * TODO: The reason why this function exists: - * due to historical reasons some of the - * top-level declarations are processed by functions - * in other files. Since we need still to set - * up the node-info and generate information like - * in-scope namespaces, this is a wrapper around - * those old parsing functions. - */ - xsltCompilerNodePush(cctxt, node); - if (node->nsDef != NULL) - cctxt->inode->inScopeNs = - xsltCompilerBuildInScopeNsList(cctxt, node); - cctxt->inode->type = type; - - switch (type) { - case XSLT_FUNC_INCLUDE: - { - int oldIsInclude; - - if (xsltCompileXSLTIncludeElem(cctxt, node) == NULL) - goto exit; - /* - * Mark this stylesheet tree as being currently included. - */ - oldIsInclude = cctxt->isInclude; - cctxt->isInclude = 1; - - if (xsltParseStylesheetInclude(cctxt->style, node) != 0) { - cctxt->style->errors++; - } - cctxt->isInclude = oldIsInclude; - } - break; - case XSLT_FUNC_PARAM: - xsltStylePreCompute(cctxt->style, node); - xsltParseGlobalParam(cctxt->style, node); - break; - case XSLT_FUNC_VARIABLE: - xsltStylePreCompute(cctxt->style, node); - xsltParseGlobalVariable(cctxt->style, node); - break; - case XSLT_FUNC_ATTRSET: - xsltParseStylesheetAttributeSet(cctxt->style, node); - break; - default: - xsltTransformError(NULL, cctxt->style, node, - "Internal error: (xsltParseTopLevelXSLTElem) " - "Cannot handle this top-level declaration.\n"); - cctxt->style->errors++; - ret = -1; - } - -exit: - xsltCompilerNodePop(cctxt, node); - - return(ret); -} - -#if 0 -static int -xsltParseRemoveWhitespace(xmlNodePtr node) -{ - if ((node == NULL) || (node->children == NULL)) - return(0); - else { - xmlNodePtr delNode = NULL, child = node->children; - - do { - if (delNode) { - xmlUnlinkNode(delNode); - xmlFreeNode(delNode); - delNode = NULL; - } - if (((child->type == XML_TEXT_NODE) || - (child->type == XML_CDATA_SECTION_NODE)) && - (IS_BLANK_NODE(child))) - delNode = child; - child = child->next; - } while (child != NULL); - if (delNode) { - xmlUnlinkNode(delNode); - xmlFreeNode(delNode); - delNode = NULL; - } - } - return(0); -} -#endif - -static int -xsltParseXSLTStylesheetElemCore(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) -{ -#ifdef WITH_XSLT_DEBUG_PARSING - int templates = 0; -#endif - xmlNodePtr cur, start = NULL; - xsltStylesheetPtr style; - - if ((cctxt == NULL) || (node == NULL) || - (node->type != XML_ELEMENT_NODE)) - return(-1); - - style = cctxt->style; - /* - * At this stage all import declarations of all stylesheet modules - * with the same stylesheet level have been processed. - * Now we can safely parse the rest of the declarations. - */ - if (IS_XSLT_ELEM_FAST(node) && IS_XSLT_NAME(node, "include")) - { - xsltDocumentPtr include; - /* - * URGENT TODO: Make this work with simplified stylesheets! - * I.e., when we won't find an xsl:stylesheet element. - */ - /* - * This is as include declaration. - */ - include = ((xsltStyleItemIncludePtr) node->psvi)->include; - if (include == NULL) { - /* TODO: raise error? */ - return(-1); - } - /* - * TODO: Actually an xsl:include should locate an embedded - * stylesheet as well; so the document-element won't always - * be the element where the actual stylesheet is rooted at. - * But such embedded stylesheets are not supported by Libxslt yet. - */ - node = xmlDocGetRootElement(include->doc); - if (node == NULL) { - return(-1); - } - } - - if (node->children == NULL) - return(0); - /* - * Push the xsl:stylesheet/xsl:transform element. - */ - xsltCompilerNodePush(cctxt, node); - cctxt->inode->isRoot = 1; - cctxt->inode->nsChanged = 0; - /* - * Start with the naked dummy info for literal result elements. - */ - cctxt->inode->litResElemInfo = cctxt->inodeList->litResElemInfo; - - /* - * In every case, we need to have - * the in-scope namespaces of the element, where the - * stylesheet is rooted at, regardless if it's an XSLT - * instruction or a literal result instruction (or if - * this is an embedded stylesheet). - */ - cctxt->inode->inScopeNs = - xsltCompilerBuildInScopeNsList(cctxt, node); - - /* - * Process attributes of xsl:stylesheet/xsl:transform. - * -------------------------------------------------- - * Allowed are: - * id = id - * extension-element-prefixes = tokens - * exclude-result-prefixes = tokens - * version = number (mandatory) - */ - if (xsltParseAttrXSLTVersion(cctxt, node, - XSLT_ELEMENT_CATEGORY_XSLT) == 0) - { - /* - * Attribute "version". - * XSLT 1.0: "An xsl:stylesheet element *must* have a version - * attribute, indicating the version of XSLT that the - * stylesheet requires". - * The root element of a simplified stylesheet must also have - * this attribute. - */ -#ifdef XSLT_REFACTORED_MANDATORY_VERSION - if (isXsltElem) - xsltTransformError(NULL, cctxt->style, node, - "The attribute 'version' is missing.\n"); - cctxt->style->errors++; -#else - /* OLD behaviour. */ - xsltTransformError(NULL, cctxt->style, node, - "xsl:version is missing: document may not be a stylesheet\n"); - cctxt->style->warnings++; -#endif - } - /* - * The namespaces declared by the attributes - * "extension-element-prefixes" and - * "exclude-result-prefixes" are local to *this* - * stylesheet tree; i.e., they are *not* visible to - * other stylesheet-modules, whether imported or included. - * - * Attribute "extension-element-prefixes". - */ - cctxt->inode->extElemNs = - xsltParseExtElemPrefixes(cctxt, node, NULL, - XSLT_ELEMENT_CATEGORY_XSLT); - /* - * Attribute "exclude-result-prefixes". - */ - cctxt->inode->exclResultNs = - xsltParseExclResultPrefixes(cctxt, node, NULL, - XSLT_ELEMENT_CATEGORY_XSLT); - /* - * Create/reuse info for the literal result element. - */ - if (cctxt->inode->nsChanged) - xsltLREInfoCreate(cctxt, node, 0); - /* - * Processed top-level elements: - * ---------------------------- - * xsl:variable, xsl:param (QName, in-scope ns, - * expression (vars allowed)) - * xsl:attribute-set (QName, in-scope ns) - * xsl:strip-space, xsl:preserve-space (XPath NameTests, - * in-scope ns) - * I *think* global scope, merge with includes - * xsl:output (QName, in-scope ns) - * xsl:key (QName, in-scope ns, pattern, - * expression (vars *not* allowed)) - * xsl:decimal-format (QName, needs in-scope ns) - * xsl:namespace-alias (in-scope ns) - * global scope, merge with includes - * xsl:template (last, QName, pattern) - * - * (whitespace-only text-nodes have *not* been removed - * yet; this will be done in xsltParseSequenceConstructor) - * - * Report misplaced child-nodes first. - */ - cur = node->children; - while (cur != NULL) { - if (cur->type == XML_TEXT_NODE) { - xsltTransformError(NULL, style, cur, - "Misplaced text node (content: '%s').\n", - (cur->content != NULL) ? cur->content : BAD_CAST ""); - style->errors++; - } else if (cur->type != XML_ELEMENT_NODE) { - xsltTransformError(NULL, style, cur, "Misplaced node.\n"); - style->errors++; - } - cur = cur->next; - } - /* - * Skip xsl:import elements; they have been processed - * already. - */ - cur = node->children; - while ((cur != NULL) && xsltParseFindTopLevelElem(cctxt, cur, - BAD_CAST "import", XSLT_NAMESPACE, 1, &cur) == 1) - cur = cur->next; - if (cur == NULL) - goto exit; - - start = cur; - /* - * Process all top-level xsl:param elements. - */ - while ((cur != NULL) && - xsltParseFindTopLevelElem(cctxt, cur, - BAD_CAST "param", XSLT_NAMESPACE, 0, &cur) == 1) - { - xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_PARAM); - cur = cur->next; - } - /* - * Process all top-level xsl:variable elements. - */ - cur = start; - while ((cur != NULL) && - xsltParseFindTopLevelElem(cctxt, cur, - BAD_CAST "variable", XSLT_NAMESPACE, 0, &cur) == 1) - { - xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_VARIABLE); - cur = cur->next; - } - /* - * Process all the rest of top-level elements. - */ - cur = start; - while (cur != NULL) { - /* - * Process element nodes. - */ - if (cur->type == XML_ELEMENT_NODE) { - if (cur->ns == NULL) { - xsltTransformError(NULL, style, cur, - "Unexpected top-level element in no namespace.\n"); - style->errors++; - cur = cur->next; - continue; - } - /* - * Process all XSLT elements. - */ - if (IS_XSLT_ELEM_FAST(cur)) { - /* - * xsl:import is only allowed at the beginning. - */ - if (IS_XSLT_NAME(cur, "import")) { - xsltTransformError(NULL, style, cur, - "Misplaced xsl:import element.\n"); - style->errors++; - cur = cur->next; - continue; - } - /* - * TODO: Change the return type of the parsing functions - * to int. - */ - if (IS_XSLT_NAME(cur, "template")) { -#ifdef WITH_XSLT_DEBUG_PARSING - templates++; -#endif - /* - * TODO: Is the position of xsl:template in the - * tree significant? If not it would be easier to - * parse them at a later stage. - */ - xsltParseXSLTTemplate(cctxt, cur); - } else if (IS_XSLT_NAME(cur, "variable")) { - /* NOP; done already */ - } else if (IS_XSLT_NAME(cur, "param")) { - /* NOP; done already */ - } else if (IS_XSLT_NAME(cur, "include")) { - if (cur->psvi != NULL) - xsltParseXSLTStylesheetElemCore(cctxt, cur); - else { - xsltTransformError(NULL, style, cur, - "Internal error: " - "(xsltParseXSLTStylesheetElemCore) " - "The xsl:include element was not compiled.\n"); - style->errors++; - } - } else if (IS_XSLT_NAME(cur, "strip-space")) { - /* No node info needed. */ - xsltParseStylesheetStripSpace(style, cur); - } else if (IS_XSLT_NAME(cur, "preserve-space")) { - /* No node info needed. */ - xsltParseStylesheetPreserveSpace(style, cur); - } else if (IS_XSLT_NAME(cur, "output")) { - /* No node-info needed. */ - xsltParseStylesheetOutput(style, cur); - } else if (IS_XSLT_NAME(cur, "key")) { - /* TODO: node-info needed for expressions ? */ - xsltParseStylesheetKey(style, cur); - } else if (IS_XSLT_NAME(cur, "decimal-format")) { - /* No node-info needed. */ - xsltParseStylesheetDecimalFormat(style, cur); - } else if (IS_XSLT_NAME(cur, "attribute-set")) { - xsltParseTopLevelXSLTElem(cctxt, cur, - XSLT_FUNC_ATTRSET); - } else if (IS_XSLT_NAME(cur, "namespace-alias")) { - /* NOP; done already */ - } else { - if (cctxt->inode->forwardsCompat) { - /* - * Forwards-compatible mode: - * - * XSLT-1: "if it is a top-level element and - * XSLT 1.0 does not allow such elements as top-level - * elements, then the element must be ignored along - * with its content;" - */ - /* - * TODO: I don't think we should generate a warning. - */ - xsltTransformError(NULL, style, cur, - "Forwards-compatible mode: Ignoring unknown XSLT " - "element '%s'.\n", cur->name); - style->warnings++; - } else { - xsltTransformError(NULL, style, cur, - "Unknown XSLT element '%s'.\n", cur->name); - style->errors++; - } - } - } else { - xsltTopLevelFunction function; - - /* - * Process non-XSLT elements, which are in a - * non-NULL namespace. - */ - /* - * QUESTION: What does xsltExtModuleTopLevelLookup() - * do exactly? - */ - function = xsltExtModuleTopLevelLookup(cur->name, - cur->ns->href); - if (function != NULL) - function(style, cur); -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltParseXSLTStylesheetElemCore : User-defined " - "data element '%s'.\n", cur->name); -#endif - } - } - cur = cur->next; - } - -exit: - -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "### END of parsing top-level elements of doc '%s'.\n", - node->doc->URL); - xsltGenericDebug(xsltGenericDebugContext, - "### Templates: %d\n", templates); -#ifdef XSLT_REFACTORED - xsltGenericDebug(xsltGenericDebugContext, - "### Max inodes: %d\n", cctxt->maxNodeInfos); - xsltGenericDebug(xsltGenericDebugContext, - "### Max LREs : %d\n", cctxt->maxLREs); -#endif /* XSLT_REFACTORED */ -#endif /* WITH_XSLT_DEBUG_PARSING */ - - xsltCompilerNodePop(cctxt, node); - return(0); -} - -/** - * xsltParseXSLTStylesheet: - * @cctxt: the compiler context - * @node: the xsl:stylesheet/xsl:transform element-node - * - * Parses the xsl:stylesheet and xsl:transform element. - * - * - * - * - * - * BIG TODO: The xsl:include stuff. - * - * Called by xsltParseStylesheetTree() - * - * Returns 0 on success, a positive result on errors and - * -1 on API or internal errors. - */ -static int -xsltParseXSLTStylesheetElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) -{ - xmlNodePtr cur, start; - - if ((cctxt == NULL) || (node == NULL)) - return(-1); - - if (node->children == NULL) - goto exit; - - /* - * Process top-level elements: - * xsl:import (must be first) - * xsl:include (this is just a pre-processing) - */ - cur = node->children; - /* - * Process xsl:import elements. - * XSLT 1.0: "The xsl:import element children must precede all - * other element children of an xsl:stylesheet element, - * including any xsl:include element children." - */ - while ((cur != NULL) && - xsltParseFindTopLevelElem(cctxt, cur, - BAD_CAST "import", XSLT_NAMESPACE, 1, &cur) == 1) - { - if (xsltParseStylesheetImport(cctxt->style, cur) != 0) { - cctxt->style->errors++; - } - cur = cur->next; - } - if (cur == NULL) - goto exit; - start = cur; - /* - * Pre-process all xsl:include elements. - */ - cur = start; - while ((cur != NULL) && - xsltParseFindTopLevelElem(cctxt, cur, - BAD_CAST "include", XSLT_NAMESPACE, 0, &cur) == 1) - { - xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_INCLUDE); - cur = cur->next; - } - /* - * Pre-process all xsl:namespace-alias elements. - * URGENT TODO: This won't work correctly: the order of included - * aliases and aliases defined here is significant. - */ - cur = start; - while ((cur != NULL) && - xsltParseFindTopLevelElem(cctxt, cur, - BAD_CAST "namespace-alias", XSLT_NAMESPACE, 0, &cur) == 1) - { - xsltNamespaceAlias(cctxt->style, cur); - cur = cur->next; - } - - if (cctxt->isInclude) { - /* - * If this stylesheet is intended for inclusion, then - * we will process only imports and includes. - */ - goto exit; - } - /* - * Now parse the rest of the top-level elements. - */ - xsltParseXSLTStylesheetElemCore(cctxt, node); -exit: - - return(0); -} - -#else /* XSLT_REFACTORED */ - -/** - * xsltParseStylesheetTop: - * @style: the XSLT stylesheet - * @top: the top level "stylesheet" or "transform" element - * - * scan the top level elements of an XSL stylesheet - */ -static void -xsltParseStylesheetTop(xsltStylesheetPtr style, xmlNodePtr top) { - xmlNodePtr cur; - xmlChar *prop; -#ifdef WITH_XSLT_DEBUG_PARSING - int templates = 0; -#endif - - if (top == NULL) - return; - - prop = xmlGetNsProp(top, (const xmlChar *)"version", NULL); - if (prop == NULL) { - xsltTransformError(NULL, style, top, - "xsl:version is missing: document may not be a stylesheet\n"); - if (style != NULL) style->warnings++; - } else { - if ((!xmlStrEqual(prop, (const xmlChar *)"1.0")) && - (!xmlStrEqual(prop, (const xmlChar *)"1.1"))) { - xsltTransformError(NULL, style, top, - "xsl:version: only 1.0 features are supported\n"); - /* TODO set up compatibility when not XSLT 1.0 */ - if (style != NULL) style->warnings++; - } - xmlFree(prop); - } - - cur = top->children; - - /* - * process xsl:import elements - */ - while (cur != NULL) { - if (IS_BLANK_NODE(cur)) { - cur = cur->next; - continue; - } - if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "import")) { - if (xsltParseStylesheetImport(style, cur) != 0) - if (style != NULL) style->errors++; - } else - break; - cur = cur->next; - } - - /* - * process other top-level elements - */ - while (cur != NULL) { - if (IS_BLANK_NODE(cur)) { - cur = cur->next; - continue; - } - if (cur->type == XML_TEXT_NODE) { - if (cur->content != NULL) { - xsltTransformError(NULL, style, cur, - "misplaced text node: '%s'\n", cur->content); - } - if (style != NULL) style->errors++; - cur = cur->next; - continue; - } - if ((cur->type == XML_ELEMENT_NODE) && (cur->ns == NULL)) { - xsltGenericError(xsltGenericErrorContext, - "Found a top-level element %s with null namespace URI\n", - cur->name); - if (style != NULL) style->errors++; - cur = cur->next; - continue; - } - if ((cur->type == XML_ELEMENT_NODE) && (!(IS_XSLT_ELEM(cur)))) { - xsltTopLevelFunction function; - - function = xsltExtModuleTopLevelLookup(cur->name, - cur->ns->href); - if (function != NULL) - function(style, cur); - -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltParseStylesheetTop : found foreign element %s\n", - cur->name); -#endif - cur = cur->next; - continue; - } - if (IS_XSLT_NAME(cur, "import")) { - xsltTransformError(NULL, style, cur, - "xsltParseStylesheetTop: ignoring misplaced import element\n"); - if (style != NULL) style->errors++; - } else if (IS_XSLT_NAME(cur, "include")) { - if (xsltParseStylesheetInclude(style, cur) != 0) - if (style != NULL) style->errors++; - } else if (IS_XSLT_NAME(cur, "strip-space")) { - xsltParseStylesheetStripSpace(style, cur); - } else if (IS_XSLT_NAME(cur, "preserve-space")) { - xsltParseStylesheetPreserveSpace(style, cur); - } else if (IS_XSLT_NAME(cur, "output")) { - xsltParseStylesheetOutput(style, cur); - } else if (IS_XSLT_NAME(cur, "key")) { - xsltParseStylesheetKey(style, cur); - } else if (IS_XSLT_NAME(cur, "decimal-format")) { - xsltParseStylesheetDecimalFormat(style, cur); - } else if (IS_XSLT_NAME(cur, "attribute-set")) { - xsltParseStylesheetAttributeSet(style, cur); - } else if (IS_XSLT_NAME(cur, "variable")) { - xsltParseGlobalVariable(style, cur); - } else if (IS_XSLT_NAME(cur, "param")) { - xsltParseGlobalParam(style, cur); - } else if (IS_XSLT_NAME(cur, "template")) { -#ifdef WITH_XSLT_DEBUG_PARSING - templates++; -#endif - xsltParseStylesheetTemplate(style, cur); - } else if (IS_XSLT_NAME(cur, "namespace-alias")) { - xsltNamespaceAlias(style, cur); - } else { - /* - * BUG TODO: The version of the *doc* is irrelevant for - * the forwards-compatible mode. - */ - if ((style != NULL) && (style->doc->version != NULL) && - (!strncmp((const char *) style->doc->version, "1.0", 3))) { - xsltTransformError(NULL, style, cur, - "xsltParseStylesheetTop: unknown %s element\n", - cur->name); - if (style != NULL) style->errors++; - } - else { - /* do Forwards-Compatible Processing */ - xsltTransformError(NULL, style, cur, - "xsltParseStylesheetTop: ignoring unknown %s element\n", - cur->name); - if (style != NULL) style->warnings++; - } - } - cur = cur->next; - } -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "parsed %d templates\n", templates); -#endif -} - -#endif /* else of XSLT_REFACTORED */ - -#ifdef XSLT_REFACTORED -/** - * xsltParseSimplifiedStylesheetTree: - * - * @style: the stylesheet (TODO: Change this to the compiler context) - * @doc: the document containing the stylesheet. - * @node: the node where the stylesheet is rooted at - * - * Returns 0 in case of success, a positive result if an error occurred - * and -1 on API and internal errors. - */ -static int -xsltParseSimplifiedStylesheetTree(xsltCompilerCtxtPtr cctxt, - xmlDocPtr doc, - xmlNodePtr node) -{ - xsltTemplatePtr templ; - - if ((cctxt == NULL) || (node == NULL)) - return(-1); - - if (xsltParseAttrXSLTVersion(cctxt, node, 0) == XSLT_ELEMENT_CATEGORY_LRE) - { - /* - * TODO: Adjust report, since this might be an - * embedded stylesheet. - */ - xsltTransformError(NULL, cctxt->style, node, - "The attribute 'xsl:version' is missing; cannot identify " - "this document as an XSLT stylesheet document.\n"); - cctxt->style->errors++; - return(1); - } - -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltParseSimplifiedStylesheetTree: document is stylesheet\n"); -#endif - - /* - * Create and link the template - */ - templ = xsltNewTemplate(); - if (templ == NULL) { - return(-1); - } - templ->next = cctxt->style->templates; - cctxt->style->templates = templ; - templ->match = xmlStrdup(BAD_CAST "/"); - - /* - * Note that we push the document-node in this special case. - */ - xsltCompilerNodePush(cctxt, (xmlNodePtr) doc); - /* - * In every case, we need to have - * the in-scope namespaces of the element, where the - * stylesheet is rooted at, regardless if it's an XSLT - * instruction or a literal result instruction (or if - * this is an embedded stylesheet). - */ - cctxt->inode->inScopeNs = - xsltCompilerBuildInScopeNsList(cctxt, node); - /* - * Parse the content and register the match-pattern. - */ - xsltParseSequenceConstructor(cctxt, node); - xsltCompilerNodePop(cctxt, (xmlNodePtr) doc); - - templ->elem = (xmlNodePtr) doc; - templ->content = node; - xsltAddTemplate(cctxt->style, templ, NULL, NULL); - cctxt->style->literal_result = 1; - return(0); -} - -#ifdef XSLT_REFACTORED_XSLT_NSCOMP -int -xsltRestoreDocumentNamespaces(xsltNsMapPtr ns, xmlDocPtr doc) -{ - if (doc == NULL) - return(-1); - /* - * Revert the changes we have applied to the namespace-URIs of - * ns-decls. - */ - while (ns != NULL) { - if ((ns->doc == doc) && (ns->ns != NULL)) { - ns->ns->href = ns->origNsName; - ns->origNsName = NULL; - ns->ns = NULL; - } - ns = ns->next; - } - return(0); -} -#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ - -/** - * xsltParseStylesheetProcess: - * @style: the XSLT stylesheet (the current stylesheet-level) - * @doc: and xmlDoc parsed XML - * - * Parses an XSLT stylesheet, adding the associated structures. - * Called by: - * xsltParseStylesheetImportedDoc() (xslt.c) - * xsltParseStylesheetInclude() (imports.c) - * - * Returns the value of the @style parameter if everything - * went right, NULL if something went amiss. - */ -xsltStylesheetPtr -xsltParseStylesheetProcess(xsltStylesheetPtr style, xmlDocPtr doc) -{ - xsltCompilerCtxtPtr cctxt; - xmlNodePtr cur; - int oldIsSimplifiedStylesheet; - - - if ((style == NULL) || (doc == NULL)) - return(NULL); - - cctxt = XSLT_CCTXT(style); - - cur = xmlDocGetRootElement(doc); - if (cur == NULL) { - xsltTransformError(NULL, style, (xmlNodePtr) doc, - "xsltParseStylesheetProcess : empty stylesheet\n"); - return(NULL); - } - oldIsSimplifiedStylesheet = cctxt->simplified; - - if ((IS_XSLT_ELEM(cur)) && - ((IS_XSLT_NAME(cur, "stylesheet")) || - (IS_XSLT_NAME(cur, "transform")))) { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltParseStylesheetProcess : found stylesheet\n"); -#endif - cctxt->simplified = 0; - style->literal_result = 0; - } else { - cctxt->simplified = 1; - style->literal_result = 1; - } - /* - * Pre-process the stylesheet if not already done before. - * This will remove PIs and comments, merge adjacent - * text nodes, internalize strings, etc. - */ - if (! style->nopreproc) - xsltParsePreprocessStylesheetTree(cctxt, cur); - /* - * Parse and compile the stylesheet. - */ - if (style->literal_result == 0) { - if (xsltParseXSLTStylesheetElem(cctxt, cur) != 0) - return(NULL); - } else { - if (xsltParseSimplifiedStylesheetTree(cctxt, doc, cur) != 0) - return(NULL); - } - - cctxt->simplified = oldIsSimplifiedStylesheet; - - return(style); -} - -#else /* XSLT_REFACTORED */ - -xsltStylesheetPtr -xsltParseStylesheetProcess(xsltStylesheetPtr ret, xmlDocPtr doc) { - xmlNodePtr cur; - - if (doc == NULL) - return(NULL); - if (ret == NULL) - return(ret); - - /* - * First steps, remove blank nodes, - * locate the xsl:stylesheet element and the - * namespace declaration. - */ - cur = xmlDocGetRootElement(doc); - if (cur == NULL) { - xsltTransformError(NULL, ret, (xmlNodePtr) doc, - "xsltParseStylesheetProcess : empty stylesheet\n"); - return(NULL); - } - - if ((IS_XSLT_ELEM(cur)) && - ((IS_XSLT_NAME(cur, "stylesheet")) || - (IS_XSLT_NAME(cur, "transform")))) { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltParseStylesheetProcess : found stylesheet\n"); -#endif - ret->literal_result = 0; - xsltParseStylesheetExcludePrefix(ret, cur, 1); - xsltParseStylesheetExtPrefix(ret, cur, 1); - } else { - xsltParseStylesheetExcludePrefix(ret, cur, 0); - xsltParseStylesheetExtPrefix(ret, cur, 0); - ret->literal_result = 1; - } - if (!ret->nopreproc) { - xsltPrecomputeStylesheet(ret, cur); - } - if (ret->literal_result == 0) { - xsltParseStylesheetTop(ret, cur); - } else { - xmlChar *prop; - xsltTemplatePtr template; - - /* - * the document itself might be the template, check xsl:version - */ - prop = xmlGetNsProp(cur, (const xmlChar *)"version", XSLT_NAMESPACE); - if (prop == NULL) { - xsltTransformError(NULL, ret, cur, - "xsltParseStylesheetProcess : document is not a stylesheet\n"); - return(NULL); - } - -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltParseStylesheetProcess : document is stylesheet\n"); -#endif - - if (!xmlStrEqual(prop, (const xmlChar *)"1.0")) { - xsltTransformError(NULL, ret, cur, - "xsl:version: only 1.0 features are supported\n"); - /* TODO set up compatibility when not XSLT 1.0 */ - ret->warnings++; - } - xmlFree(prop); - - /* - * Create and link the template - */ - template = xsltNewTemplate(); - if (template == NULL) { - return(NULL); - } - template->next = ret->templates; - ret->templates = template; - template->match = xmlStrdup((const xmlChar *)"/"); - - /* - * parse the content and register the pattern - */ - xsltParseTemplateContent(ret, (xmlNodePtr) doc); - template->elem = (xmlNodePtr) doc; - template->content = doc->children; - xsltAddTemplate(ret, template, NULL, NULL); - ret->literal_result = 1; - } - - return(ret); -} - -#endif /* else of XSLT_REFACTORED */ - -/** - * xsltParseStylesheetImportedDoc: - * @doc: an xmlDoc parsed XML - * @style: pointer to the parent stylesheet (if it exists) - * - * parse an XSLT stylesheet building the associated structures - * except the processing not needed for imported documents. - * - * Returns a new XSLT stylesheet structure. - */ - -xsltStylesheetPtr -xsltParseStylesheetImportedDoc(xmlDocPtr doc, - xsltStylesheetPtr parentStyle) { - xsltStylesheetPtr retStyle; - - if (doc == NULL) - return(NULL); - - retStyle = xsltNewStylesheet(); - if (retStyle == NULL) - return(NULL); - /* - * Set the importing stylesheet module; also used to detect recursion. - */ - retStyle->parent = parentStyle; - /* - * Adjust the string dict. - */ - if (doc->dict != NULL) { - xmlDictFree(retStyle->dict); - retStyle->dict = doc->dict; -#ifdef WITH_XSLT_DEBUG - xsltGenericDebug(xsltGenericDebugContext, - "reusing dictionary from %s for stylesheet\n", - doc->URL); -#endif - xmlDictReference(retStyle->dict); - } - - /* - * TODO: Eliminate xsltGatherNamespaces(); we must not restrict - * the stylesheet to containt distinct namespace prefixes. - */ - xsltGatherNamespaces(retStyle); - -#ifdef XSLT_REFACTORED - { - xsltCompilerCtxtPtr cctxt; - xsltStylesheetPtr oldCurSheet; - - if (parentStyle == NULL) { - xsltPrincipalStylesheetDataPtr principalData; - /* - * Principal stylesheet - * -------------------- - */ - retStyle->principal = retStyle; - /* - * Create extra data for the principal stylesheet. - */ - principalData = xsltNewPrincipalStylesheetData(); - if (principalData == NULL) { - xsltFreeStylesheet(retStyle); - return(NULL); - } - retStyle->principalData = principalData; - /* - * Create the compilation context - * ------------------------------ - * (only once; for the principal stylesheet). - * This is currently the only function where the - * compilation context is created. - */ - cctxt = xsltCompilationCtxtCreate(retStyle); - if (cctxt == NULL) { - xsltFreeStylesheet(retStyle); - return(NULL); - } - retStyle->compCtxt = (void *) cctxt; - cctxt->style = retStyle; - cctxt->dict = retStyle->dict; - cctxt->psData = principalData; - /* - * Push initial dummy node info. - */ - cctxt->depth = -1; - xsltCompilerNodePush(cctxt, (xmlNodePtr) doc); - } else { - /* - * Imported stylesheet. - */ - retStyle->principal = parentStyle->principal; - cctxt = parentStyle->compCtxt; - retStyle->compCtxt = cctxt; - } - /* - * Save the old and set the current stylesheet structure in the - * compilation context. - */ - oldCurSheet = cctxt->style; - cctxt->style = retStyle; - - retStyle->doc = doc; - xsltParseStylesheetProcess(retStyle, doc); - - cctxt->style = oldCurSheet; - if (parentStyle == NULL) { - /* - * Pop the initial dummy node info. - */ - xsltCompilerNodePop(cctxt, (xmlNodePtr) doc); - } else { - /* - * Clear the compilation context of imported - * stylesheets. - * TODO: really? - */ - /* retStyle->compCtxt = NULL; */ - } - /* - * Free the stylesheet if there were errors. - */ - if (retStyle != NULL) { - if (retStyle->errors != 0) { -#ifdef XSLT_REFACTORED_XSLT_NSCOMP - /* - * Restore all changes made to namespace URIs of ns-decls. - */ - if (cctxt->psData->nsMap) - xsltRestoreDocumentNamespaces(cctxt->psData->nsMap, doc); -#endif - /* - * Detach the doc from the stylesheet; otherwise the doc - * will be freed in xsltFreeStylesheet(). - */ - retStyle->doc = NULL; - /* - * Cleanup the doc if its the main stylesheet. - */ - if (parentStyle == NULL) { - xsltCleanupStylesheetTree(doc, xmlDocGetRootElement(doc)); - if (retStyle->compCtxt != NULL) { - xsltCompilationCtxtFree(retStyle->compCtxt); - retStyle->compCtxt = NULL; - } - } - - xsltFreeStylesheet(retStyle); - retStyle = NULL; - } - } - } - -#else /* XSLT_REFACTORED */ - /* - * Old behaviour. - */ - retStyle->doc = doc; - xsltParseStylesheetProcess(retStyle, doc); - if (retStyle != NULL) { - if (retStyle->errors != 0) { - retStyle->doc = NULL; - if (parentStyle == NULL) - xsltCleanupStylesheetTree(doc, - xmlDocGetRootElement(doc)); - xsltFreeStylesheet(retStyle); - retStyle = NULL; - } - } -#endif /* else of XSLT_REFACTORED */ - - return(retStyle); -} - -/** - * xsltParseStylesheetDoc: - * @doc: and xmlDoc parsed XML - * - * parse an XSLT stylesheet building the associated structures - * - * Returns a new XSLT stylesheet structure. - */ - -xsltStylesheetPtr -xsltParseStylesheetDoc(xmlDocPtr doc) { - xsltStylesheetPtr ret; - - ret = xsltParseStylesheetImportedDoc(doc, NULL); - if (ret == NULL) - return(NULL); - - xsltResolveStylesheetAttributeSet(ret); -#ifdef XSLT_REFACTORED - /* - * Free the compilation context. - * TODO: Check if it's better to move this cleanup to - * xsltParseStylesheetImportedDoc(). - */ - if (ret->compCtxt != NULL) { - xsltCompilationCtxtFree(XSLT_CCTXT(ret)); - ret->compCtxt = NULL; - } -#endif - return(ret); -} - -/** - * xsltParseStylesheetFile: - * @filename: the filename/URL to the stylesheet - * - * Load and parse an XSLT stylesheet - * - * Returns a new XSLT stylesheet structure. - */ - -xsltStylesheetPtr -xsltParseStylesheetFile(const xmlChar* filename) { - xsltSecurityPrefsPtr sec; - xsltStylesheetPtr ret; - xmlDocPtr doc; - - - if (filename == NULL) - return(NULL); - -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltParseStylesheetFile : parse %s\n", filename); -#endif - - /* - * Security framework check - */ - sec = xsltGetDefaultSecurityPrefs(); - if (sec != NULL) { - int res; - - res = xsltCheckRead(sec, NULL, filename); - if (res == 0) { - xsltTransformError(NULL, NULL, NULL, - "xsltParseStylesheetFile: read rights for %s denied\n", - filename); - return(NULL); - } - } - - doc = xsltDocDefaultLoader(filename, NULL, XSLT_PARSE_OPTIONS, - NULL, XSLT_LOAD_START); - if (doc == NULL) { - xsltTransformError(NULL, NULL, NULL, - "xsltParseStylesheetFile : cannot parse %s\n", filename); - return(NULL); - } - ret = xsltParseStylesheetDoc(doc); - if (ret == NULL) { - xmlFreeDoc(doc); - return(NULL); - } - - return(ret); -} - -/************************************************************************ - * * - * Handling of Stylesheet PI * - * * - ************************************************************************/ - -#define CUR (*cur) -#define SKIP(val) cur += (val) -#define NXT(val) cur[(val)] -#define SKIP_BLANKS \ - while (IS_BLANK(CUR)) NEXT -#define NEXT ((*cur) ? cur++ : cur) - -/** - * xsltParseStylesheetPI: - * @value: the value of the PI - * - * This function checks that the type is text/xml and extracts - * the URI-Reference for the stylesheet - * - * Returns the URI-Reference for the stylesheet or NULL (it need to - * be freed by the caller) - */ -static xmlChar * -xsltParseStylesheetPI(const xmlChar *value) { - const xmlChar *cur; - const xmlChar *start; - xmlChar *val; - xmlChar tmp; - xmlChar *href = NULL; - int isXml = 0; - - if (value == NULL) - return(NULL); - - cur = value; - while (CUR != 0) { - SKIP_BLANKS; - if ((CUR == 't') && (NXT(1) == 'y') && (NXT(2) == 'p') && - (NXT(3) == 'e')) { - SKIP(4); - SKIP_BLANKS; - if (CUR != '=') - continue; - NEXT; - if ((CUR != '\'') && (CUR != '"')) - continue; - tmp = CUR; - NEXT; - start = cur; - while ((CUR != 0) && (CUR != tmp)) - NEXT; - if (CUR != tmp) - continue; - val = xmlStrndup(start, cur - start); - NEXT; - if (val == NULL) - return(NULL); - if ((xmlStrcasecmp(val, BAD_CAST "text/xml")) && - (xmlStrcasecmp(val, BAD_CAST "text/xsl"))) { - xmlFree(val); - break; - } - isXml = 1; - xmlFree(val); - } else if ((CUR == 'h') && (NXT(1) == 'r') && (NXT(2) == 'e') && - (NXT(3) == 'f')) { - SKIP(4); - SKIP_BLANKS; - if (CUR != '=') - continue; - NEXT; - if ((CUR != '\'') && (CUR != '"')) - continue; - tmp = CUR; - NEXT; - start = cur; - while ((CUR != 0) && (CUR != tmp)) - NEXT; - if (CUR != tmp) - continue; - if (href == NULL) - href = xmlStrndup(start, cur - start); - NEXT; - } else { - while ((CUR != 0) && (!IS_BLANK(CUR))) - NEXT; - } - - } - - if (!isXml) { - if (href != NULL) - xmlFree(href); - href = NULL; - } - return(href); -} - -/** - * xsltLoadStylesheetPI: - * @doc: a document to process - * - * This function tries to locate the stylesheet PI in the given document - * If found, and if contained within the document, it will extract - * that subtree to build the stylesheet to process @doc (doc itself will - * be modified). If found but referencing an external document it will - * attempt to load it and generate a stylesheet from it. In both cases, - * the resulting stylesheet and the document need to be freed once the - * transformation is done. - * - * Returns a new XSLT stylesheet structure or NULL if not found. - */ -xsltStylesheetPtr -xsltLoadStylesheetPI(xmlDocPtr doc) { - xmlNodePtr child; - xsltStylesheetPtr ret = NULL; - xmlChar *href = NULL; - xmlURIPtr URI; - - if (doc == NULL) - return(NULL); - - /* - * Find the text/xml stylesheet PI id any before the root - */ - child = doc->children; - while ((child != NULL) && (child->type != XML_ELEMENT_NODE)) { - if ((child->type == XML_PI_NODE) && - (xmlStrEqual(child->name, BAD_CAST "xml-stylesheet"))) { - href = xsltParseStylesheetPI(child->content); - if (href != NULL) - break; - } - child = child->next; - } - - /* - * If found check the href to select processing - */ - if (href != NULL) { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltLoadStylesheetPI : found PI href=%s\n", href); -#endif - URI = xmlParseURI((const char *) href); - if (URI == NULL) { - xsltTransformError(NULL, NULL, child, - "xml-stylesheet : href %s is not valid\n", href); - xmlFree(href); - return(NULL); - } - if ((URI->fragment != NULL) && (URI->scheme == NULL) && - (URI->opaque == NULL) && (URI->authority == NULL) && - (URI->server == NULL) && (URI->user == NULL) && - (URI->path == NULL) && (URI->query == NULL)) { - xmlAttrPtr ID; - -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltLoadStylesheetPI : Reference to ID %s\n", href); -#endif - if (URI->fragment[0] == '#') - ID = xmlGetID(doc, (const xmlChar *) &(URI->fragment[1])); - else - ID = xmlGetID(doc, (const xmlChar *) URI->fragment); - if (ID == NULL) { - xsltTransformError(NULL, NULL, child, - "xml-stylesheet : no ID %s found\n", URI->fragment); - } else { - xmlDocPtr fake; - xmlNodePtr subtree; - - /* - * move the subtree in a new document passed to - * the stylesheet analyzer - */ - subtree = ID->parent; - fake = xmlNewDoc(NULL); - if (fake != NULL) { - /* - * the dictionnary should be shared since nodes are - * moved over. - */ - fake->dict = doc->dict; - xmlDictReference(doc->dict); -#ifdef WITH_XSLT_DEBUG - xsltGenericDebug(xsltGenericDebugContext, - "reusing dictionary from %s for stylesheet\n", - doc->URL); -#endif - - xmlUnlinkNode(subtree); - xmlAddChild((xmlNodePtr) fake, subtree); - ret = xsltParseStylesheetDoc(fake); - if (ret == NULL) - xmlFreeDoc(fake); - } - } - } else { - xmlChar *URL, *base; - - /* - * Reference to an external stylesheet - */ - - base = xmlNodeGetBase(doc, (xmlNodePtr) doc); - URL = xmlBuildURI(href, base); - if (URL != NULL) { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltLoadStylesheetPI : fetching %s\n", URL); -#endif - ret = xsltParseStylesheetFile(URL); - xmlFree(URL); - } else { -#ifdef WITH_XSLT_DEBUG_PARSING - xsltGenericDebug(xsltGenericDebugContext, - "xsltLoadStylesheetPI : fetching %s\n", href); -#endif - ret = xsltParseStylesheetFile(href); - } - if (base != NULL) - xmlFree(base); - } - xmlFreeURI(URI); - xmlFree(href); - } - return(ret); -} +/* + * xslt.c: Implemetation of an XSL Transformation 1.0 engine + * + * Reference: + * XSLT specification + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * Associating Style Sheets with XML documents + * http://www.w3.org/1999/06/REC-xml-stylesheet-19990629 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xslt.h" +#include "xsltInternals.h" +#include "pattern.h" +#include "variables.h" +#include "namespaces.h" +#include "attributes.h" +#include "xsltutils.h" +#include "imports.h" +#include "keys.h" +#include "documents.h" +#include "extensions.h" +#include "preproc.h" +#include "extra.h" +#include "security.h" + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_PARSING +/* #define WITH_XSLT_DEBUG_BLANKS */ +#endif + +const char *xsltEngineVersion = LIBXSLT_VERSION_STRING LIBXSLT_VERSION_EXTRA; +const int xsltLibxsltVersion = LIBXSLT_VERSION; +const int xsltLibxmlVersion = LIBXML_VERSION; + +#ifdef XSLT_REFACTORED + +const xmlChar *xsltConstNamespaceNameXSLT = (const xmlChar *) XSLT_NAMESPACE; + +/* +* xsltLiteralResultMarker: +* Marker for Literal result elements, in order to avoid multiple attempts +* to recognize such elements in the stylesheet's tree. +* This marker is set on node->psvi during the initial traversal +* of a stylesheet's node tree. +* +const xmlChar *xsltLiteralResultMarker = + (const xmlChar *) "Literal Result Element"; +*/ + +/* +* xsltXSLTTextMarker: +* Marker for xsl:text elements. Used to recognize xsl:text elements +* for post-processing of the stylesheet's tree, where those +* elements are removed from the tree. +*/ +const xmlChar *xsltXSLTTextMarker = (const xmlChar *) "XSLT Text Element"; + +/* +* xsltXSLTAttrMarker: +* Marker for XSLT attribute on Literal Result Elements. +*/ +const xmlChar *xsltXSLTAttrMarker = (const xmlChar *) "LRE XSLT Attr"; + +#endif + +/* + * Harmless but avoiding a problem when compiling against a + * libxml <= 2.3.11 without LIBXML_DEBUG_ENABLED + */ +#ifndef LIBXML_DEBUG_ENABLED +double xmlXPathStringEvalNumber(const xmlChar *str); +#endif +/* + * Useful macros + */ + +#ifdef IS_BLANK +#undef IS_BLANK +#endif +#define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \ + ((c) == 0x0D)) + +#ifdef IS_BLANK_NODE +#undef IS_BLANK_NODE +#endif +#define IS_BLANK_NODE(n) \ + (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content))) + +/** + * xsltParseContentError: + * + * @style: the stylesheet + * @node: the node where the error occured + * + * Compile-time error function. + */ +static void +xsltParseContentError(xsltStylesheetPtr style, + xmlNodePtr node) +{ + if ((style == NULL) || (node == NULL)) + return; + + if (IS_XSLT_ELEM(node)) + xsltTransformError(NULL, style, node, + "The XSLT-element '%s' is not allowed at this position.\n", + node->name); + else + xsltTransformError(NULL, style, node, + "The element '%s' is not allowed at this position.\n", + node->name); + style->errors++; +} + +#ifdef XSLT_REFACTORED +#else +/** + * exclPrefixPush: + * @style: the transformation stylesheet + * @value: the excluded namespace name to push on the stack + * + * Push an excluded namespace name on the stack + * + * Returns the new index in the stack or 0 in case of error + */ +static int +exclPrefixPush(xsltStylesheetPtr style, xmlChar * value) +{ + if (style->exclPrefixMax == 0) { + style->exclPrefixMax = 4; + style->exclPrefixTab = + (xmlChar * *)xmlMalloc(style->exclPrefixMax * + sizeof(style->exclPrefixTab[0])); + if (style->exclPrefixTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); + return (0); + } + } + if (style->exclPrefixNr >= style->exclPrefixMax) { + style->exclPrefixMax *= 2; + style->exclPrefixTab = + (xmlChar * *)xmlRealloc(style->exclPrefixTab, + style->exclPrefixMax * + sizeof(style->exclPrefixTab[0])); + if (style->exclPrefixTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); + return (0); + } + } + style->exclPrefixTab[style->exclPrefixNr] = value; + style->exclPrefix = value; + return (style->exclPrefixNr++); +} +/** + * exclPrefixPop: + * @style: the transformation stylesheet + * + * Pop an excluded prefix value from the stack + * + * Returns the stored excluded prefix value + */ +static xmlChar * +exclPrefixPop(xsltStylesheetPtr style) +{ + xmlChar *ret; + + if (style->exclPrefixNr <= 0) + return (0); + style->exclPrefixNr--; + if (style->exclPrefixNr > 0) + style->exclPrefix = style->exclPrefixTab[style->exclPrefixNr - 1]; + else + style->exclPrefix = NULL; + ret = style->exclPrefixTab[style->exclPrefixNr]; + style->exclPrefixTab[style->exclPrefixNr] = 0; + return (ret); +} +#endif + +/************************************************************************ + * * + * Helper functions * + * * + ************************************************************************/ + +/** + * xsltInit: + * + * Initializes the processor (e.g. registers built-in extensions, + * etc.) + */ + +static int initialized = 0; + +void +xsltInit (void) { + if (initialized == 0) { + initialized = 1; + xsltRegisterAllExtras(); + } +} + +/** + * xsltUninit + * + * Uninitializes the processor. + */ + +void +xsltUninit (void) { + initialized = 0; +} + +/** + * xsltIsBlank: + * @str: a string + * + * Check if a string is ignorable + * + * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise + */ +int +xsltIsBlank(xmlChar *str) { + if (str == NULL) + return(1); + while (*str != 0) { + if (!(IS_BLANK(*str))) return(0); + str++; + } + return(1); +} + +/************************************************************************ + * * + * Routines to handle XSLT data structures * + * * + ************************************************************************/ +static xsltDecimalFormatPtr +xsltNewDecimalFormat(xmlChar *name) +{ + xsltDecimalFormatPtr self; + /* UTF-8 for 0x2030 */ + static const xmlChar permille[4] = {0xe2, 0x80, 0xb0, 0}; + + self = xmlMalloc(sizeof(xsltDecimalFormat)); + if (self != NULL) { + self->next = NULL; + self->name = name; + + /* Default values */ + self->digit = xmlStrdup(BAD_CAST("#")); + self->patternSeparator = xmlStrdup(BAD_CAST(";")); + self->decimalPoint = xmlStrdup(BAD_CAST(".")); + self->grouping = xmlStrdup(BAD_CAST(",")); + self->percent = xmlStrdup(BAD_CAST("%")); + self->permille = xmlStrdup(BAD_CAST(permille)); + self->zeroDigit = xmlStrdup(BAD_CAST("0")); + self->minusSign = xmlStrdup(BAD_CAST("-")); + self->infinity = xmlStrdup(BAD_CAST("Infinity")); + self->noNumber = xmlStrdup(BAD_CAST("NaN")); + } + return self; +} + +static void +xsltFreeDecimalFormat(xsltDecimalFormatPtr self) +{ + if (self != NULL) { + if (self->digit) + xmlFree(self->digit); + if (self->patternSeparator) + xmlFree(self->patternSeparator); + if (self->decimalPoint) + xmlFree(self->decimalPoint); + if (self->grouping) + xmlFree(self->grouping); + if (self->percent) + xmlFree(self->percent); + if (self->permille) + xmlFree(self->permille); + if (self->zeroDigit) + xmlFree(self->zeroDigit); + if (self->minusSign) + xmlFree(self->minusSign); + if (self->infinity) + xmlFree(self->infinity); + if (self->noNumber) + xmlFree(self->noNumber); + if (self->name) + xmlFree(self->name); + xmlFree(self); + } +} + +static void +xsltFreeDecimalFormatList(xsltStylesheetPtr self) +{ + xsltDecimalFormatPtr iter; + xsltDecimalFormatPtr tmp; + + if (self == NULL) + return; + + iter = self->decimalFormat; + while (iter != NULL) { + tmp = iter->next; + xsltFreeDecimalFormat(iter); + iter = tmp; + } +} + +/** + * xsltDecimalFormatGetByName: + * @style: the XSLT stylesheet + * @name: the decimal-format name to find + * + * Find decimal-format by name + * + * Returns the xsltDecimalFormatPtr + */ +xsltDecimalFormatPtr +xsltDecimalFormatGetByName(xsltStylesheetPtr style, xmlChar *name) +{ + xsltDecimalFormatPtr result = NULL; + + if (name == NULL) + return style->decimalFormat; + + while (style != NULL) { + for (result = style->decimalFormat->next; + result != NULL; + result = result->next) { + if (xmlStrEqual(name, result->name)) + return result; + } + style = xsltNextImport(style); + } + return result; +} + + +/** + * xsltNewTemplate: + * + * Create a new XSLT Template + * + * Returns the newly allocated xsltTemplatePtr or NULL in case of error + */ +static xsltTemplatePtr +xsltNewTemplate(void) { + xsltTemplatePtr cur; + + cur = (xsltTemplatePtr) xmlMalloc(sizeof(xsltTemplate)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewTemplate : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltTemplate)); + cur->priority = XSLT_PAT_NO_PRIORITY; + return(cur); +} + +/** + * xsltFreeTemplate: + * @template: an XSLT template + * + * Free up the memory allocated by @template + */ +static void +xsltFreeTemplate(xsltTemplatePtr template) { + if (template == NULL) + return; + if (template->match) xmlFree(template->match); +/* +* NOTE: @name and @nameURI are put into the string dict now. +* if (template->name) xmlFree(template->name); +* if (template->nameURI) xmlFree(template->nameURI); +*/ +/* + if (template->mode) xmlFree(template->mode); + if (template->modeURI) xmlFree(template->modeURI); + */ + if (template->inheritedNs) xmlFree(template->inheritedNs); + memset(template, -1, sizeof(xsltTemplate)); + xmlFree(template); +} + +/** + * xsltFreeTemplateList: + * @template: an XSLT template list + * + * Free up the memory allocated by all the elements of @template + */ +static void +xsltFreeTemplateList(xsltTemplatePtr template) { + xsltTemplatePtr cur; + + while (template != NULL) { + cur = template; + template = template->next; + xsltFreeTemplate(cur); + } +} + +#ifdef XSLT_REFACTORED + +static void +xsltFreeNsAliasList(xsltNsAliasPtr item) +{ + xsltNsAliasPtr tmp; + + while (item) { + tmp = item; + item = item->next; + xmlFree(tmp); + } + return; +} + +#ifdef XSLT_REFACTORED_XSLT_NSCOMP +static void +xsltFreeNamespaceMap(xsltNsMapPtr item) +{ + xsltNsMapPtr tmp; + + while (item) { + tmp = item; + item = item->next; + xmlFree(tmp); + } + return; +} + +static xsltNsMapPtr +xsltNewNamespaceMapItem(xsltCompilerCtxtPtr cctxt, + xmlDocPtr doc, + xmlNsPtr ns, + xmlNodePtr elem) +{ + xsltNsMapPtr ret; + + if ((cctxt == NULL) || (doc == NULL) || (ns == NULL)) + return(NULL); + + ret = (xsltNsMapPtr) xmlMalloc(sizeof(xsltNsMap)); + if (ret == NULL) { + xsltTransformError(NULL, cctxt->style, elem, + "Internal error: (xsltNewNamespaceMapItem) " + "memory allocation failed.\n"); + return(NULL); + } + memset(ret, 0, sizeof(xsltNsMap)); + ret->doc = doc; + ret->ns = ns; + ret->origNsName = ns->href; + /* + * Store the item at current stylesheet-level. + */ + if (cctxt->psData->nsMap != NULL) + ret->next = cctxt->psData->nsMap; + cctxt->psData->nsMap = ret; + + return(ret); +} +#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ + +/** + * xsltCompilerVarInfoFree: + * @cctxt: the compilation context + * + * Frees the list of information for vars/params. + */ +static void +xsltCompilerVarInfoFree(xsltCompilerCtxtPtr cctxt) +{ + xsltVarInfoPtr ivar = cctxt->ivars, ivartmp; + + while (ivar) { + ivartmp = ivar; + ivar = ivar->next; + xmlFree(ivartmp); + } +} + +/** + * xsltCompilerCtxtFree: + * + * Free an XSLT compiler context. + */ +static void +xsltCompilationCtxtFree(xsltCompilerCtxtPtr cctxt) +{ + if (cctxt == NULL) + return; +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "Freeing compilation context\n"); + xsltGenericDebug(xsltGenericDebugContext, + "### Max inodes: %d\n", cctxt->maxNodeInfos); + xsltGenericDebug(xsltGenericDebugContext, + "### Max LREs : %d\n", cctxt->maxLREs); +#endif + /* + * Free node-infos. + */ + if (cctxt->inodeList != NULL) { + xsltCompilerNodeInfoPtr tmp, cur = cctxt->inodeList; + while (cur != NULL) { + tmp = cur; + cur = cur->next; + xmlFree(tmp); + } + } + if (cctxt->tmpList != NULL) + xsltPointerListFree(cctxt->tmpList); +#ifdef XSLT_REFACTORED_XPATHCOMP + if (cctxt->xpathCtxt != NULL) + xmlXPathFreeContext(cctxt->xpathCtxt); +#endif + if (cctxt->nsAliases != NULL) + xsltFreeNsAliasList(cctxt->nsAliases); + + if (cctxt->ivars) + xsltCompilerVarInfoFree(cctxt); + + xmlFree(cctxt); +} + +/** + * xsltCompilerCreate: + * + * Creates an XSLT compiler context. + * + * Returns the pointer to the created xsltCompilerCtxt or + * NULL in case of an internal error. + */ +static xsltCompilerCtxtPtr +xsltCompilationCtxtCreate(xsltStylesheetPtr style) { + xsltCompilerCtxtPtr ret; + + ret = (xsltCompilerCtxtPtr) xmlMalloc(sizeof(xsltCompilerCtxt)); + if (ret == NULL) { + xsltTransformError(NULL, style, NULL, + "xsltCompilerCreate: allocation of compiler " + "context failed.\n"); + return(NULL); + } + memset(ret, 0, sizeof(xsltCompilerCtxt)); + + ret->errSeverity = XSLT_ERROR_SEVERITY_ERROR; + ret->tmpList = xsltPointerListCreate(20); + if (ret->tmpList == NULL) { + goto internal_err; + } +#ifdef XSLT_REFACTORED_XPATHCOMP + /* + * Create the XPath compilation context in order + * to speed up precompilation of XPath expressions. + */ + ret->xpathCtxt = xmlXPathNewContext(NULL); + if (ret->xpathCtxt == NULL) + goto internal_err; +#endif + + return(ret); + +internal_err: + xsltCompilationCtxtFree(ret); + return(NULL); +} + +static void +xsltLREEffectiveNsNodesFree(xsltEffectiveNsPtr first) +{ + xsltEffectiveNsPtr tmp; + + while (first != NULL) { + tmp = first; + first = first->nextInStore; + xmlFree(tmp); + } +} + +static void +xsltFreePrincipalStylesheetData(xsltPrincipalStylesheetDataPtr data) +{ + if (data == NULL) + return; + + if (data->inScopeNamespaces != NULL) { + int i; + xsltNsListContainerPtr nsi; + xsltPointerListPtr list = + (xsltPointerListPtr) data->inScopeNamespaces; + + for (i = 0; i < list->number; i++) { + /* + * REVISIT TODO: Free info of in-scope namespaces. + */ + nsi = (xsltNsListContainerPtr) list->items[i]; + if (nsi->list != NULL) + xmlFree(nsi->list); + xmlFree(nsi); + } + xsltPointerListFree(list); + data->inScopeNamespaces = NULL; + } + + if (data->exclResultNamespaces != NULL) { + int i; + xsltPointerListPtr list = (xsltPointerListPtr) + data->exclResultNamespaces; + + for (i = 0; i < list->number; i++) + xsltPointerListFree((xsltPointerListPtr) list->items[i]); + + xsltPointerListFree(list); + data->exclResultNamespaces = NULL; + } + + if (data->extElemNamespaces != NULL) { + xsltPointerListPtr list = (xsltPointerListPtr) + data->extElemNamespaces; + int i; + + for (i = 0; i < list->number; i++) + xsltPointerListFree((xsltPointerListPtr) list->items[i]); + + xsltPointerListFree(list); + data->extElemNamespaces = NULL; + } + if (data->effectiveNs) { + xsltLREEffectiveNsNodesFree(data->effectiveNs); + data->effectiveNs = NULL; + } +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + xsltFreeNamespaceMap(data->nsMap); +#endif + xmlFree(data); +} + +static xsltPrincipalStylesheetDataPtr +xsltNewPrincipalStylesheetData(void) +{ + xsltPrincipalStylesheetDataPtr ret; + + ret = (xsltPrincipalStylesheetDataPtr) + xmlMalloc(sizeof(xsltPrincipalStylesheetData)); + if (ret == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewPrincipalStylesheetData: memory allocation failed.\n"); + return(NULL); + } + memset(ret, 0, sizeof(xsltPrincipalStylesheetData)); + + /* + * Global list of in-scope namespaces. + */ + ret->inScopeNamespaces = xsltPointerListCreate(-1); + if (ret->inScopeNamespaces == NULL) + goto internal_err; + /* + * Global list of excluded result ns-decls. + */ + ret->exclResultNamespaces = xsltPointerListCreate(-1); + if (ret->exclResultNamespaces == NULL) + goto internal_err; + /* + * Global list of extension instruction namespace names. + */ + ret->extElemNamespaces = xsltPointerListCreate(-1); + if (ret->extElemNamespaces == NULL) + goto internal_err; + + return(ret); + +internal_err: + + return(NULL); +} + +#endif + +/** + * xsltNewStylesheet: + * + * Create a new XSLT Stylesheet + * + * Returns the newly allocated xsltStylesheetPtr or NULL in case of error + */ +xsltStylesheetPtr +xsltNewStylesheet(void) { + xsltStylesheetPtr ret = NULL; + + ret = (xsltStylesheetPtr) xmlMalloc(sizeof(xsltStylesheet)); + if (ret == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewStylesheet : malloc failed\n"); + goto internal_err; + } + memset(ret, 0, sizeof(xsltStylesheet)); + + ret->omitXmlDeclaration = -1; + ret->standalone = -1; + ret->decimalFormat = xsltNewDecimalFormat(NULL); + ret->indent = -1; + ret->errors = 0; + ret->warnings = 0; + ret->exclPrefixNr = 0; + ret->exclPrefixMax = 0; + ret->exclPrefixTab = NULL; + ret->extInfos = NULL; + ret->extrasNr = 0; + ret->internalized = 1; + ret->literal_result = 0; + ret->dict = xmlDictCreate(); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "creating dictionary for stylesheet\n"); +#endif + + xsltInit(); + + return(ret); + +internal_err: + if (ret != NULL) + xsltFreeStylesheet(ret); + return(NULL); +} + +/** + * xsltAllocateExtra: + * @style: an XSLT stylesheet + * + * Allocate an extra runtime information slot statically while compiling + * the stylesheet and return its number + * + * Returns the number of the slot + */ +int +xsltAllocateExtra(xsltStylesheetPtr style) +{ + return(style->extrasNr++); +} + +/** + * xsltAllocateExtraCtxt: + * @ctxt: an XSLT transformation context + * + * Allocate an extra runtime information slot at run-time + * and return its number + * This make sure there is a slot ready in the transformation context + * + * Returns the number of the slot + */ +int +xsltAllocateExtraCtxt(xsltTransformContextPtr ctxt) +{ + if (ctxt->extrasNr >= ctxt->extrasMax) { + int i; + if (ctxt->extrasNr == 0) { + ctxt->extrasMax = 20; + ctxt->extras = (xsltRuntimeExtraPtr) + xmlMalloc(ctxt->extrasMax * sizeof(xsltRuntimeExtra)); + if (ctxt->extras == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltAllocateExtraCtxt: out of memory\n"); + ctxt->state = XSLT_STATE_ERROR; + return(0); + } + for (i = 0;i < ctxt->extrasMax;i++) { + ctxt->extras[i].info = NULL; + ctxt->extras[i].deallocate = NULL; + ctxt->extras[i].val.ptr = NULL; + } + + } else { + xsltRuntimeExtraPtr tmp; + + ctxt->extrasMax += 100; + tmp = (xsltRuntimeExtraPtr) xmlRealloc(ctxt->extras, + ctxt->extrasMax * sizeof(xsltRuntimeExtra)); + if (tmp == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltAllocateExtraCtxt: out of memory\n"); + ctxt->state = XSLT_STATE_ERROR; + return(0); + } + ctxt->extras = tmp; + for (i = ctxt->extrasNr;i < ctxt->extrasMax;i++) { + ctxt->extras[i].info = NULL; + ctxt->extras[i].deallocate = NULL; + ctxt->extras[i].val.ptr = NULL; + } + } + } + return(ctxt->extrasNr++); +} + +/** + * xsltFreeStylesheetList: + * @style: an XSLT stylesheet list + * + * Free up the memory allocated by the list @style + */ +static void +xsltFreeStylesheetList(xsltStylesheetPtr style) { + xsltStylesheetPtr next; + + while (style != NULL) { + next = style->next; + xsltFreeStylesheet(style); + style = next; + } +} + +/** + * xsltCleanupStylesheetTree: + * + * @doc: the document-node + * @node: the element where the stylesheet is rooted at + * + * Actually @node need not be the document-element, but + * currently Libxslt does not support embedeed stylesheets. + * + * Returns 0 if OK, -1 on API or internal errors. + */ +static int +xsltCleanupStylesheetTree(xmlDocPtr doc, xmlNodePtr rootElem) +{ +#if 0 /* TODO: Currently disabled, since probably not needed. */ + xmlNodePtr cur; + + if ((doc == NULL) || (rootElem == NULL) || + (rootElem->type != XML_ELEMENT_NODE) || + (doc != rootElem->doc)) + return(-1); + + /* + * Cleanup was suggested by Aleksey Sanin: + * Clear the PSVI field to avoid problems if the + * node-tree of the stylesheet is intended to be used for + * further processing by the user (e.g. for compiling it + * once again - although not recommended). + */ + + cur = rootElem; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + /* + * Clear the PSVI field. + */ + cur->psvi = NULL; + if (cur->children) { + cur = cur->children; + continue; + } + } + +leave_node: + if (cur == rootElem) + break; + if (cur->next != NULL) + cur = cur->next; + else { + cur = cur->parent; + if (cur == NULL) + break; + goto leave_node; + } + } +#endif /* #if 0 */ + return(0); +} + +/** + * xsltFreeStylesheet: + * @style: an XSLT stylesheet + * + * Free up the memory allocated by @style + */ +void +xsltFreeStylesheet(xsltStylesheetPtr style) +{ + if (style == NULL) + return; + +#ifdef XSLT_REFACTORED + /* + * Start with a cleanup of the main stylesheet's doc. + */ + if ((style->principal == style) && (style->doc)) + xsltCleanupStylesheetTree(style->doc, + xmlDocGetRootElement(style->doc)); +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + /* + * Restore changed ns-decls before freeing the document. + */ + if ((style->doc != NULL) && + XSLT_HAS_INTERNAL_NSMAP(style)) + { + xsltRestoreDocumentNamespaces(XSLT_GET_INTERNAL_NSMAP(style), + style->doc); + } +#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ +#else + /* + * Start with a cleanup of the main stylesheet's doc. + */ + if ((style->parent == NULL) && (style->doc)) + xsltCleanupStylesheetTree(style->doc, + xmlDocGetRootElement(style->doc)); +#endif /* XSLT_REFACTORED */ + + xsltFreeKeys(style); + xsltFreeExts(style); + xsltFreeTemplateHashes(style); + xsltFreeDecimalFormatList(style); + xsltFreeTemplateList(style->templates); + xsltFreeAttributeSetsHashes(style); + xsltFreeNamespaceAliasHashes(style); + xsltFreeStylePreComps(style); + /* + * Free documents of all included stylsheet modules of this + * stylesheet level. + */ + xsltFreeStyleDocuments(style); + /* + * TODO: Best time to shutdown extension stuff? + */ + xsltShutdownExts(style); + + if (style->variables != NULL) + xsltFreeStackElemList(style->variables); + if (style->cdataSection != NULL) + xmlHashFree(style->cdataSection, NULL); + if (style->stripSpaces != NULL) + xmlHashFree(style->stripSpaces, NULL); + if (style->nsHash != NULL) + xmlHashFree(style->nsHash, NULL); + if (style->exclPrefixTab != NULL) + xmlFree(style->exclPrefixTab); + if (style->method != NULL) + xmlFree(style->method); + if (style->methodURI != NULL) + xmlFree(style->methodURI); + if (style->version != NULL) + xmlFree(style->version); + if (style->encoding != NULL) + xmlFree(style->encoding); + if (style->doctypePublic != NULL) + xmlFree(style->doctypePublic); + if (style->doctypeSystem != NULL) + xmlFree(style->doctypeSystem); + if (style->mediaType != NULL) + xmlFree(style->mediaType); + if (style->attVTs) + xsltFreeAVTList(style->attVTs); + if (style->imports != NULL) + xsltFreeStylesheetList(style->imports); + +#ifdef XSLT_REFACTORED + /* + * If this is the principal stylesheet, then + * free its internal data. + */ + if (style->principal == style) { + if (style->principalData) { + xsltFreePrincipalStylesheetData(style->principalData); + style->principalData = NULL; + } + } +#endif + /* + * Better to free the main document of this stylesheet level + * at the end - so here. + */ + if (style->doc != NULL) { + xmlFreeDoc(style->doc); + } + +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "freeing dictionary from stylesheet\n"); +#endif + xmlDictFree(style->dict); + + memset(style, -1, sizeof(xsltStylesheet)); + xmlFree(style); +} + +/************************************************************************ + * * + * Parsing of an XSLT Stylesheet * + * * + ************************************************************************/ + +#ifdef XSLT_REFACTORED + /* + * This is now performed in an optimized way in xsltParseXSLTTemplate. + */ +#else +/** + * xsltGetInheritedNsList: + * @style: the stylesheet + * @template: the template + * @node: the current node + * + * Search all the namespace applying to a given element except the ones + * from excluded output prefixes currently in scope. Initialize the + * template inheritedNs list with it. + * + * Returns the number of entries found + */ +static int +xsltGetInheritedNsList(xsltStylesheetPtr style, + xsltTemplatePtr template, + xmlNodePtr node) +{ + xmlNsPtr cur; + xmlNsPtr *ret = NULL; + int nbns = 0; + int maxns = 10; + int i; + + if ((style == NULL) || (template == NULL) || (node == NULL) || + (template->inheritedNsNr != 0) || (template->inheritedNs != NULL)) + return(0); + while (node != NULL) { + if (node->type == XML_ELEMENT_NODE) { + cur = node->nsDef; + while (cur != NULL) { + if (xmlStrEqual(cur->href, XSLT_NAMESPACE)) + goto skip_ns; + + if ((cur->prefix != NULL) && + (xsltCheckExtPrefix(style, cur->prefix))) + goto skip_ns; + /* + * Check if this namespace was excluded. + * Note that at this point only the exclusions defined + * on the topmost stylesheet element are in the exclusion-list. + */ + for (i = 0;i < style->exclPrefixNr;i++) { + if (xmlStrEqual(cur->href, style->exclPrefixTab[i])) + goto skip_ns; + } + if (ret == NULL) { + ret = + (xmlNsPtr *) xmlMalloc((maxns + 1) * + sizeof(xmlNsPtr)); + if (ret == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltGetInheritedNsList : out of memory!\n"); + return(0); + } + ret[nbns] = NULL; + } + /* + * Skip shadowed namespace bindings. + */ + for (i = 0; i < nbns; i++) { + if ((cur->prefix == ret[i]->prefix) || + (xmlStrEqual(cur->prefix, ret[i]->prefix))) + break; + } + if (i >= nbns) { + if (nbns >= maxns) { + maxns *= 2; + ret = (xmlNsPtr *) xmlRealloc(ret, + (maxns + + 1) * + sizeof(xmlNsPtr)); + if (ret == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltGetInheritedNsList : realloc failed!\n"); + return(0); + } + } + ret[nbns++] = cur; + ret[nbns] = NULL; + } +skip_ns: + cur = cur->next; + } + } + node = node->parent; + } + if (nbns != 0) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "template has %d inherited namespaces\n", nbns); +#endif + template->inheritedNsNr = nbns; + template->inheritedNs = ret; + } + return (nbns); +} +#endif /* else of XSLT_REFACTORED */ + +/** + * xsltParseStylesheetOutput: + * @style: the XSLT stylesheet + * @cur: the "output" element + * + * parse an XSLT stylesheet output element and record + * information related to the stylesheet output + */ + +void +xsltParseStylesheetOutput(xsltStylesheetPtr style, xmlNodePtr cur) +{ + xmlChar *elements, + *prop; + xmlChar *element, + *end; + + if ((cur == NULL) || (style == NULL)) + return; + + prop = xmlGetNsProp(cur, (const xmlChar *) "version", NULL); + if (prop != NULL) { + if (style->version != NULL) + xmlFree(style->version); + style->version = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *) "encoding", NULL); + if (prop != NULL) { + if (style->encoding != NULL) + xmlFree(style->encoding); + style->encoding = prop; + } + + /* relaxed to support xt:document + * TODO KB: What does "relaxed to support xt:document" mean? + */ + prop = xmlGetNsProp(cur, (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; + + /* + * TODO: Don't use xsltGetQNameURI(). + */ + URI = xsltGetQNameURI(cur, &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(NULL, style, cur, + "invalid value for method: %s\n", prop); + if (style != NULL) style->warnings++; + } + } else { + style->method = prop; + style->methodURI = xmlStrdup(URI); + } + } + + prop = xmlGetNsProp(cur, (const xmlChar *) "doctype-system", NULL); + if (prop != NULL) { + if (style->doctypeSystem != NULL) + xmlFree(style->doctypeSystem); + style->doctypeSystem = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *) "doctype-public", NULL); + if (prop != NULL) { + if (style->doctypePublic != NULL) + xmlFree(style->doctypePublic); + style->doctypePublic = prop; + } + + prop = xmlGetNsProp(cur, (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(NULL, style, cur, + "invalid value for standalone: %s\n", prop); + style->errors++; + } + xmlFree(prop); + } + + prop = xmlGetNsProp(cur, (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(NULL, style, cur, + "invalid value for indent: %s\n", prop); + style->errors++; + } + xmlFree(prop); + } + + prop = xmlGetNsProp(cur, (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(NULL, style, cur, + "invalid value for omit-xml-declaration: %s\n", + prop); + style->errors++; + } + xmlFree(prop); + } + + elements = xmlGetNsProp(cur, (const xmlChar *) "cdata-section-elements", + NULL); + if (elements != NULL) { + if (style->cdataSection == NULL) + style->cdataSection = xmlHashCreate(10); + if (style->cdataSection == NULL) + return; + + element = elements; + while (*element != 0) { + while (IS_BLANK(*element)) + element++; + if (*element == 0) + break; + end = element; + while ((*end != 0) && (!IS_BLANK(*end))) + end++; + element = xmlStrndup(element, end - element); + if (element) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "add cdata section output element %s\n", + element); +#endif + if (xmlValidateQName(BAD_CAST element, 0) != 0) { + xsltTransformError(NULL, style, cur, + "Attribute 'cdata-section-elements': The value " + "'%s' is not a valid QName.\n", element); + xmlFree(element); + style->errors++; + } else { + const xmlChar *URI; + + /* + * TODO: Don't use xsltGetQNameURI(). + */ + URI = xsltGetQNameURI(cur, &element); + if (element == NULL) { + /* + * TODO: We'll report additionally an error + * via the stylesheet's error handling. + */ + xsltTransformError(NULL, style, cur, + "Attribute 'cdata-section-elements': The value " + "'%s' is not a valid QName.\n", element); + style->errors++; + } else { + xmlNsPtr ns; + + /* + * XSLT-1.0 "Each QName is expanded into an + * expanded-name using the namespace declarations in + * effect on the xsl:output element in which the QName + * occurs; if there is a default namespace, it is used + * for QNames that do not have a prefix" + * NOTE: Fix of bug #339570. + */ + if (URI == NULL) { + ns = xmlSearchNs(style->doc, cur, NULL); + if (ns != NULL) + URI = ns->href; + } + xmlHashAddEntry2(style->cdataSection, element, URI, + (void *) "cdata"); + xmlFree(element); + } + } + } + element = end; + } + xmlFree(elements); + } + + prop = xmlGetNsProp(cur, (const xmlChar *) "media-type", NULL); + if (prop != NULL) { + if (style->mediaType) + xmlFree(style->mediaType); + style->mediaType = prop; + } + if (cur->children != NULL) { + xsltParseContentError(style, cur->children); + } +} + +/** + * xsltParseStylesheetDecimalFormat: + * @style: the XSLT stylesheet + * @cur: the "decimal-format" element + * + * + * + * + * parse an XSLT stylesheet decimal-format element and + * and record the formatting characteristics + */ +static void +xsltParseStylesheetDecimalFormat(xsltStylesheetPtr style, xmlNodePtr cur) +{ + xmlChar *prop; + xsltDecimalFormatPtr format; + xsltDecimalFormatPtr iter; + + if ((cur == NULL) || (style == NULL)) + return; + + format = style->decimalFormat; + + prop = xmlGetNsProp(cur, BAD_CAST("name"), NULL); + if (prop != NULL) { + format = xsltDecimalFormatGetByName(style, prop); + if (format != NULL) { + xsltTransformError(NULL, style, cur, + "xsltParseStylestyleDecimalFormat: %s already exists\n", prop); + if (style != NULL) style->warnings++; + return; + } + format = xsltNewDecimalFormat(prop); + if (format == NULL) { + xsltTransformError(NULL, style, cur, + "xsltParseStylestyleDecimalFormat: failed creating new decimal-format\n"); + if (style != NULL) style->errors++; + return; + } + /* Append new decimal-format structure */ + for (iter = style->decimalFormat; iter->next; iter = iter->next) + ; + if (iter) + iter->next = format; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"decimal-separator", NULL); + if (prop != NULL) { + if (format->decimalPoint != NULL) xmlFree(format->decimalPoint); + format->decimalPoint = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"grouping-separator", NULL); + if (prop != NULL) { + if (format->grouping != NULL) xmlFree(format->grouping); + format->grouping = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"infinity", NULL); + if (prop != NULL) { + if (format->infinity != NULL) xmlFree(format->infinity); + format->infinity = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"minus-sign", NULL); + if (prop != NULL) { + if (format->minusSign != NULL) xmlFree(format->minusSign); + format->minusSign = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"NaN", NULL); + if (prop != NULL) { + if (format->noNumber != NULL) xmlFree(format->noNumber); + format->noNumber = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"percent", NULL); + if (prop != NULL) { + if (format->percent != NULL) xmlFree(format->percent); + format->percent = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"per-mille", NULL); + if (prop != NULL) { + if (format->permille != NULL) xmlFree(format->permille); + format->permille = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"zero-digit", NULL); + if (prop != NULL) { + if (format->zeroDigit != NULL) xmlFree(format->zeroDigit); + format->zeroDigit = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"digit", NULL); + if (prop != NULL) { + if (format->digit != NULL) xmlFree(format->digit); + format->digit = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"pattern-separator", NULL); + if (prop != NULL) { + if (format->patternSeparator != NULL) xmlFree(format->patternSeparator); + format->patternSeparator = prop; + } + if (cur->children != NULL) { + xsltParseContentError(style, cur->children); + } +} + +/** + * xsltParseStylesheetPreserveSpace: + * @style: the XSLT stylesheet + * @cur: the "preserve-space" element + * + * parse an XSLT stylesheet preserve-space element and record + * elements needing preserving + */ + +static void +xsltParseStylesheetPreserveSpace(xsltStylesheetPtr style, xmlNodePtr cur) { + xmlChar *elements; + xmlChar *element, *end; + + if ((cur == NULL) || (style == NULL)) + return; + + elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL); + if (elements == NULL) { + xsltTransformError(NULL, style, cur, + "xsltParseStylesheetPreserveSpace: missing elements attribute\n"); + if (style != NULL) style->warnings++; + return; + } + + if (style->stripSpaces == NULL) + style->stripSpaces = xmlHashCreate(10); + if (style->stripSpaces == NULL) + return; + + element = elements; + while (*element != 0) { + while (IS_BLANK(*element)) element++; + if (*element == 0) + break; + end = element; + while ((*end != 0) && (!IS_BLANK(*end))) end++; + element = xmlStrndup(element, end - element); + if (element) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "add preserved space element %s\n", element); +#endif + if (xmlStrEqual(element, (const xmlChar *)"*")) { + style->stripAll = -1; + } else { + const xmlChar *URI; + + /* + * TODO: Don't use xsltGetQNameURI(). + */ + URI = xsltGetQNameURI(cur, &element); + + xmlHashAddEntry2(style->stripSpaces, element, URI, + (xmlChar *) "preserve"); + } + xmlFree(element); + } + element = end; + } + xmlFree(elements); + if (cur->children != NULL) { + xsltParseContentError(style, cur->children); + } +} + +#ifdef XSLT_REFACTORED +#else +/** + * xsltParseStylesheetExtPrefix: + * @style: the XSLT stylesheet + * @template: the "extension-element-prefixes" prefix + * + * parse an XSLT stylesheet's "extension-element-prefix" attribute value + * and register the namespaces of extension instruction. + * SPEC "A namespace is designated as an extension namespace by using + * an extension-element-prefixes attribute on: + * 1) an xsl:stylesheet element + * 2) an xsl:extension-element-prefixes attribute on a + * literal result element + * 3) an extension instruction." + */ +static void +xsltParseStylesheetExtPrefix(xsltStylesheetPtr style, xmlNodePtr cur, + int isXsltElem) { + xmlChar *prefixes; + xmlChar *prefix, *end; + + if ((cur == NULL) || (style == NULL)) + return; + + if (isXsltElem) { + /* For xsl:stylesheet/xsl:transform. */ + prefixes = xmlGetNsProp(cur, + (const xmlChar *)"extension-element-prefixes", NULL); + } else { + /* For literal result elements and extension instructions. */ + prefixes = xmlGetNsProp(cur, + (const xmlChar *)"extension-element-prefixes", XSLT_NAMESPACE); + } + if (prefixes == NULL) { + return; + } + + prefix = prefixes; + while (*prefix != 0) { + while (IS_BLANK(*prefix)) prefix++; + if (*prefix == 0) + break; + end = prefix; + while ((*end != 0) && (!IS_BLANK(*end))) end++; + prefix = xmlStrndup(prefix, end - prefix); + if (prefix) { + xmlNsPtr ns; + + if (xmlStrEqual(prefix, (const xmlChar *)"#default")) + ns = xmlSearchNs(style->doc, cur, NULL); + else + ns = xmlSearchNs(style->doc, cur, prefix); + if (ns == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:extension-element-prefix : undefined namespace %s\n", + prefix); + if (style != NULL) style->warnings++; + } else { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "add extension prefix %s\n", prefix); +#endif + xsltRegisterExtPrefix(style, prefix, ns->href); + } + xmlFree(prefix); + } + prefix = end; + } + xmlFree(prefixes); +} +#endif /* else of XSLT_REFACTORED */ + +/** + * xsltParseStylesheetStripSpace: + * @style: the XSLT stylesheet + * @cur: the "strip-space" element + * + * parse an XSLT stylesheet's strip-space element and record + * the elements needing stripping + */ + +static void +xsltParseStylesheetStripSpace(xsltStylesheetPtr style, xmlNodePtr cur) { + xmlChar *elements; + xmlChar *element, *end; + + if ((cur == NULL) || (style == NULL)) + return; + + elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL); + if (elements == NULL) { + xsltTransformError(NULL, style, cur, + "xsltParseStylesheetStripSpace: missing elements attribute\n"); + if (style != NULL) style->warnings++; + return; + } + + if (style->stripSpaces == NULL) + style->stripSpaces = xmlHashCreate(10); + if (style->stripSpaces == NULL) + return; + + element = elements; + while (*element != 0) { + while (IS_BLANK(*element)) element++; + if (*element == 0) + break; + end = element; + while ((*end != 0) && (!IS_BLANK(*end))) end++; + element = xmlStrndup(element, end - element); + if (element) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "add stripped space element %s\n", element); +#endif + if (xmlStrEqual(element, (const xmlChar *)"*")) { + style->stripAll = 1; + } else { + const xmlChar *URI; + + /* + * TODO: Don't use xsltGetQNameURI(). + */ + URI = xsltGetQNameURI(cur, &element); + + xmlHashAddEntry2(style->stripSpaces, element, URI, + (xmlChar *) "strip"); + } + xmlFree(element); + } + element = end; + } + xmlFree(elements); + if (cur->children != NULL) { + xsltParseContentError(style, cur->children); + } +} + +#ifdef XSLT_REFACTORED +#else +/** + * xsltParseStylesheetExcludePrefix: + * @style: the XSLT stylesheet + * @cur: the current point in the stylesheet + * + * parse an XSLT stylesheet exclude prefix and record + * namespaces needing stripping + * + * Returns the number of Excluded prefixes added at that level + */ + +static int +xsltParseStylesheetExcludePrefix(xsltStylesheetPtr style, xmlNodePtr cur, + int isXsltElem) +{ + int nb = 0; + xmlChar *prefixes; + xmlChar *prefix, *end; + + if ((cur == NULL) || (style == NULL)) + return(0); + + if (isXsltElem) + prefixes = xmlGetNsProp(cur, + (const xmlChar *)"exclude-result-prefixes", NULL); + else + prefixes = xmlGetNsProp(cur, + (const xmlChar *)"exclude-result-prefixes", XSLT_NAMESPACE); + + if (prefixes == NULL) { + return(0); + } + + prefix = prefixes; + while (*prefix != 0) { + while (IS_BLANK(*prefix)) prefix++; + if (*prefix == 0) + break; + end = prefix; + while ((*end != 0) && (!IS_BLANK(*end))) end++; + prefix = xmlStrndup(prefix, end - prefix); + if (prefix) { + xmlNsPtr ns; + + if (xmlStrEqual(prefix, (const xmlChar *)"#default")) + ns = xmlSearchNs(style->doc, cur, NULL); + else + ns = xmlSearchNs(style->doc, cur, prefix); + if (ns == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:exclude-result-prefixes : undefined namespace %s\n", + prefix); + if (style != NULL) style->warnings++; + } else { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "exclude result prefix %s\n", prefix); +#endif + exclPrefixPush(style, (xmlChar *) ns->href); + nb++; + } + xmlFree(prefix); + } + prefix = end; + } + xmlFree(prefixes); + return(nb); +} +#endif /* else of XSLT_REFACTORED */ + +#ifdef XSLT_REFACTORED + +/* +* xsltTreeEnsureXMLDecl: +* @doc: the doc +* +* BIG NOTE: +* This was copy&pasted from Libxml2's xmlTreeEnsureXMLDecl() in "tree.c". +* Ensures that there is an XML namespace declaration on the doc. +* +* Returns the XML ns-struct or NULL on API and internal errors. +*/ +static xmlNsPtr +xsltTreeEnsureXMLDecl(xmlDocPtr doc) +{ + if (doc == NULL) + return (NULL); + if (doc->oldNs != NULL) + return (doc->oldNs); + { + xmlNsPtr ns; + ns = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); + if (ns == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltTreeEnsureXMLDecl: Failed to allocate " + "the XML namespace.\n"); + return (NULL); + } + memset(ns, 0, sizeof(xmlNs)); + ns->type = XML_LOCAL_NAMESPACE; + /* + * URGENT TODO: revisit this. + */ +#ifdef LIBXML_NAMESPACE_DICT + if (doc->dict) + ns->href = xmlDictLookup(doc->dict, XML_XML_NAMESPACE, -1); + else + ns->href = xmlStrdup(XML_XML_NAMESPACE); +#else + ns->href = xmlStrdup(XML_XML_NAMESPACE); +#endif + ns->prefix = xmlStrdup((const xmlChar *)"xml"); + doc->oldNs = ns; + return (ns); + } +} + +/* +* xsltTreeAcquireStoredNs: +* @doc: the doc +* @nsName: the namespace name +* @prefix: the prefix +* +* BIG NOTE: +* This was copy&pasted from Libxml2's xmlDOMWrapStoreNs() in "tree.c". +* Creates or reuses an xmlNs struct on doc->oldNs with +* the given prefix and namespace name. +* +* Returns the aquired ns struct or NULL in case of an API +* or internal error. +*/ +static xmlNsPtr +xsltTreeAcquireStoredNs(xmlDocPtr doc, + const xmlChar *nsName, + const xmlChar *prefix) +{ + xmlNsPtr ns; + + if (doc == NULL) + return (NULL); + if (doc->oldNs != NULL) + ns = doc->oldNs; + else + ns = xsltTreeEnsureXMLDecl(doc); + if (ns == NULL) + return (NULL); + if (ns->next != NULL) { + /* Reuse. */ + ns = ns->next; + while (ns != NULL) { + if ((ns->prefix == NULL) != (prefix == NULL)) { + /* NOP */ + } else if (prefix == NULL) { + if (xmlStrEqual(ns->href, nsName)) + return (ns); + } else { + if ((ns->prefix[0] == prefix[0]) && + xmlStrEqual(ns->prefix, prefix) && + xmlStrEqual(ns->href, nsName)) + return (ns); + + } + if (ns->next == NULL) + break; + ns = ns->next; + } + } + /* Create. */ + ns->next = xmlNewNs(NULL, nsName, prefix); + return (ns->next); +} + +/** + * xsltLREBuildEffectiveNs: + * + * Apply ns-aliasing on the namespace of the given @elem and + * its attributes. + */ +static int +xsltLREBuildEffectiveNs(xsltCompilerCtxtPtr cctxt, + xmlNodePtr elem) +{ + xmlNsPtr ns; + xsltNsAliasPtr alias; + + if ((cctxt == NULL) || (elem == NULL)) + return(-1); + if ((cctxt->nsAliases == NULL) || (! cctxt->hasNsAliases)) + return(0); + + alias = cctxt->nsAliases; + while (alias != NULL) { + if ( /* If both namespaces are NULL... */ + ( (elem->ns == NULL) && + ((alias->literalNs == NULL) || + (alias->literalNs->href == NULL)) ) || + /* ... or both namespace are equal */ + ( (elem->ns != NULL) && + (alias->literalNs != NULL) && + xmlStrEqual(elem->ns->href, alias->literalNs->href) ) ) + { + if ((alias->targetNs != NULL) && + (alias->targetNs->href != NULL)) + { + /* + * Convert namespace. + */ + if (elem->doc == alias->docOfTargetNs) { + /* + * This is the nice case: same docs. + * This will eventually assign a ns-decl which + * is shadowed, but this has no negative effect on + * the generation of the result tree. + */ + elem->ns = alias->targetNs; + } else { + /* + * This target xmlNs originates from a different + * stylesheet tree. Try to locate it in the + * in-scope namespaces. + * OPTIMIZE TODO: Use the compiler-node-info inScopeNs. + */ + ns = xmlSearchNs(elem->doc, elem, + alias->targetNs->prefix); + /* + * If no matching ns-decl found, then assign a + * ns-decl stored in xmlDoc. + */ + if ((ns == NULL) || + (! xmlStrEqual(ns->href, alias->targetNs->href))) + { + /* + * BIG NOTE: The use of xsltTreeAcquireStoredNs() + * is not very efficient, but currently I don't + * see an other way of *safely* changing a node's + * namespace, since the xmlNs struct in + * alias->targetNs might come from an other + * stylesheet tree. So we need to anchor it in the + * current document, without adding it to the tree, + * which would otherwise change the in-scope-ns + * semantic of the tree. + */ + ns = xsltTreeAcquireStoredNs(elem->doc, + alias->targetNs->href, + alias->targetNs->prefix); + + if (ns == NULL) { + xsltTransformError(NULL, cctxt->style, elem, + "Internal error in " + "xsltLREBuildEffectiveNs(): " + "failed to acquire a stored " + "ns-declaration.\n"); + cctxt->style->errors++; + return(-1); + + } + } + elem->ns = ns; + } + } else { + /* + * Move into or leave in the NULL namespace. + */ + elem->ns = NULL; + } + break; + } + alias = alias->next; + } + /* + * Same with attributes of literal result elements. + */ + if (elem->properties != NULL) { + xmlAttrPtr attr = elem->properties; + + while (attr != NULL) { + if (attr->ns == NULL) { + attr = attr->next; + continue; + } + alias = cctxt->nsAliases; + while (alias != NULL) { + if ( /* If both namespaces are NULL... */ + ( (elem->ns == NULL) && + ((alias->literalNs == NULL) || + (alias->literalNs->href == NULL)) ) || + /* ... or both namespace are equal */ + ( (elem->ns != NULL) && + (alias->literalNs != NULL) && + xmlStrEqual(elem->ns->href, alias->literalNs->href) ) ) + { + if ((alias->targetNs != NULL) && + (alias->targetNs->href != NULL)) + { + if (elem->doc == alias->docOfTargetNs) { + elem->ns = alias->targetNs; + } else { + ns = xmlSearchNs(elem->doc, elem, + alias->targetNs->prefix); + if ((ns == NULL) || + (! xmlStrEqual(ns->href, alias->targetNs->href))) + { + ns = xsltTreeAcquireStoredNs(elem->doc, + alias->targetNs->href, + alias->targetNs->prefix); + + if (ns == NULL) { + xsltTransformError(NULL, cctxt->style, elem, + "Internal error in " + "xsltLREBuildEffectiveNs(): " + "failed to acquire a stored " + "ns-declaration.\n"); + cctxt->style->errors++; + return(-1); + + } + } + elem->ns = ns; + } + } else { + /* + * Move into or leave in the NULL namespace. + */ + elem->ns = NULL; + } + break; + } + alias = alias->next; + } + + attr = attr->next; + } + } + return(0); +} + +/** + * xsltLREBuildEffectiveNsNodes: + * + * Computes the effective namespaces nodes for a literal result + * element. + * @effectiveNs is the set of effective ns-nodes + * on the literal result element, which will be added to the result + * element if not already existing in the result tree. + * This means that excluded namespaces (via exclude-result-prefixes, + * extension-element-prefixes and the XSLT namespace) not added + * to the set. + * Namespace-aliasing was applied on the @effectiveNs. + */ +static int +xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt, + xsltStyleItemLRElementInfoPtr item, + xmlNodePtr elem, + int isLRE) +{ + xmlNsPtr ns, tmpns; + xsltEffectiveNsPtr effNs, lastEffNs = NULL; + int i, j, holdByElem; + xsltPointerListPtr extElemNs = cctxt->inode->extElemNs; + xsltPointerListPtr exclResultNs = cctxt->inode->exclResultNs; + + if ((cctxt == NULL) || (cctxt->inode == NULL) || (elem == NULL) || + (item == NULL) || (item->effectiveNs != NULL)) + return(-1); + + if (item->inScopeNs == NULL) + return(0); + + extElemNs = cctxt->inode->extElemNs; + exclResultNs = cctxt->inode->exclResultNs; + + for (i = 0; i < item->inScopeNs->totalNumber; i++) { + ns = item->inScopeNs->list[i]; + /* + * Skip namespaces designated as excluded namespaces + * ------------------------------------------------- + * + * XSLT-20 TODO: In XSLT 2.0 we need to keep namespaces + * which are target namespaces of namespace-aliases + * regardless if designated as excluded. + * + * Exclude the XSLT namespace. + */ + if (xmlStrEqual(ns->href, XSLT_NAMESPACE)) + goto skip_ns; + + /* + * Apply namespace aliasing + * ------------------------ + * + * SPEC XSLT 2.0 + * "- A namespace node whose string value is a literal namespace + * URI is not copied to the result tree. + * - A namespace node whose string value is a target namespace URI + * is copied to the result tree, whether or not the URI + * identifies an excluded namespace." + * + * NOTE: The ns-aliasing machanism is non-cascading. + * (checked with Saxon, Xalan and MSXML .NET). + * URGENT TODO: is style->nsAliases the effective list of + * ns-aliases, or do we need to lookup the whole + * import-tree? + * TODO: Get rid of import-tree lookup. + */ + if (cctxt->hasNsAliases) { + xsltNsAliasPtr alias; + /* + * First check for being a target namespace. + */ + alias = cctxt->nsAliases; + do { + /* + * TODO: Is xmlns="" handled already? + */ + if ((alias->targetNs != NULL) && + (xmlStrEqual(alias->targetNs->href, ns->href))) + { + /* + * Recognized as a target namespace; use it regardless + * if excluded otherwise. + */ + goto add_effective_ns; + } + alias = alias->next; + } while (alias != NULL); + + alias = cctxt->nsAliases; + do { + /* + * TODO: Is xmlns="" handled already? + */ + if ((alias->literalNs != NULL) && + (xmlStrEqual(alias->literalNs->href, ns->href))) + { + /* + * Recognized as an namespace alias; do not use it. + */ + goto skip_ns; + } + alias = alias->next; + } while (alias != NULL); + } + + /* + * Exclude excluded result namespaces. + */ + if (exclResultNs) { + for (j = 0; j < exclResultNs->number; j++) + if (xmlStrEqual(ns->href, BAD_CAST exclResultNs->items[j])) + goto skip_ns; + } + /* + * Exclude extension-element namespaces. + */ + if (extElemNs) { + for (j = 0; j < extElemNs->number; j++) + if (xmlStrEqual(ns->href, BAD_CAST extElemNs->items[j])) + goto skip_ns; + } + +add_effective_ns: + /* + * OPTIMIZE TODO: This information may not be needed. + */ + if (isLRE && (elem->nsDef != NULL)) { + holdByElem = 0; + tmpns = elem->nsDef; + do { + if (tmpns == ns) { + holdByElem = 1; + break; + } + tmpns = tmpns->next; + } while (tmpns != NULL); + } else + holdByElem = 0; + + + /* + * Add the effective namespace declaration. + */ + effNs = (xsltEffectiveNsPtr) xmlMalloc(sizeof(xsltEffectiveNs)); + if (effNs == NULL) { + xsltTransformError(NULL, cctxt->style, elem, + "Internal error in xsltLREBuildEffectiveNs(): " + "failed to allocate memory.\n"); + cctxt->style->errors++; + return(-1); + } + if (cctxt->psData->effectiveNs == NULL) { + cctxt->psData->effectiveNs = effNs; + effNs->nextInStore = NULL; + } else { + effNs->nextInStore = cctxt->psData->effectiveNs; + cctxt->psData->effectiveNs = effNs; + } + + effNs->next = NULL; + effNs->prefix = ns->prefix; + effNs->nsName = ns->href; + effNs->holdByElem = holdByElem; + + if (lastEffNs == NULL) + item->effectiveNs = effNs; + else + lastEffNs->next = effNs; + lastEffNs = effNs; + +skip_ns: + {} + } + return(0); +} + + +/** + * xsltLREInfoCreate: + * + * @isLRE: indicates if the given @elem is a literal result element + * + * Creates a new info for a literal result element. + */ +static int +xsltLREInfoCreate(xsltCompilerCtxtPtr cctxt, + xmlNodePtr elem, + int isLRE) +{ + xsltStyleItemLRElementInfoPtr item; + + if ((cctxt == NULL) || (cctxt->inode == NULL)) + return(-1); + + item = (xsltStyleItemLRElementInfoPtr) + xmlMalloc(sizeof(xsltStyleItemLRElementInfo)); + if (item == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "Internal error in xsltLREInfoCreate(): " + "memory allocation failed.\n"); + cctxt->style->errors++; + return(-1); + } + memset(item, 0, sizeof(xsltStyleItemLRElementInfo)); + item->type = XSLT_FUNC_LITERAL_RESULT_ELEMENT; + /* + * Store it in the stylesheet. + */ + item->next = cctxt->style->preComps; + cctxt->style->preComps = (xsltElemPreCompPtr) item; + /* + * @inScopeNs are used for execution of XPath expressions + * in AVTs. + */ + item->inScopeNs = cctxt->inode->inScopeNs; + + if (elem) + xsltLREBuildEffectiveNsNodes(cctxt, item, elem, isLRE); + + cctxt->inode->litResElemInfo = item; + cctxt->inode->nsChanged = 0; + cctxt->maxLREs++; + return(0); +} + +/** + * xsltCompilerVarInfoPush: + * @cctxt: the compilation context + * + * Pushes a new var/param info onto the stack. + * + * Returns the acquired variable info. + */ +static xsltVarInfoPtr +xsltCompilerVarInfoPush(xsltCompilerCtxtPtr cctxt, + xmlNodePtr inst, + const xmlChar *name, + const xmlChar *nsName) +{ + xsltVarInfoPtr ivar; + + if ((cctxt->ivar != NULL) && (cctxt->ivar->next != NULL)) { + ivar = cctxt->ivar->next; + } else if ((cctxt->ivar == NULL) && (cctxt->ivars != NULL)) { + ivar = cctxt->ivars; + } else { + ivar = (xsltVarInfoPtr) xmlMalloc(sizeof(xsltVarInfo)); + if (ivar == NULL) { + xsltTransformError(NULL, cctxt->style, inst, + "xsltParseInScopeVarPush: xmlMalloc() failed!\n"); + cctxt->style->errors++; + return(NULL); + } + /* memset(retVar, 0, sizeof(xsltInScopeVar)); */ + if (cctxt->ivars == NULL) { + cctxt->ivars = ivar; + ivar->prev = NULL; + } else { + cctxt->ivar->next = ivar; + ivar->prev = cctxt->ivar; + } + cctxt->ivar = ivar; + ivar->next = NULL; + } + ivar->depth = cctxt->depth; + ivar->name = name; + ivar->nsName = nsName; + return(ivar); +} + +/** + * xsltCompilerVarInfoPop: + * @cctxt: the compilation context + * + * Pops all var/param infos from the stack, which + * have the current depth. + */ +static void +xsltCompilerVarInfoPop(xsltCompilerCtxtPtr cctxt) +{ + + while ((cctxt->ivar != NULL) && + (cctxt->ivar->depth > cctxt->depth)) + { + cctxt->ivar = cctxt->ivar->prev; + } +} + +/* +* xsltCompilerNodePush: +* +* @cctxt: the compilation context +* @node: the node to be pushed (this can also be the doc-node) +* +* Returns the current node info structure or +* NULL in case of an internal error. +*/ +xsltCompilerNodeInfoPtr +xsltCompilerNodePush(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) +{ + xsltCompilerNodeInfoPtr inode, iprev; + + if ((cctxt->inode != NULL) && (cctxt->inode->next != NULL)) { + inode = cctxt->inode->next; + } else if ((cctxt->inode == NULL) && (cctxt->inodeList != NULL)) { + inode = cctxt->inodeList; + } else { + /* + * Create a new node-info. + */ + inode = (xsltCompilerNodeInfoPtr) + xmlMalloc(sizeof(xsltCompilerNodeInfo)); + if (inode == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "xsltCompilerNodePush: malloc failed.\n"); + return(NULL); + } + memset(inode, 0, sizeof(xsltCompilerNodeInfo)); + if (cctxt->inodeList == NULL) + cctxt->inodeList = inode; + else { + cctxt->inodeLast->next = inode; + inode->prev = cctxt->inodeLast; + } + cctxt->inodeLast = inode; + cctxt->maxNodeInfos++; + if (cctxt->inode == NULL) { + cctxt->inode = inode; + /* + * Create an initial literal result element info for + * the root of the stylesheet. + */ + xsltLREInfoCreate(cctxt, NULL, 0); + } + } + cctxt->depth++; + cctxt->inode = inode; + /* + * REVISIT TODO: Keep the reset always complete. + * NOTE: Be carefull with the @node, since it might be + * a doc-node. + */ + inode->node = node; + inode->depth = cctxt->depth; + inode->templ = NULL; + inode->category = XSLT_ELEMENT_CATEGORY_XSLT; + inode->type = 0; + inode->item = NULL; + inode->curChildType = 0; + inode->extContentHandled = 0; + inode->isRoot = 0; + + if (inode->prev != NULL) { + iprev = inode->prev; + /* + * Inherit the following information: + * --------------------------------- + * + * In-scope namespaces + */ + inode->inScopeNs = iprev->inScopeNs; + /* + * Info for literal result elements + */ + inode->litResElemInfo = iprev->litResElemInfo; + inode->nsChanged = iprev->nsChanged; + /* + * Excluded result namespaces + */ + inode->exclResultNs = iprev->exclResultNs; + /* + * Extension instruction namespaces + */ + inode->extElemNs = iprev->extElemNs; + /* + * Whitespace preservation + */ + inode->preserveWhitespace = iprev->preserveWhitespace; + /* + * Forwards-compatible mode + */ + inode->forwardsCompat = iprev->forwardsCompat; + } else { + inode->inScopeNs = NULL; + inode->exclResultNs = NULL; + inode->extElemNs = NULL; + inode->preserveWhitespace = 0; + inode->forwardsCompat = 0; + } + + return(inode); +} + +/* +* xsltCompilerNodePop: +* +* @cctxt: the compilation context +* @node: the node to be pushed (this can also be the doc-node) +* +* Pops the current node info. +*/ +static void +xsltCompilerNodePop(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) +{ + if (cctxt->inode == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltCompilerNodePop: Top-node mismatch.\n"); + return; + } + /* + * NOTE: Be carefull with the @node, since it might be + * a doc-node. + */ + if (cctxt->inode->node != node) { + xmlGenericError(xmlGenericErrorContext, + "xsltCompilerNodePop: Node mismatch.\n"); + goto mismatch; + } + if (cctxt->inode->depth != cctxt->depth) { + xmlGenericError(xmlGenericErrorContext, + "xsltCompilerNodePop: Depth mismatch.\n"); + goto mismatch; + } + /* + * Pop information of variables. + */ + if ((cctxt->ivar) && (cctxt->ivar->depth > cctxt->depth)) + xsltCompilerVarInfoPop(cctxt); + + cctxt->depth--; + cctxt->inode = cctxt->inode->prev; + if (cctxt->inode != NULL) + cctxt->inode->curChildType = 0; + return; + +mismatch: + { + const xmlChar *nsName = NULL, *name = NULL; + const xmlChar *infnsName = NULL, *infname = NULL; + + if (node) { + if (node->type == XML_ELEMENT_NODE) { + name = node->name; + if (node->ns != NULL) + nsName = node->ns->href; + else + nsName = BAD_CAST ""; + } else { + name = BAD_CAST "#document"; + nsName = BAD_CAST ""; + } + } else + name = BAD_CAST "Not given"; + + if (cctxt->inode->node) { + if (node->type == XML_ELEMENT_NODE) { + infname = cctxt->inode->node->name; + if (cctxt->inode->node->ns != NULL) + infnsName = cctxt->inode->node->ns->href; + else + infnsName = BAD_CAST ""; + } else { + infname = BAD_CAST "#document"; + infnsName = BAD_CAST ""; + } + } else + infname = BAD_CAST "Not given"; + + + xmlGenericError(xmlGenericErrorContext, + "xsltCompilerNodePop: Given : '%s' URI '%s'\n", + name, nsName); + xmlGenericError(xmlGenericErrorContext, + "xsltCompilerNodePop: Expected: '%s' URI '%s'\n", + infname, infnsName); + } +} + +/* +* xsltCompilerBuildInScopeNsList: +* +* Create and store the list of in-scope namespaces for the given +* node in the stylesheet. If there are no changes in the in-scope +* namespaces then the last ns-info of the ancestor axis will be returned. +* Compilation-time only. +* +* Returns the ns-info or NULL if there are no namespaces in scope. +*/ +static xsltNsListContainerPtr +xsltCompilerBuildInScopeNsList(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) +{ + xsltNsListContainerPtr nsi = NULL; + xmlNsPtr *list = NULL, ns; + int i, maxns = 5; + /* + * Create a new ns-list for this position in the node-tree. + * xmlGetNsList() will return NULL, if there are no ns-decls in the + * tree. Note that the ns-decl for the XML namespace is not added + * to the resulting list; the XPath module handles the XML namespace + * internally. + */ + while (node != NULL) { + if (node->type == XML_ELEMENT_NODE) { + ns = node->nsDef; + while (ns != NULL) { + if (nsi == NULL) { + nsi = (xsltNsListContainerPtr) + xmlMalloc(sizeof(xsltNsListContainer)); + if (nsi == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "xsltCompilerBuildInScopeNsList: " + "malloc failed!\n"); + goto internal_err; + } + memset(nsi, 0, sizeof(xsltNsListContainer)); + nsi->list = + (xmlNsPtr *) xmlMalloc(maxns * sizeof(xmlNsPtr)); + if (nsi->list == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "xsltCompilerBuildInScopeNsList: " + "malloc failed!\n"); + goto internal_err; + } + nsi->list[0] = NULL; + } + /* + * Skip shadowed namespace bindings. + */ + for (i = 0; i < nsi->totalNumber; i++) { + if ((ns->prefix == nsi->list[i]->prefix) || + (xmlStrEqual(ns->prefix, nsi->list[i]->prefix))) + break; + } + if (i >= nsi->totalNumber) { + if (nsi->totalNumber +1 >= maxns) { + maxns *= 2; + nsi->list = + (xmlNsPtr *) xmlRealloc(nsi->list, + maxns * sizeof(xmlNsPtr)); + if (nsi->list == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "xsltCompilerBuildInScopeNsList: " + "realloc failed!\n"); + goto internal_err; + } + } + nsi->list[nsi->totalNumber++] = ns; + nsi->list[nsi->totalNumber] = NULL; + } + + ns = ns->next; + } + } + node = node->parent; + } + if (nsi == NULL) + return(NULL); + /* + * Move the default namespace to last position. + */ + nsi->xpathNumber = nsi->totalNumber; + for (i = 0; i < nsi->totalNumber; i++) { + if (nsi->list[i]->prefix == NULL) { + ns = nsi->list[i]; + nsi->list[i] = nsi->list[nsi->totalNumber-1]; + nsi->list[nsi->totalNumber-1] = ns; + nsi->xpathNumber--; + break; + } + } + /* + * Store the ns-list in the stylesheet. + */ + if (xsltPointerListAddSize( + (xsltPointerListPtr)cctxt->psData->inScopeNamespaces, + (void *) nsi, 5) == -1) + { + xmlFree(nsi); + nsi = NULL; + xsltTransformError(NULL, cctxt->style, NULL, + "xsltCompilerBuildInScopeNsList: failed to add ns-info.\n"); + goto internal_err; + } + /* + * Notify of change in status wrt namespaces. + */ + if (cctxt->inode != NULL) + cctxt->inode->nsChanged = 1; + + return(nsi); + +internal_err: + if (list != NULL) + xmlFree(list); + cctxt->style->errors++; + return(NULL); +} + +static int +xsltParseNsPrefixList(xsltCompilerCtxtPtr cctxt, + xsltPointerListPtr list, + xmlNodePtr node, + const xmlChar *value) +{ + xmlChar *cur, *end; + xmlNsPtr ns; + + if ((cctxt == NULL) || (value == NULL) || (list == NULL)) + return(-1); + + list->number = 0; + + cur = (xmlChar *) value; + while (*cur != 0) { + while (IS_BLANK(*cur)) cur++; + if (*cur == 0) + break; + end = cur; + while ((*end != 0) && (!IS_BLANK(*end))) end++; + cur = xmlStrndup(cur, end - cur); + if (cur == NULL) { + cur = end; + continue; + } + /* + * TODO: Export and use xmlSearchNsByPrefixStrict() + * in Libxml2, tree.c, since xmlSearchNs() is in most + * cases not efficient and in some cases not correct. + * + * XSLT-2 TODO: XSLT 2.0 allows an additional "#all" value. + */ + if ((cur[0] == '#') && + xmlStrEqual(cur, (const xmlChar *)"#default")) + ns = xmlSearchNs(cctxt->style->doc, node, NULL); + else + ns = xmlSearchNs(cctxt->style->doc, node, cur); + + if (ns == NULL) { + /* + * TODO: Better to report the attr-node, otherwise + * the user won't know which attribute was invalid. + */ + xsltTransformError(NULL, cctxt->style, node, + "No namespace binding in scope for prefix '%s'.\n", cur); + /* + * XSLT-1.0: "It is an error if there is no namespace + * bound to the prefix on the element bearing the + * exclude-result-prefixes or xsl:exclude-result-prefixes + * attribute." + */ + cctxt->style->errors++; + } else { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "resolved prefix '%s'\n", cur); +#endif + /* + * Note that we put the namespace name into the dict. + */ + if (xsltPointerListAddSize(list, + (void *) xmlDictLookup(cctxt->style->dict, + ns->href, -1), 5) == -1) + { + xmlFree(cur); + goto internal_err; + } + } + xmlFree(cur); + + cur = end; + } + return(0); + +internal_err: + cctxt->style->errors++; + return(-1); +} + +/** + * xsltCompilerUtilsCreateMergedList: + * @dest: the destination list (optional) + * @first: the first list + * @second: the second list (optional) + * + * Appends the content of @second to @first into @destination. + * If @destination is NULL a new list will be created. + * + * Returns the merged list of items or NULL if there's nothing to merge. + */ +static xsltPointerListPtr +xsltCompilerUtilsCreateMergedList(xsltPointerListPtr first, + xsltPointerListPtr second) +{ + xsltPointerListPtr ret; + size_t num; + + if (first) + num = first->number; + else + num = 0; + if (second) + num += second->number; + if (num == 0) + return(NULL); + ret = xsltPointerListCreate(num); + if (ret == NULL) + return(NULL); + /* + * Copy contents. + */ + if ((first != NULL) && (first->number != 0)) { + memcpy(ret->items, first->items, + first->number * sizeof(void *)); + if ((second != NULL) && (second->number != 0)) + memcpy(ret->items + first->number, second->items, + second->number * sizeof(void *)); + } else if ((second != NULL) && (second->number != 0)) + memcpy(ret->items, (void *) second->items, + second->number * sizeof(void *)); + ret->number = num; + return(ret); +} + +/* +* xsltParseExclResultPrefixes: +* +* Create and store the list of in-scope namespaces for the given +* node in the stylesheet. If there are no changes in the in-scope +* namespaces then the last ns-info of the ancestor axis will be returned. +* Compilation-time only. +* +* Returns the ns-info or NULL if there are no namespaces in scope. +*/ +static xsltPointerListPtr +xsltParseExclResultPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, + xsltPointerListPtr def, + int instrCategory) +{ + xsltPointerListPtr list = NULL; + xmlChar *value; + xmlAttrPtr attr; + + if ((cctxt == NULL) || (node == NULL)) + return(NULL); + + if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT) + attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes", NULL); + else + attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes", + XSLT_NAMESPACE); + if (attr == NULL) + return(def); + + if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) { + /* + * Mark the XSLT attr. + */ + attr->psvi = (void *) xsltXSLTAttrMarker; + } + + if ((attr->children != NULL) && + (attr->children->content != NULL)) + value = attr->children->content; + else { + xsltTransformError(NULL, cctxt->style, node, + "Attribute 'exclude-result-prefixes': Invalid value.\n"); + cctxt->style->errors++; + return(def); + } + + if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node, + BAD_CAST value) != 0) + goto exit; + if (cctxt->tmpList->number == 0) + goto exit; + /* + * Merge the list with the inherited list. + */ + list = xsltCompilerUtilsCreateMergedList(def, cctxt->tmpList); + if (list == NULL) + goto exit; + /* + * Store the list in the stylesheet/compiler context. + */ + if (xsltPointerListAddSize( + cctxt->psData->exclResultNamespaces, list, 5) == -1) + { + xsltPointerListFree(list); + list = NULL; + goto exit; + } + /* + * Notify of change in status wrt namespaces. + */ + if (cctxt->inode != NULL) + cctxt->inode->nsChanged = 1; + +exit: + if (list != NULL) + return(list); + else + return(def); +} + +/* +* xsltParseExtElemPrefixes: +* +* Create and store the list of in-scope namespaces for the given +* node in the stylesheet. If there are no changes in the in-scope +* namespaces then the last ns-info of the ancestor axis will be returned. +* Compilation-time only. +* +* Returns the ns-info or NULL if there are no namespaces in scope. +*/ +static xsltPointerListPtr +xsltParseExtElemPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, + xsltPointerListPtr def, + int instrCategory) +{ + xsltPointerListPtr list = NULL; + xmlAttrPtr attr; + xmlChar *value; + int i; + + if ((cctxt == NULL) || (node == NULL)) + return(NULL); + + if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT) + attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes", NULL); + else + attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes", + XSLT_NAMESPACE); + if (attr == NULL) + return(def); + + if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) { + /* + * Mark the XSLT attr. + */ + attr->psvi = (void *) xsltXSLTAttrMarker; + } + + if ((attr->children != NULL) && + (attr->children->content != NULL)) + value = attr->children->content; + else { + xsltTransformError(NULL, cctxt->style, node, + "Attribute 'extension-element-prefixes': Invalid value.\n"); + cctxt->style->errors++; + return(def); + } + + + if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node, + BAD_CAST value) != 0) + goto exit; + + if (cctxt->tmpList->number == 0) + goto exit; + /* + * REVISIT: Register the extension namespaces. + */ + for (i = 0; i < cctxt->tmpList->number; i++) + xsltRegisterExtPrefix(cctxt->style, NULL, + BAD_CAST cctxt->tmpList->items[i]); + /* + * Merge the list with the inherited list. + */ + list = xsltCompilerUtilsCreateMergedList(def, cctxt->tmpList); + if (list == NULL) + goto exit; + /* + * Store the list in the stylesheet. + */ + if (xsltPointerListAddSize( + cctxt->psData->extElemNamespaces, list, 5) == -1) + { + xsltPointerListFree(list); + list = NULL; + goto exit; + } + /* + * Notify of change in status wrt namespaces. + */ + if (cctxt->inode != NULL) + cctxt->inode->nsChanged = 1; + +exit: + if (list != NULL) + return(list); + else + return(def); +} + +/* +* xsltParseAttrXSLTVersion: +* +* @cctxt: the compilation context +* @node: the element-node +* @isXsltElem: whether this is an XSLT element +* +* Parses the attribute xsl:version. +* +* Returns 1 if there was such an attribute, 0 if not and +* -1 if an internal or API error occured. +*/ +static int +xsltParseAttrXSLTVersion(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, + int instrCategory) +{ + xmlChar *value; + xmlAttrPtr attr; + + if ((cctxt == NULL) || (node == NULL)) + return(-1); + + if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT) + attr = xmlHasNsProp(node, BAD_CAST "version", NULL); + else + attr = xmlHasNsProp(node, BAD_CAST "version", XSLT_NAMESPACE); + + if (attr == NULL) + return(0); + + attr->psvi = (void *) xsltXSLTAttrMarker; + + if ((attr->children != NULL) && + (attr->children->content != NULL)) + value = attr->children->content; + else { + xsltTransformError(NULL, cctxt->style, node, + "Attribute 'version': Invalid value.\n"); + cctxt->style->errors++; + return(1); + } + + if (! xmlStrEqual(value, (const xmlChar *)"1.0")) { + cctxt->inode->forwardsCompat = 1; + /* + * TODO: To what extent do we support the + * forwards-compatible mode? + */ + /* + * Report this only once per compilation episode. + */ + if (! cctxt->hasForwardsCompat) { + cctxt->hasForwardsCompat = 1; + cctxt->errSeverity = XSLT_ERROR_SEVERITY_WARNING; + xsltTransformError(NULL, cctxt->style, node, + "Warning: the attribute xsl:version specifies a value " + "different from '1.0'. Switching to forwards-compatible " + "mode. Only features of XSLT 1.0 are supported by this " + "processor.\n"); + cctxt->style->warnings++; + cctxt->errSeverity = XSLT_ERROR_SEVERITY_ERROR; + } + } else { + cctxt->inode->forwardsCompat = 0; + } + + if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) { + /* + * Set a marker on XSLT attributes. + */ + attr->psvi = (void *) xsltXSLTAttrMarker; + } + return(1); +} + +static int +xsltParsePreprocessStylesheetTree(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) +{ + xmlNodePtr deleteNode, cur, txt, textNode = NULL; + xmlDocPtr doc; + xsltStylesheetPtr style; + int internalize = 0, findSpaceAttr; + int xsltStylesheetElemDepth; + xmlAttrPtr attr; + xmlChar *value; + const xmlChar *name, *nsNameXSLT = NULL; + int strictWhitespace, inXSLText = 0; +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + xsltNsMapPtr nsMapItem; +#endif + + if ((cctxt == NULL) || (cctxt->style == NULL) || + (node == NULL) || (node->type != XML_ELEMENT_NODE)) + return(-1); + + doc = node->doc; + if (doc == NULL) + goto internal_err; + + style = cctxt->style; + if ((style->dict != NULL) && (doc->dict == style->dict)) + internalize = 1; + else + style->internalized = 0; + + /* + * Init value of xml:space. Since this might be an embedded + * stylesheet, this is needed to be performed on the element + * where the stylesheet is rooted at, taking xml:space of + * ancestors into account. + */ + if (! cctxt->simplified) + xsltStylesheetElemDepth = cctxt->depth +1; + else + xsltStylesheetElemDepth = 0; + + if (xmlNodeGetSpacePreserve(node) != 1) + cctxt->inode->preserveWhitespace = 0; + else + cctxt->inode->preserveWhitespace = 1; + + /* + * Eval if we should keep the old incorrect behaviour. + */ + strictWhitespace = (cctxt->strict != 0) ? 1 : 0; + + nsNameXSLT = xsltConstNamespaceNameXSLT; + + deleteNode = NULL; + cur = node; + while (cur != NULL) { + if (deleteNode != NULL) { + +#ifdef WITH_XSLT_DEBUG_BLANKS + xsltGenericDebug(xsltGenericDebugContext, + "xsltParsePreprocessStylesheetTree: removing node\n"); +#endif + xmlUnlinkNode(deleteNode); + xmlFreeNode(deleteNode); + deleteNode = NULL; + } + if (cur->type == XML_ELEMENT_NODE) { + + /* + * Clear the PSVI field. + */ + cur->psvi = NULL; + + xsltCompilerNodePush(cctxt, cur); + + inXSLText = 0; + textNode = NULL; + findSpaceAttr = 1; + cctxt->inode->stripWhitespace = 0; + /* + * TODO: I'd love to use a string pointer comparison here :-/ + */ + if (IS_XSLT_ELEM(cur)) { +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + if (cur->ns->href != nsNameXSLT) { + nsMapItem = xsltNewNamespaceMapItem(cctxt, + doc, cur->ns, cur); + if (nsMapItem == NULL) + goto internal_err; + cur->ns->href = nsNameXSLT; + } +#endif + + if (cur->name == NULL) + goto process_attributes; + /* + * Mark the XSLT element for later recognition. + * TODO: Using the marker is still too dangerous, since if + * the parsing mechanism leaves out an XSLT element, then + * this might hit the transformation-mechanism, which + * will break if it doesn't expect such a marker. + */ + /* cur->psvi = (void *) xsltXSLTElemMarker; */ + + /* + * XSLT 2.0: "Any whitespace text node whose parent is + * one of the following elements is removed from the " + * tree, regardless of any xml:space attributes:..." + * xsl:apply-imports, + * xsl:apply-templates, + * xsl:attribute-set, + * xsl:call-template, + * xsl:choose, + * xsl:stylesheet, xsl:transform. + * XSLT 2.0: xsl:analyze-string, + * xsl:character-map, + * xsl:next-match + * + * TODO: I'd love to use a string pointer comparison here :-/ + */ + name = cur->name; + switch (*name) { + case 't': + if ((name[0] == 't') && (name[1] == 'e') && + (name[2] == 'x') && (name[3] == 't') && + (name[4] == 0)) + { + /* + * Process the xsl:text element. + * ---------------------------- + * Mark it for later recognition. + */ + cur->psvi = (void *) xsltXSLTTextMarker; + /* + * For stylesheets, the set of + * whitespace-preserving element names + * consists of just xsl:text. + */ + findSpaceAttr = 0; + cctxt->inode->preserveWhitespace = 1; + inXSLText = 1; + } + break; + case 'c': + if (xmlStrEqual(name, BAD_CAST "choose") || + xmlStrEqual(name, BAD_CAST "call-template")) + cctxt->inode->stripWhitespace = 1; + break; + case 'a': + if (xmlStrEqual(name, BAD_CAST "apply-templates") || + xmlStrEqual(name, BAD_CAST "apply-imports") || + xmlStrEqual(name, BAD_CAST "attribute-set")) + + cctxt->inode->stripWhitespace = 1; + break; + default: + if (xsltStylesheetElemDepth == cctxt->depth) { + /* + * This is a xsl:stylesheet/xsl:transform. + */ + cctxt->inode->stripWhitespace = 1; + break; + } + + if ((cur->prev != NULL) && + (cur->prev->type == XML_TEXT_NODE)) + { + /* + * XSLT 2.0 : "Any whitespace text node whose + * following-sibling node is an xsl:param or + * xsl:sort element is removed from the tree, + * regardless of any xml:space attributes." + */ + if (((*name == 'p') || (*name == 's')) && + (xmlStrEqual(name, BAD_CAST "param") || + xmlStrEqual(name, BAD_CAST "sort"))) + { + do { + if (IS_BLANK_NODE(cur->prev)) { + txt = cur->prev; + xmlUnlinkNode(txt); + xmlFreeNode(txt); + } else { + /* + * This will result in a content + * error, when hitting the parsing + * functions. + */ + break; + } + } while (cur->prev); + } + } + break; + } + } + +process_attributes: + /* + * Process attributes. + * ------------------ + */ + if (cur->properties != NULL) { + if (cur->children == NULL) + findSpaceAttr = 0; + attr = cur->properties; + do { +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + if ((attr->ns) && (attr->ns->href != nsNameXSLT) && + xmlStrEqual(attr->ns->href, nsNameXSLT)) + { + nsMapItem = xsltNewNamespaceMapItem(cctxt, + doc, attr->ns, cur); + if (nsMapItem == NULL) + goto internal_err; + attr->ns->href = nsNameXSLT; + } +#endif + if (internalize) { + /* + * Internalize the attribute's value; the goal is to + * speed up operations and minimize used space by + * compiled stylesheets. + */ + txt = attr->children; + /* + * NOTE that this assumes only one + * text-node in the attribute's content. + */ + if ((txt != NULL) && (txt->content != NULL) && + (!xmlDictOwns(style->dict, txt->content))) + { + value = (xmlChar *) xmlDictLookup(style->dict, + txt->content, -1); + xmlNodeSetContent(txt, NULL); + txt->content = value; + } + } + /* + * Process xml:space attributes. + * ---------------------------- + */ + if ((findSpaceAttr != 0) && + (attr->ns != NULL) && + (attr->name != NULL) && + (attr->name[0] == 's') && + (attr->ns->prefix != NULL) && + (attr->ns->prefix[0] == 'x') && + (attr->ns->prefix[1] == 'm') && + (attr->ns->prefix[2] == 'l') && + (attr->ns->prefix[3] == 0)) + { + value = xmlGetNsProp(cur, BAD_CAST "space", + XML_XML_NAMESPACE); + if (value != NULL) { + if (xmlStrEqual(value, BAD_CAST "preserve")) { + cctxt->inode->preserveWhitespace = 1; + } else if (xmlStrEqual(value, BAD_CAST "default")) { + cctxt->inode->preserveWhitespace = 0; + } else { + /* Invalid value for xml:space. */ + xsltTransformError(NULL, style, cur, + "Attribute xml:space: Invalid value.\n"); + cctxt->style->warnings++; + } + findSpaceAttr = 0; + xmlFree(value); + } + + } + attr = attr->next; + } while (attr != NULL); + } + /* + * We'll descend into the children of element nodes only. + */ + if (cur->children != NULL) { + cur = cur->children; + continue; + } + } else if ((cur->type == XML_TEXT_NODE) || + (cur->type == XML_CDATA_SECTION_NODE)) + { + /* + * Merge adjacent text/CDATA-section-nodes + * --------------------------------------- + * In order to avoid breaking of existing stylesheets, + * if the old behaviour is wanted (strictWhitespace == 0), + * then we *won't* merge adjacent text-nodes + * (except in xsl:text); this will ensure that whitespace-only + * text nodes are (incorrectly) not stripped in some cases. + * + * Example: : zoo + * Corrent (strict) result: zoo + * Incorrect (old) result : zoo + * + * NOTE that we *will* merge adjacent text-nodes if + * they are in xsl:text. + * Example, the following: + * zoo + * will result in both cases in: + * zoo + */ + cur->type = XML_TEXT_NODE; + if ((strictWhitespace != 0) || (inXSLText != 0)) { + /* + * New behaviour; merge nodes. + */ + if (textNode == NULL) + textNode = cur; + else { + if (cur->content != NULL) + xmlNodeAddContent(textNode, cur->content); + deleteNode = cur; + } + if ((cur->next == NULL) || + (cur->next->type == XML_ELEMENT_NODE)) + goto end_of_text; + else + goto next_sibling; + } else { + /* + * Old behaviour. + */ + if (textNode == NULL) + textNode = cur; + goto end_of_text; + } + } else if ((cur->type == XML_COMMENT_NODE) || + (cur->type == XML_PI_NODE)) + { + /* + * Remove processing instructions and comments. + */ + deleteNode = cur; + if ((cur->next == NULL) || + (cur->next->type == XML_ELEMENT_NODE)) + goto end_of_text; + else + goto next_sibling; + } else { + textNode = NULL; + /* + * Invalid node-type for this data-model. + */ + xsltTransformError(NULL, style, cur, + "Invalid type of node for the XSLT data model.\n"); + cctxt->style->errors++; + goto next_sibling; + } + +end_of_text: + if (textNode) { + value = textNode->content; + /* + * At this point all adjacent text/CDATA-section nodes + * have been merged. + * + * Strip whitespace-only text-nodes. + * (cctxt->inode->stripWhitespace) + */ + if ((value == NULL) || (*value == 0) || + (((cctxt->inode->stripWhitespace) || + (! cctxt->inode->preserveWhitespace)) && + IS_BLANK(*value) && + xsltIsBlank(value))) + { + if (textNode != cur) { + xmlUnlinkNode(textNode); + xmlFreeNode(textNode); + } else + deleteNode = textNode; + textNode = NULL; + goto next_sibling; + } + /* + * Convert CDATA-section nodes to text-nodes. + * TODO: Can this produce problems? + */ + if (textNode->type != XML_TEXT_NODE) { + textNode->type = XML_TEXT_NODE; + textNode->name = xmlStringText; + } + if (internalize && + (textNode->content != NULL) && + (!xmlDictOwns(style->dict, textNode->content))) + { + /* + * Internalize the string. + */ + value = (xmlChar *) xmlDictLookup(style->dict, + textNode->content, -1); + xmlNodeSetContent(textNode, NULL); + textNode->content = value; + } + textNode = NULL; + /* + * Note that "disable-output-escaping" of the xsl:text + * element will be applied at a later level, when + * XSLT elements are processed. + */ + } + +next_sibling: + if (cur->type == XML_ELEMENT_NODE) { + xsltCompilerNodePop(cctxt, cur); + } + if (cur == node) + break; + if (cur->next != NULL) { + cur = cur->next; + } else { + cur = cur->parent; + inXSLText = 0; + goto next_sibling; + }; + } + if (deleteNode != NULL) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParsePreprocessStylesheetTree: removing node\n"); +#endif + xmlUnlinkNode(deleteNode); + xmlFreeNode(deleteNode); + } + return(0); + +internal_err: + return(-1); +} + +#endif /* XSLT_REFACTORED */ + +#ifdef XSLT_REFACTORED +#else +static void +xsltPrecomputeStylesheet(xsltStylesheetPtr style, xmlNodePtr cur) +{ + xmlNodePtr deleteNode; + int internalize = 0; + + if ((style == NULL) || (cur == NULL)) + return; + + if ((cur->doc != NULL) && (style->dict != NULL) && + (cur->doc->dict == style->dict)) + internalize = 1; + else + style->internalized = 0; + /* + * This content comes from the stylesheet + * For stylesheets, the set of whitespace-preserving + * element names consists of just xsl:text. + */ + deleteNode = NULL; + while (cur != NULL) { + if (deleteNode != NULL) { +#ifdef WITH_XSLT_DEBUG_BLANKS + xsltGenericDebug(xsltGenericDebugContext, + "xsltPrecomputeStylesheet: removing ignorable blank node\n"); +#endif + xmlUnlinkNode(deleteNode); + xmlFreeNode(deleteNode); + deleteNode = NULL; + } + if (cur->type == XML_ELEMENT_NODE) { + int exclPrefixes; + /* + * Internalize attributes values. + */ + if ((internalize) && (cur->properties != NULL)) { + xmlAttrPtr attr = cur->properties; + xmlNodePtr txt; + + while (attr != NULL) { + txt = attr->children; + if ((txt != NULL) && (txt->type == XML_TEXT_NODE) && + (txt->content != NULL) && + (!xmlDictOwns(style->dict, txt->content))) + { + xmlChar *tmp; + + /* + * internalize the text string, goal is to speed + * up operations and minimize used space by compiled + * stylesheets. + */ + tmp = (xmlChar *) xmlDictLookup(style->dict, + txt->content, -1); + if (tmp != txt->content) { + xmlNodeSetContent(txt, NULL); + txt->content = tmp; + } + } + attr = attr->next; + } + } + if (IS_XSLT_ELEM(cur)) { + exclPrefixes = 0; + xsltStylePreCompute(style, cur); + if (IS_XSLT_NAME(cur, "text")) { + for (;exclPrefixes > 0;exclPrefixes--) + exclPrefixPop(style); + goto skip_children; + } + } else { + exclPrefixes = xsltParseStylesheetExcludePrefix(style, cur, 0); + } + + if ((cur->nsDef != NULL) && (style->exclPrefixNr > 0)) { + xmlNsPtr ns = cur->nsDef, prev = NULL, next; + xmlNodePtr root = NULL; + int i, moved; + + root = xmlDocGetRootElement(cur->doc); + if ((root != NULL) && (root != cur)) { + while (ns != NULL) { + moved = 0; + next = ns->next; + for (i = 0;i < style->exclPrefixNr;i++) { + if ((ns->prefix != NULL) && + (xmlStrEqual(ns->href, + style->exclPrefixTab[i]))) { + /* + * Move the namespace definition on the root + * element to avoid duplicating it without + * loosing it. + */ + if (prev == NULL) { + cur->nsDef = ns->next; + } else { + prev->next = ns->next; + } + ns->next = root->nsDef; + root->nsDef = ns; + moved = 1; + break; + } + } + if (moved == 0) + prev = ns; + ns = next; + } + } + } + /* + * If we have prefixes locally, recurse and pop them up when + * going back + */ + if (exclPrefixes > 0) { + xsltPrecomputeStylesheet(style, cur->children); + for (;exclPrefixes > 0;exclPrefixes--) + exclPrefixPop(style); + goto skip_children; + } + } else if (cur->type == XML_TEXT_NODE) { + if (IS_BLANK_NODE(cur)) { + if (xmlNodeGetSpacePreserve(cur) != 1) { + deleteNode = cur; + } + } else if ((cur->content != NULL) && (internalize) && + (!xmlDictOwns(style->dict, cur->content))) { + xmlChar *tmp; + + /* + * internalize the text string, goal is to speed + * up operations and minimize used space by compiled + * stylesheets. + */ + tmp = (xmlChar *) xmlDictLookup(style->dict, cur->content, -1); + xmlNodeSetContent(cur, NULL); + cur->content = tmp; + } + } else if ((cur->type != XML_ELEMENT_NODE) && + (cur->type != XML_CDATA_SECTION_NODE)) { + deleteNode = cur; + goto skip_children; + } + + /* + * Skip to next node + */ + if (cur->children != NULL) { + if ((cur->children->type != XML_ENTITY_DECL) && + (cur->children->type != XML_ENTITY_REF_NODE) && + (cur->children->type != XML_ENTITY_NODE)) { + cur = cur->children; + continue; + } + } + +skip_children: + if (cur->next != NULL) { + cur = cur->next; + continue; + } + do { + + cur = cur->parent; + if (cur == NULL) + break; + if (cur == (xmlNodePtr) style->doc) { + cur = NULL; + break; + } + if (cur->next != NULL) { + cur = cur->next; + break; + } + } while (cur != NULL); + } + if (deleteNode != NULL) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltPrecomputeStylesheet: removing ignorable blank node\n"); +#endif + xmlUnlinkNode(deleteNode); + xmlFreeNode(deleteNode); + } +} +#endif /* end of else XSLT_REFACTORED */ + +/** + * xsltGatherNamespaces: + * @style: the XSLT stylesheet + * + * Browse the stylesheet and build the namspace hash table which + * will be used for XPath interpretation. If needed do a bit of normalization + */ + +static void +xsltGatherNamespaces(xsltStylesheetPtr style) { + xmlNodePtr cur; + const xmlChar *URI; + + if (style == NULL) + return; + /* + * TODO: basically if the stylesheet uses the same prefix for different + * patterns, well they may be in problem, hopefully they will get + * a warning first. + */ + /* + * TODO: Eliminate the use of the hash for XPath expressions. + * An expression should be evaluated in the context of the in-scope + * namespaces; eliminate the restriction of an XML document to contain + * no duplicate prefixes for different namespace names. + * + */ + cur = xmlDocGetRootElement(style->doc); + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + xmlNsPtr ns = cur->nsDef; + while (ns != NULL) { + if (ns->prefix != NULL) { + if (style->nsHash == NULL) { + style->nsHash = xmlHashCreate(10); + if (style->nsHash == NULL) { + xsltTransformError(NULL, style, cur, + "xsltGatherNamespaces: failed to create hash table\n"); + style->errors++; + return; + } + } + URI = xmlHashLookup(style->nsHash, ns->prefix); + if ((URI != NULL) && (!xmlStrEqual(URI, ns->href))) { + xsltTransformError(NULL, style, cur, + "Namespaces prefix %s used for multiple namespaces\n",ns->prefix); + style->warnings++; + } else if (URI == NULL) { + xmlHashUpdateEntry(style->nsHash, ns->prefix, + (void *) ns->href, (xmlHashDeallocator)xmlFree); + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "Added namespace: %s mapped to %s\n", ns->prefix, ns->href); +#endif + } + } + ns = ns->next; + } + } + + /* + * Skip to next node + */ + if (cur->children != NULL) { + if (cur->children->type != XML_ENTITY_DECL) { + cur = cur->children; + continue; + } + } + if (cur->next != NULL) { + cur = cur->next; + continue; + } + + do { + cur = cur->parent; + if (cur == NULL) + break; + if (cur == (xmlNodePtr) style->doc) { + cur = NULL; + break; + } + if (cur->next != NULL) { + cur = cur->next; + break; + } + } while (cur != NULL); + } +} + +#ifdef XSLT_REFACTORED + +static xsltStyleType +xsltGetXSLTElementTypeByNode(xsltCompilerCtxtPtr cctxt, + xmlNodePtr node) +{ + if ((node == NULL) || (node->type != XML_ELEMENT_NODE) || + (node->name == NULL)) + return(0); + + if (node->name[0] == 'a') { + if (IS_XSLT_NAME(node, "apply-templates")) + return(XSLT_FUNC_APPLYTEMPLATES); + else if (IS_XSLT_NAME(node, "attribute")) + return(XSLT_FUNC_ATTRIBUTE); + else if (IS_XSLT_NAME(node, "apply-imports")) + return(XSLT_FUNC_APPLYIMPORTS); + else if (IS_XSLT_NAME(node, "attribute-set")) + return(0); + + } else if (node->name[0] == 'c') { + if (IS_XSLT_NAME(node, "choose")) + return(XSLT_FUNC_CHOOSE); + else if (IS_XSLT_NAME(node, "copy")) + return(XSLT_FUNC_COPY); + else if (IS_XSLT_NAME(node, "copy-of")) + return(XSLT_FUNC_COPYOF); + else if (IS_XSLT_NAME(node, "call-template")) + return(XSLT_FUNC_CALLTEMPLATE); + else if (IS_XSLT_NAME(node, "comment")) + return(XSLT_FUNC_COMMENT); + + } else if (node->name[0] == 'd') { + if (IS_XSLT_NAME(node, "document")) + return(XSLT_FUNC_DOCUMENT); + else if (IS_XSLT_NAME(node, "decimal-format")) + return(0); + + } else if (node->name[0] == 'e') { + if (IS_XSLT_NAME(node, "element")) + return(XSLT_FUNC_ELEMENT); + + } else if (node->name[0] == 'f') { + if (IS_XSLT_NAME(node, "for-each")) + return(XSLT_FUNC_FOREACH); + else if (IS_XSLT_NAME(node, "fallback")) + return(XSLT_FUNC_FALLBACK); + + } else if (*(node->name) == 'i') { + if (IS_XSLT_NAME(node, "if")) + return(XSLT_FUNC_IF); + else if (IS_XSLT_NAME(node, "include")) + return(0); + else if (IS_XSLT_NAME(node, "import")) + return(0); + + } else if (*(node->name) == 'k') { + if (IS_XSLT_NAME(node, "key")) + return(0); + + } else if (*(node->name) == 'm') { + if (IS_XSLT_NAME(node, "message")) + return(XSLT_FUNC_MESSAGE); + + } else if (*(node->name) == 'n') { + if (IS_XSLT_NAME(node, "number")) + return(XSLT_FUNC_NUMBER); + else if (IS_XSLT_NAME(node, "namespace-alias")) + return(0); + + } else if (*(node->name) == 'o') { + if (IS_XSLT_NAME(node, "otherwise")) + return(XSLT_FUNC_OTHERWISE); + else if (IS_XSLT_NAME(node, "output")) + return(0); + + } else if (*(node->name) == 'p') { + if (IS_XSLT_NAME(node, "param")) + return(XSLT_FUNC_PARAM); + else if (IS_XSLT_NAME(node, "processing-instruction")) + return(XSLT_FUNC_PI); + else if (IS_XSLT_NAME(node, "preserve-space")) + return(0); + + } else if (*(node->name) == 's') { + if (IS_XSLT_NAME(node, "sort")) + return(XSLT_FUNC_SORT); + else if (IS_XSLT_NAME(node, "strip-space")) + return(0); + else if (IS_XSLT_NAME(node, "stylesheet")) + return(0); + + } else if (node->name[0] == 't') { + if (IS_XSLT_NAME(node, "text")) + return(XSLT_FUNC_TEXT); + else if (IS_XSLT_NAME(node, "template")) + return(0); + else if (IS_XSLT_NAME(node, "transform")) + return(0); + + } else if (*(node->name) == 'v') { + if (IS_XSLT_NAME(node, "value-of")) + return(XSLT_FUNC_VALUEOF); + else if (IS_XSLT_NAME(node, "variable")) + return(XSLT_FUNC_VARIABLE); + + } else if (*(node->name) == 'w') { + if (IS_XSLT_NAME(node, "when")) + return(XSLT_FUNC_WHEN); + if (IS_XSLT_NAME(node, "with-param")) + return(XSLT_FUNC_WITHPARAM); + } + return(0); +} + +/** + * xsltParseAnyXSLTElem: + * + * @cctxt: the compilation context + * @elem: the element node of the XSLT instruction + * + * Parses, validates the content models and compiles XSLT instructions. + * + * Returns 0 if everything's fine; + * -1 on API or internal errors. + */ +int +xsltParseAnyXSLTElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr elem) +{ + if ((cctxt == NULL) || (elem == NULL) || + (elem->type != XML_ELEMENT_NODE)) + return(-1); + + elem->psvi = NULL; + + if (! (IS_XSLT_ELEM_FAST(elem))) + return(-1); + /* + * Detection of handled content of extension instructions. + */ + if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { + cctxt->inode->extContentHandled = 1; + } + + xsltCompilerNodePush(cctxt, elem); + /* + * URGENT TODO: Find a way to speed up this annoying redundant + * textual node-name and namespace comparison. + */ + if (cctxt->inode->prev->curChildType != 0) + cctxt->inode->type = cctxt->inode->prev->curChildType; + else + cctxt->inode->type = xsltGetXSLTElementTypeByNode(cctxt, elem); + /* + * Update the in-scope namespaces if needed. + */ + if (elem->nsDef != NULL) + cctxt->inode->inScopeNs = + xsltCompilerBuildInScopeNsList(cctxt, elem); + /* + * xsltStylePreCompute(): + * This will compile the information found on the current + * element's attributes. NOTE that this won't process the + * children of the instruction. + */ + xsltStylePreCompute(cctxt->style, elem); + /* + * TODO: How to react on errors in xsltStylePreCompute() ? + */ + + /* + * Validate the content model of the XSLT-element. + */ + switch (cctxt->inode->type) { + case XSLT_FUNC_APPLYIMPORTS: + /* EMPTY */ + goto empty_content; + case XSLT_FUNC_APPLYTEMPLATES: + /* */ + goto apply_templates; + case XSLT_FUNC_ATTRIBUTE: + /* */ + goto sequence_constructor; + case XSLT_FUNC_CALLTEMPLATE: + /* */ + goto call_template; + case XSLT_FUNC_CHOOSE: + /* */ + goto choose; + case XSLT_FUNC_COMMENT: + /* */ + goto sequence_constructor; + case XSLT_FUNC_COPY: + /* */ + goto sequence_constructor; + case XSLT_FUNC_COPYOF: + /* EMPTY */ + goto empty_content; + case XSLT_FUNC_DOCUMENT: /* Extra one */ + /* ?? template ?? */ + goto sequence_constructor; + case XSLT_FUNC_ELEMENT: + /* */ + goto sequence_constructor; + case XSLT_FUNC_FALLBACK: + /* */ + goto sequence_constructor; + case XSLT_FUNC_FOREACH: + /* */ + goto for_each; + case XSLT_FUNC_IF: + /* */ + goto sequence_constructor; + case XSLT_FUNC_OTHERWISE: + /* */ + goto sequence_constructor; + case XSLT_FUNC_MESSAGE: + /* */ + goto sequence_constructor; + case XSLT_FUNC_NUMBER: + /* EMPTY */ + goto empty_content; + case XSLT_FUNC_PARAM: + /* + * Check for redefinition. + */ + if ((elem->psvi != NULL) && (cctxt->ivar != NULL)) { + xsltVarInfoPtr ivar = cctxt->ivar; + + do { + if ((ivar->name == + ((xsltStyleItemParamPtr) elem->psvi)->name) && + (ivar->nsName == + ((xsltStyleItemParamPtr) elem->psvi)->ns)) + { + elem->psvi = NULL; + xsltTransformError(NULL, cctxt->style, elem, + "Redefinition of variable or parameter '%s'.\n", + ivar->name); + cctxt->style->errors++; + goto error; + } + ivar = ivar->prev; + } while (ivar != NULL); + } + /* */ + goto sequence_constructor; + case XSLT_FUNC_PI: + /* */ + goto sequence_constructor; + case XSLT_FUNC_SORT: + /* EMPTY */ + goto empty_content; + case XSLT_FUNC_TEXT: + /* */ + goto text; + case XSLT_FUNC_VALUEOF: + /* EMPTY */ + goto empty_content; + case XSLT_FUNC_VARIABLE: + /* + * Check for redefinition. + */ + if ((elem->psvi != NULL) && (cctxt->ivar != NULL)) { + xsltVarInfoPtr ivar = cctxt->ivar; + + do { + if ((ivar->name == + ((xsltStyleItemVariablePtr) elem->psvi)->name) && + (ivar->nsName == + ((xsltStyleItemVariablePtr) elem->psvi)->ns)) + { + elem->psvi = NULL; + xsltTransformError(NULL, cctxt->style, elem, + "Redefinition of variable or parameter '%s'.\n", + ivar->name); + cctxt->style->errors++; + goto error; + } + ivar = ivar->prev; + } while (ivar != NULL); + } + /* */ + goto sequence_constructor; + case XSLT_FUNC_WHEN: + /* */ + goto sequence_constructor; + case XSLT_FUNC_WITHPARAM: + /* */ + goto sequence_constructor; + default: +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseXSLTNode: Unhandled XSLT element '%s'.\n", + elem->name); +#endif + xsltTransformError(NULL, cctxt->style, elem, + "xsltParseXSLTNode: Internal error; " + "unhandled XSLT element '%s'.\n", elem->name); + cctxt->style->errors++; + goto internal_err; + } + +apply_templates: + /* */ + if (elem->children != NULL) { + xmlNodePtr child = elem->children; + do { + if (child->type == XML_ELEMENT_NODE) { + if (IS_XSLT_ELEM_FAST(child)) { + if (xmlStrEqual(child->name, BAD_CAST "with-param")) { + cctxt->inode->curChildType = XSLT_FUNC_WITHPARAM; + xsltParseAnyXSLTElem(cctxt, child); + } else if (xmlStrEqual(child->name, BAD_CAST "sort")) { + cctxt->inode->curChildType = XSLT_FUNC_SORT; + xsltParseAnyXSLTElem(cctxt, child); + } else + xsltParseContentError(cctxt->style, child); + } else + xsltParseContentError(cctxt->style, child); + } + child = child->next; + } while (child != NULL); + } + goto exit; + +call_template: + /* */ + if (elem->children != NULL) { + xmlNodePtr child = elem->children; + do { + if (child->type == XML_ELEMENT_NODE) { + if (IS_XSLT_ELEM_FAST(child)) { + xsltStyleType type; + + type = xsltGetXSLTElementTypeByNode(cctxt, child); + if (type == XSLT_FUNC_WITHPARAM) { + cctxt->inode->curChildType = XSLT_FUNC_WITHPARAM; + xsltParseAnyXSLTElem(cctxt, child); + } else { + xsltParseContentError(cctxt->style, child); + } + } else + xsltParseContentError(cctxt->style, child); + } + child = child->next; + } while (child != NULL); + } + goto exit; + +text: + if (elem->children != NULL) { + xmlNodePtr child = elem->children; + do { + if ((child->type != XML_TEXT_NODE) && + (child->type != XML_CDATA_SECTION_NODE)) + { + xsltTransformError(NULL, cctxt->style, elem, + "The XSLT 'text' element must have only character " + "data as content.\n"); + } + child = child->next; + } while (child != NULL); + } + goto exit; + +empty_content: + if (elem->children != NULL) { + xmlNodePtr child = elem->children; + /* + * Relaxed behaviour: we will allow whitespace-only text-nodes. + */ + do { + if (((child->type != XML_TEXT_NODE) && + (child->type != XML_CDATA_SECTION_NODE)) || + (! IS_BLANK_NODE(child))) + { + xsltTransformError(NULL, cctxt->style, elem, + "This XSLT element must have no content.\n"); + cctxt->style->errors++; + break; + } + child = child->next; + } while (child != NULL); + } + goto exit; + +choose: + /* */ + /* + * TODO: text-nodes in between are *not* allowed in XSLT 1.0. + * The old behaviour did not check this. + * NOTE: In XSLT 2.0 they are stripped beforehand + * if whitespace-only (regardless of xml:space). + */ + if (elem->children != NULL) { + xmlNodePtr child = elem->children; + int nbWhen = 0, nbOtherwise = 0, err = 0; + do { + if (child->type == XML_ELEMENT_NODE) { + if (IS_XSLT_ELEM_FAST(child)) { + xsltStyleType type; + + type = xsltGetXSLTElementTypeByNode(cctxt, child); + if (type == XSLT_FUNC_WHEN) { + nbWhen++; + if (nbOtherwise) { + xsltParseContentError(cctxt->style, child); + err = 1; + break; + } + cctxt->inode->curChildType = XSLT_FUNC_WHEN; + xsltParseAnyXSLTElem(cctxt, child); + } else if (type == XSLT_FUNC_OTHERWISE) { + if (! nbWhen) { + xsltParseContentError(cctxt->style, child); + err = 1; + break; + } + if (nbOtherwise) { + xsltTransformError(NULL, cctxt->style, elem, + "The XSLT 'choose' element must not contain " + "more than one XSLT 'otherwise' element.\n"); + cctxt->style->errors++; + err = 1; + break; + } + nbOtherwise++; + cctxt->inode->curChildType = XSLT_FUNC_OTHERWISE; + xsltParseAnyXSLTElem(cctxt, child); + } else + xsltParseContentError(cctxt->style, child); + } else + xsltParseContentError(cctxt->style, child); + } + /* + else + xsltParseContentError(cctxt, child); + */ + child = child->next; + } while (child != NULL); + if ((! err) && (! nbWhen)) { + xsltTransformError(NULL, cctxt->style, elem, + "The XSLT element 'choose' must contain at least one " + "XSLT element 'when'.\n"); + cctxt->style->errors++; + } + } + goto exit; + +for_each: + /* */ + /* + * NOTE: Text-nodes before xsl:sort are *not* allowed in XSLT 1.0. + * The old behaviour did not allow this, but it catched this + * only at transformation-time. + * In XSLT 2.0 they are stripped beforehand if whitespace-only + * (regardless of xml:space). + */ + if (elem->children != NULL) { + xmlNodePtr child = elem->children; + /* + * Parse xsl:sort first. + */ + do { + if ((child->type == XML_ELEMENT_NODE) && + IS_XSLT_ELEM_FAST(child)) + { + if (xsltGetXSLTElementTypeByNode(cctxt, child) == + XSLT_FUNC_SORT) + { + cctxt->inode->curChildType = XSLT_FUNC_SORT; + xsltParseAnyXSLTElem(cctxt, child); + } else + break; + } else + break; + child = child->next; + } while (child != NULL); + /* + * Parse the sequece constructor. + */ + if (child != NULL) + xsltParseSequenceConstructor(cctxt, child); + } + goto exit; + +sequence_constructor: + /* + * Parse the sequence constructor. + */ + if (elem->children != NULL) + xsltParseSequenceConstructor(cctxt, elem->children); + + /* + * Register information for vars/params. Only needed if there + * are any following siblings. + */ + if ((elem->next != NULL) && + ((cctxt->inode->type == XSLT_FUNC_VARIABLE) || + (cctxt->inode->type == XSLT_FUNC_PARAM))) + { + if ((elem->psvi != NULL) && + (((xsltStyleBasicItemVariablePtr) elem->psvi)->name)) + { + xsltCompilerVarInfoPush(cctxt, elem, + ((xsltStyleBasicItemVariablePtr) elem->psvi)->name, + ((xsltStyleBasicItemVariablePtr) elem->psvi)->ns); + } + } + +error: +exit: + xsltCompilerNodePop(cctxt, elem); + return(0); + +internal_err: + xsltCompilerNodePop(cctxt, elem); + return(-1); +} + +/** + * xsltForwardsCompatUnkownItemCreate: + * + * @cctxt: the compilation context + * + * Creates a compiled representation of the unknown + * XSLT instruction. + * + * Returns the compiled representation. + */ +static xsltStyleItemUknownPtr +xsltForwardsCompatUnkownItemCreate(xsltCompilerCtxtPtr cctxt) +{ + xsltStyleItemUknownPtr item; + + item = (xsltStyleItemUknownPtr) xmlMalloc(sizeof(xsltStyleItemUknown)); + if (item == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "Internal error in xsltForwardsCompatUnkownItemCreate(): " + "Failed to allocate memory.\n"); + cctxt->style->errors++; + return(NULL); + } + memset(item, 0, sizeof(xsltStyleItemUknown)); + item->type = XSLT_FUNC_UNKOWN_FORWARDS_COMPAT; + /* + * Store it in the stylesheet. + */ + item->next = cctxt->style->preComps; + cctxt->style->preComps = (xsltElemPreCompPtr) item; + return(item); +} + +/** + * xsltParseUnknownXSLTElem: + * + * @cctxt: the compilation context + * @node: the element of the unknown XSLT instruction + * + * Parses an unknown XSLT element. + * If forwards compatible mode is enabled this will allow + * such an unknown XSLT and; otherwise it is rejected. + * + * Returns 1 in the unknown XSLT instruction is rejected, + * 0 if everything's fine and + * -1 on API or internal errors. + */ +static int +xsltParseUnknownXSLTElem(xsltCompilerCtxtPtr cctxt, + xmlNodePtr node) +{ + if ((cctxt == NULL) || (node == NULL)) + return(-1); + + /* + * Detection of handled content of extension instructions. + */ + if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { + cctxt->inode->extContentHandled = 1; + } + if (cctxt->inode->forwardsCompat == 0) { + /* + * We are not in forwards-compatible mode, so raise an error. + */ + xsltTransformError(NULL, cctxt->style, node, + "Unknown XSLT element '%s'.\n", node->name); + cctxt->style->errors++; + return(1); + } + /* + * Forwards-compatible mode. + * ------------------------ + * + * Parse/compile xsl:fallback elements. + * + * QUESTION: Do we have to raise an error if there's no xsl:fallback? + * ANSWER: No, since in the stylesheet the fallback behaviour might + * also be provided by using the XSLT function "element-available". + */ + if (cctxt->unknownItem == NULL) { + /* + * Create a singleton for all unknown XSLT instructions. + */ + cctxt->unknownItem = xsltForwardsCompatUnkownItemCreate(cctxt); + if (cctxt->unknownItem == NULL) { + node->psvi = NULL; + return(-1); + } + } + node->psvi = cctxt->unknownItem; + if (node->children == NULL) + return(0); + else { + xmlNodePtr child = node->children; + + xsltCompilerNodePush(cctxt, node); + /* + * Update the in-scope namespaces if needed. + */ + if (node->nsDef != NULL) + cctxt->inode->inScopeNs = + xsltCompilerBuildInScopeNsList(cctxt, node); + /* + * Parse all xsl:fallback children. + */ + do { + if ((child->type == XML_ELEMENT_NODE) && + IS_XSLT_ELEM_FAST(child) && + IS_XSLT_NAME(child, "fallback")) + { + cctxt->inode->curChildType = XSLT_FUNC_FALLBACK; + xsltParseAnyXSLTElem(cctxt, child); + } + child = child->next; + } while (child != NULL); + + xsltCompilerNodePop(cctxt, node); + } + return(0); +} + +/** + * xsltParseSequenceConstructor: + * + * @cctxt: the compilation context + * @cur: the start-node of the content to be parsed + * + * Parses a "template" content (or "sequence constructor" in XSLT 2.0 terms). + * This will additionally remove xsl:text elements from the tree. + */ +void +xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) +{ + xsltStyleType type; + xmlNodePtr deleteNode = NULL; + + if (cctxt == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltParseSequenceConstructor: Bad arguments\n"); + cctxt->style->errors++; + return; + } + /* + * Detection of handled content of extension instructions. + */ + if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { + cctxt->inode->extContentHandled = 1; + } + if (cur == NULL) + return; + /* + * This is the content reffered to as a "template". + * E.g. an xsl:element has such content model: + * + * + * + * NOTE that in XSLT-2 the term "template" was abandoned due to + * confusion with xsl:template and the term "sequence constructor" + * was introduced instead. + * + * The following XSLT-instructions are allowed to appear: + * xsl:apply-templates, xsl:call-template, xsl:apply-imports, + * xsl:for-each, xsl:value-of, xsl:copy-of, xsl:number, + * xsl:choose, xsl:if, xsl:text, xsl:copy, xsl:variable, + * xsl:message, xsl:fallback, + * xsl:processing-instruction, xsl:comment, xsl:element + * xsl:attribute. + * Additional allowed content: + * 1) extension instructions + * 2) literal result elements + * 3) PCDATA + * + * NOTE that this content model does *not* allow xsl:param. + */ + while (cur != NULL) { + if (deleteNode != NULL) { +#ifdef WITH_XSLT_DEBUG_BLANKS + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseSequenceConstructor: removing xsl:text element\n"); +#endif + xmlUnlinkNode(deleteNode); + xmlFreeNode(deleteNode); + deleteNode = NULL; + } + if (cur->type == XML_ELEMENT_NODE) { + + if (cur->psvi == xsltXSLTTextMarker) { + /* + * xsl:text elements + * -------------------------------------------------------- + */ + xmlNodePtr tmp; + + cur->psvi = NULL; + /* + * Mark the xsl:text element for later deletion. + */ + deleteNode = cur; + /* + * Validate content. + */ + tmp = cur->children; + if (tmp) { + /* + * We don't expect more than one text-node in the + * content, since we already merged adjacent + * text/CDATA-nodes and eliminated PI/comment-nodes. + */ + if ((tmp->type == XML_TEXT_NODE) || + (tmp->next == NULL)) + { + /* + * Leave the contained text-node in the tree. + */ + xmlUnlinkNode(tmp); + xmlAddPrevSibling(cur, tmp); + } else { + tmp = NULL; + xsltTransformError(NULL, cctxt->style, cur, + "Element 'xsl:text': Invalid type " + "of node found in content.\n"); + cctxt->style->errors++; + } + } + if (cur->properties) { + xmlAttrPtr attr; + /* + * TODO: We need to report errors for + * invalid attrs. + */ + attr = cur->properties; + do { + if ((attr->ns == NULL) && + (attr->name != NULL) && + (attr->name[0] == 'd') && + xmlStrEqual(attr->name, + BAD_CAST "disable-output-escaping")) + { + /* + * Attr "disable-output-escaping". + * XSLT-2: This attribute is deprecated. + */ + if ((attr->children != NULL) && + xmlStrEqual(attr->children->content, + BAD_CAST "yes")) + { + /* + * Disable output escaping for this + * text node. + */ + if (tmp) + tmp->name = xmlStringTextNoenc; + } else if ((attr->children == NULL) || + (attr->children->content == NULL) || + (!xmlStrEqual(attr->children->content, + BAD_CAST "no"))) + { + xsltTransformError(NULL, cctxt->style, + cur, + "Attribute 'disable-output-escaping': " + "Invalid value. Expected is " + "'yes' or 'no'.\n"); + cctxt->style->errors++; + } + break; + } + attr = attr->next; + } while (attr != NULL); + } + } else if (IS_XSLT_ELEM_FAST(cur)) { + /* + * TODO: Using the XSLT-marker is still not stable yet. + */ + /* if (cur->psvi == xsltXSLTElemMarker) { */ + /* + * XSLT instructions + * -------------------------------------------------------- + */ + cur->psvi = NULL; + type = xsltGetXSLTElementTypeByNode(cctxt, cur); + switch (type) { + case XSLT_FUNC_APPLYIMPORTS: + case XSLT_FUNC_APPLYTEMPLATES: + case XSLT_FUNC_ATTRIBUTE: + case XSLT_FUNC_CALLTEMPLATE: + case XSLT_FUNC_CHOOSE: + case XSLT_FUNC_COMMENT: + case XSLT_FUNC_COPY: + case XSLT_FUNC_COPYOF: + case XSLT_FUNC_DOCUMENT: /* Extra one */ + case XSLT_FUNC_ELEMENT: + case XSLT_FUNC_FALLBACK: + case XSLT_FUNC_FOREACH: + case XSLT_FUNC_IF: + case XSLT_FUNC_MESSAGE: + case XSLT_FUNC_NUMBER: + case XSLT_FUNC_PI: + case XSLT_FUNC_TEXT: + case XSLT_FUNC_VALUEOF: + case XSLT_FUNC_VARIABLE: + /* + * Parse the XSLT element. + */ + cctxt->inode->curChildType = type; + xsltParseAnyXSLTElem(cctxt, cur); + break; + default: + xsltParseUnknownXSLTElem(cctxt, cur); + cur = cur->next; + continue; + } + } else { + /* + * Non-XSLT elements + * ----------------- + */ + xsltCompilerNodePush(cctxt, cur); + /* + * Update the in-scope namespaces if needed. + */ + if (cur->nsDef != NULL) + cctxt->inode->inScopeNs = + xsltCompilerBuildInScopeNsList(cctxt, cur); + /* + * The current element is either a literal result element + * or an extension instruction. + * + * Process attr "xsl:extension-element-prefixes". + * FUTURE TODO: IIRC in XSLT 2.0 this attribute must be + * processed by the implementor of the extension function; + * i.e., it won't be handled by the XSLT processor. + */ + /* SPEC 1.0: + * "exclude-result-prefixes" is only allowed on literal + * result elements and "xsl:exclude-result-prefixes" + * on xsl:stylesheet/xsl:transform. + * SPEC 2.0: + * "There are a number of standard attributes + * that may appear on any XSLT element: specifically + * version, exclude-result-prefixes, + * extension-element-prefixes, xpath-default-namespace, + * default-collation, and use-when." + * + * SPEC 2.0: + * For literal result elements: + * "xsl:version, xsl:exclude-result-prefixes, + * xsl:extension-element-prefixes, + * xsl:xpath-default-namespace, + * xsl:default-collation, or xsl:use-when." + */ + if (cur->properties) + cctxt->inode->extElemNs = + xsltParseExtElemPrefixes(cctxt, + cur, cctxt->inode->extElemNs, + XSLT_ELEMENT_CATEGORY_LRE); + /* + * Eval if we have an extension instruction here. + */ + if ((cur->ns != NULL) && + (cctxt->inode->extElemNs != NULL) && + (xsltCheckExtPrefix(cctxt->style, cur->ns->href) == 1)) + { + /* + * Extension instructions + * ---------------------------------------------------- + * Mark the node information. + */ + cctxt->inode->category = XSLT_ELEMENT_CATEGORY_EXTENSION; + cctxt->inode->extContentHandled = 0; + if (cur->psvi != NULL) { + cur->psvi = NULL; + /* + * TODO: Temporary sanity check. + */ + xsltTransformError(NULL, cctxt->style, cur, + "Internal error in xsltParseSequenceConstructor(): " + "Occupied PSVI field.\n"); + cctxt->style->errors++; + cur = cur->next; + continue; + } + cur->psvi = (void *) + xsltPreComputeExtModuleElement(cctxt->style, cur); + + if (cur->psvi == NULL) { + /* + * OLD COMMENT: "Unknown element, maybe registered + * at the context level. Mark it for later + * recognition." + * QUESTION: What does the xsltExtMarker mean? + * ANSWER: It is used in + * xsltApplySequenceConstructor() at + * transformation-time to look out for extension + * registered in the transformation context. + */ + cur->psvi = (void *) xsltExtMarker; + } + /* + * BIG NOTE: Now the ugly part. In previous versions + * of Libxslt (until 1.1.16), all the content of an + * extension instruction was processed and compiled without + * the need of the extension-author to explicitely call + * such a processing;.We now need to mimic this old + * behaviour in order to avoid breaking old code + * on the extension-author's side. + * The mechanism: + * 1) If the author does *not* set the + * compile-time-flag @extContentHandled, then we'll + * parse the content assuming that it's a "template" + * (or "sequence constructor in XSLT 2.0 terms). + * NOTE: If the extension is registered at + * transformation-time only, then there's no way of + * knowing that content shall be valid, and we'll + * process the content the same way. + * 2) If the author *does* set the flag, then we'll assume + * that the author has handled the parsing him/herself + * (e.g. called xsltParseSequenceConstructor(), etc. + * explicitely in his/her code). + */ + if ((cur->children != NULL) && + (cctxt->inode->extContentHandled == 0)) + { + /* + * Default parsing of the content using the + * sequence-constructor model. + */ + xsltParseSequenceConstructor(cctxt, cur->children); + } + } else { + /* + * Literal result element + * ---------------------------------------------------- + * Allowed XSLT attributes: + * xsl:extension-element-prefixes CDATA #IMPLIED + * xsl:exclude-result-prefixes CDATA #IMPLIED + * TODO: xsl:use-attribute-sets %qnames; #IMPLIED + * xsl:version NMTOKEN #IMPLIED + */ + cur->psvi = NULL; + cctxt->inode->category = XSLT_ELEMENT_CATEGORY_LRE; + if (cur->properties != NULL) { + xmlAttrPtr attr = cur->properties; + /* + * Attribute "xsl:exclude-result-prefixes". + */ + cctxt->inode->exclResultNs = + xsltParseExclResultPrefixes(cctxt, cur, + cctxt->inode->exclResultNs, + XSLT_ELEMENT_CATEGORY_LRE); + /* + * Attribute "xsl:version". + */ + xsltParseAttrXSLTVersion(cctxt, cur, + XSLT_ELEMENT_CATEGORY_LRE); + /* + * Report invalid XSLT attributes. + * For XSLT 1.0 only xsl:use-attribute-sets is allowed + * next to xsl:version, xsl:exclude-result-prefixes and + * xsl:extension-element-prefixes. + * + * Mark all XSLT attributes, in order to skip such + * attributes when instantiating the LRE. + */ + do { + if ((attr->psvi != xsltXSLTAttrMarker) && + IS_XSLT_ATTR_FAST(attr)) + { + if (! xmlStrEqual(attr->name, + BAD_CAST "use-attribute-sets")) + { + xsltTransformError(NULL, cctxt->style, + cur, + "Unknown XSLT attribute '%s'.\n", + attr->name); + cctxt->style->errors++; + } else { + /* + * XSLT attr marker. + */ + attr->psvi = (void *) xsltXSLTAttrMarker; + } + } + attr = attr->next; + } while (attr != NULL); + } + /* + * Create/reuse info for the literal result element. + */ + if (cctxt->inode->nsChanged) + xsltLREInfoCreate(cctxt, cur, 1); + cur->psvi = cctxt->inode->litResElemInfo; + /* + * Apply ns-aliasing on the element and on its attributes. + */ + if (cctxt->hasNsAliases) + xsltLREBuildEffectiveNs(cctxt, cur); + /* + * Compile attribute value templates (AVT). + */ + if (cur->properties) { + xmlAttrPtr attr = cur->properties; + + while (attr != NULL) { + xsltCompileAttr(cctxt->style, attr); + attr = attr->next; + } + } + /* + * Parse the content, which is defined to be a "template" + * (or "sequence constructor" in XSLT 2.0 terms). + */ + if (cur->children != NULL) { + xsltParseSequenceConstructor(cctxt, cur->children); + } + } + /* + * Leave the non-XSLT element. + */ + xsltCompilerNodePop(cctxt, cur); + } + } + cur = cur->next; + } + if (deleteNode != NULL) { +#ifdef WITH_XSLT_DEBUG_BLANKS + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseSequenceConstructor: removing xsl:text element\n"); +#endif + xmlUnlinkNode(deleteNode); + xmlFreeNode(deleteNode); + deleteNode = NULL; + } +} + +/** + * xsltParseTemplateContent: + * @style: the XSLT stylesheet + * @templ: the node containing the content to be parsed + * + * Parses and compiles the content-model of an xsl:template element. + * Note that this is *not* the "template" content model (or "sequence + * constructor" in XSLT 2.0); it it allows addional xsl:param + * elements as immediate children of @templ. + * + * Called by: + * exsltFuncFunctionComp() (EXSLT, functions.c) + * So this is intended to be called from extension functions. + */ +void +xsltParseTemplateContent(xsltStylesheetPtr style, xmlNodePtr templ) { + if ((style == NULL) || (templ == NULL)) + return; + + /* + * Detection of handled content of extension instructions. + */ + if (XSLT_CCTXT(style)->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { + XSLT_CCTXT(style)->inode->extContentHandled = 1; + } + + if (templ->children != NULL) { + xmlNodePtr child = templ->children; + /* + * Process xsl:param elements, which can only occur as the + * immediate children of xsl:template (well, and of any + * user-defined extension instruction if needed). + */ + do { + if ((child->type == XML_ELEMENT_NODE) && + IS_XSLT_ELEM_FAST(child) && + IS_XSLT_NAME(child, "param")) + { + XSLT_CCTXT(style)->inode->curChildType = XSLT_FUNC_PARAM; + xsltParseAnyXSLTElem(XSLT_CCTXT(style), child); + } else + break; + child = child->next; + } while (child != NULL); + /* + * Parse the content and register the pattern. + */ + xsltParseSequenceConstructor(XSLT_CCTXT(style), child); + } +} + +#else /* XSLT_REFACTORED */ + +/** + * xsltParseTemplateContent: + * @style: the XSLT stylesheet + * @templ: the container node (can be a document for literal results) + * + * parse a template content-model + * Clean-up the template content from unwanted ignorable blank nodes + * and process xslt:text + */ +void +xsltParseTemplateContent(xsltStylesheetPtr style, xmlNodePtr templ) { + xmlNodePtr cur, delete; + /* + * This content comes from the stylesheet + * For stylesheets, the set of whitespace-preserving + * element names consists of just xsl:text. + */ + cur = templ->children; + delete = NULL; + while (cur != NULL) { + if (delete != NULL) { +#ifdef WITH_XSLT_DEBUG_BLANKS + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseTemplateContent: removing text\n"); +#endif + xmlUnlinkNode(delete); + xmlFreeNode(delete); + delete = NULL; + } + if (IS_XSLT_ELEM(cur)) { + if (IS_XSLT_NAME(cur, "text")) { + /* + * TODO: Processing of xsl:text should be moved to + * xsltPrecomputeStylesheet(), since otherwise this + * will be performed for every multiply included + * stylesheet; i.e. this here is not skipped with + * the use of the style->nopreproc flag. + */ + if (cur->children != NULL) { + xmlChar *prop; + xmlNodePtr text = cur->children, next; + int noesc = 0; + + prop = xmlGetNsProp(cur, + (const xmlChar *)"disable-output-escaping", + NULL); + if (prop != NULL) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "Disable escaping: %s\n", text->content); +#endif + if (xmlStrEqual(prop, (const xmlChar *)"yes")) { + noesc = 1; + } else if (!xmlStrEqual(prop, + (const xmlChar *)"no")){ + xsltTransformError(NULL, style, cur, + "xsl:text: disable-output-escaping allows only yes or no\n"); + style->warnings++; + + } + xmlFree(prop); + } + + while (text != NULL) { + if (text->type == XML_COMMENT_NODE) { + text = text->next; + continue; + } + if ((text->type != XML_TEXT_NODE) && + (text->type != XML_CDATA_SECTION_NODE)) { + xsltTransformError(NULL, style, cur, + "xsltParseTemplateContent: xslt:text content problem\n"); + style->errors++; + break; + } + if ((noesc) && (text->type != XML_CDATA_SECTION_NODE)) + text->name = xmlStringTextNoenc; + text = text->next; + } + + /* + * replace xsl:text by the list of childs + */ + if (text == NULL) { + text = cur->children; + while (text != NULL) { + if ((style->internalized) && + (text->content != NULL) && + (!xmlDictOwns(style->dict, text->content))) { + + /* + * internalize the text string + */ + if (text->doc->dict != NULL) { + const xmlChar *tmp; + + tmp = xmlDictLookup(text->doc->dict, + text->content, -1); + if (tmp != text->content) { + xmlNodeSetContent(text, NULL); + text->content = (xmlChar *) tmp; + } + } + } + + next = text->next; + xmlUnlinkNode(text); + xmlAddPrevSibling(cur, text); + text = next; + } + } + } + delete = cur; + goto skip_children; + } + } + else if ((cur->ns != NULL) && (style->nsDefs != NULL) && + (xsltCheckExtPrefix(style, cur->ns->prefix))) + { + /* + * okay this is an extension element compile it too + */ + xsltStylePreCompute(style, cur); + } else { + /* + * This is an element which will be output as part of the + * template exectution, precompile AVT if found. + */ + if ((cur->ns == NULL) && (style->defaultAlias != NULL) && + (cur->type == XML_ELEMENT_NODE)) { + cur->ns = xmlSearchNsByHref(cur->doc, cur, + style->defaultAlias); + } + if (cur->properties != NULL) { + xmlAttrPtr attr = cur->properties; + + while (attr != NULL) { + xsltCompileAttr(style, attr); + attr = attr->next; + } + } + } + /* + * Skip to next node + */ + if (cur->children != NULL) { + if (cur->children->type != XML_ENTITY_DECL) { + cur = cur->children; + continue; + } + } +skip_children: + if (cur->next != NULL) { + cur = cur->next; + continue; + } + + do { + cur = cur->parent; + if (cur == NULL) + break; + if (cur == templ) { + cur = NULL; + break; + } + if (cur->next != NULL) { + cur = cur->next; + break; + } + } while (cur != NULL); + } + if (delete != NULL) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseTemplateContent: removing text\n"); +#endif + xmlUnlinkNode(delete); + xmlFreeNode(delete); + delete = NULL; + } + + /* + * Skip the first params + */ + cur = templ->children; + while (cur != NULL) { + if ((IS_XSLT_ELEM(cur)) && (!(IS_XSLT_NAME(cur, "param")))) + break; + cur = cur->next; + } + + /* + * Browse the remainder of the template + */ + while (cur != NULL) { + if ((IS_XSLT_ELEM(cur)) && (IS_XSLT_NAME(cur, "param"))) { + xmlNodePtr param = cur; + + xsltTransformError(NULL, style, cur, + "xsltParseTemplateContent: ignoring misplaced param element\n"); + if (style != NULL) style->warnings++; + cur = cur->next; + xmlUnlinkNode(param); + xmlFreeNode(param); + } else + break; + } +} + +#endif /* else XSLT_REFACTORED */ + +/** + * xsltParseStylesheetKey: + * @style: the XSLT stylesheet + * @key: the "key" element + * + * + * + * + * parse an XSLT stylesheet key definition and register it + */ + +static void +xsltParseStylesheetKey(xsltStylesheetPtr style, xmlNodePtr key) { + xmlChar *prop = NULL; + xmlChar *use = NULL; + xmlChar *match = NULL; + xmlChar *name = NULL; + xmlChar *nameURI = NULL; + + if ((style == NULL) || (key == NULL)) + return; + + /* + * Get arguments + */ + prop = xmlGetNsProp(key, (const xmlChar *)"name", NULL); + if (prop != NULL) { + const xmlChar *URI; + + /* + * TODO: Don't use xsltGetQNameURI(). + */ + URI = xsltGetQNameURI(key, &prop); + if (prop == NULL) { + if (style != NULL) style->errors++; + goto error; + } else { + name = prop; + if (URI != NULL) + nameURI = xmlStrdup(URI); + } +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseStylesheetKey: name %s\n", name); +#endif + } else { + xsltTransformError(NULL, style, key, + "xsl:key : error missing name\n"); + if (style != NULL) style->errors++; + goto error; + } + + match = xmlGetNsProp(key, (const xmlChar *)"match", NULL); + if (match == NULL) { + xsltTransformError(NULL, style, key, + "xsl:key : error missing match\n"); + if (style != NULL) style->errors++; + goto error; + } + + use = xmlGetNsProp(key, (const xmlChar *)"use", NULL); + if (use == NULL) { + xsltTransformError(NULL, style, key, + "xsl:key : error missing use\n"); + if (style != NULL) style->errors++; + goto error; + } + + /* + * register the keys + */ + xsltAddKey(style, name, nameURI, match, use, key); + + +error: + if (use != NULL) + xmlFree(use); + if (match != NULL) + xmlFree(match); + if (name != NULL) + xmlFree(name); + if (nameURI != NULL) + xmlFree(nameURI); + + if (key->children != NULL) { + xsltParseContentError(style, key->children); + } +} + +#ifdef XSLT_REFACTORED +/** + * xsltParseXSLTTemplate: + * @style: the XSLT stylesheet + * @template: the "template" element + * + * parse an XSLT stylesheet template building the associated structures + * TODO: Is @style ever expected to be NULL? + * + * Called from: + * xsltParseXSLTStylesheet() + * xsltParseStylesheetTop() + */ + +static void +xsltParseXSLTTemplate(xsltCompilerCtxtPtr cctxt, xmlNodePtr templNode) { + xsltTemplatePtr templ; + xmlChar *prop; + double priority; + + if ((cctxt == NULL) || (templNode == NULL)) + return; + + /* + * Create and link the structure + */ + templ = xsltNewTemplate(); + if (templ == NULL) + return; + + xsltCompilerNodePush(cctxt, templNode); + if (templNode->nsDef != NULL) + cctxt->inode->inScopeNs = + xsltCompilerBuildInScopeNsList(cctxt, templNode); + + templ->next = cctxt->style->templates; + cctxt->style->templates = templ; + templ->style = cctxt->style; + + /* + * Attribute "mode". + */ + prop = xmlGetNsProp(templNode, (const xmlChar *)"mode", NULL); + if (prop != NULL) { + const xmlChar *modeURI; + + /* + * TODO: We need a standardized function for extraction + * of namespace names and local names from QNames. + * Don't use xsltGetQNameURI() as it cannot channeö + * reports through the context. + */ + modeURI = xsltGetQNameURI(templNode, &prop); + if (prop == NULL) { + cctxt->style->errors++; + goto error; + } + templ->mode = xmlDictLookup(cctxt->style->dict, prop, -1); + xmlFree(prop); + prop = NULL; + if (xmlValidateNCName(templ->mode, 0)) { + xsltTransformError(NULL, cctxt->style, templNode, + "xsl:template: Attribute 'mode': The local part '%s' " + "of the value is not a valid NCName.\n", templ->name); + cctxt->style->errors++; + goto error; + } + if (modeURI != NULL) + templ->modeURI = xmlDictLookup(cctxt->style->dict, modeURI, -1); +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseXSLTTemplate: mode %s\n", templ->mode); +#endif + } + /* + * Attribute "match". + */ + prop = xmlGetNsProp(templNode, (const xmlChar *)"match", NULL); + if (prop != NULL) { + templ->match = prop; + prop = NULL; + } + /* + * Attribute "priority". + */ + prop = xmlGetNsProp(templNode, (const xmlChar *)"priority", NULL); + if (prop != NULL) { + priority = xmlXPathStringEvalNumber(prop); + templ->priority = (float) priority; + xmlFree(prop); + prop = NULL; + } + /* + * Attribute "name". + */ + prop = xmlGetNsProp(templNode, (const xmlChar *)"name", NULL); + if (prop != NULL) { + const xmlChar *nameURI; + xsltTemplatePtr curTempl; + + /* + * TODO: Don't use xsltGetQNameURI(). + */ + nameURI = xsltGetQNameURI(templNode, &prop); + if (prop == NULL) { + cctxt->style->errors++; + goto error; + } + templ->name = xmlDictLookup(cctxt->style->dict, prop, -1); + xmlFree(prop); + prop = NULL; + if (xmlValidateNCName(templ->name, 0)) { + xsltTransformError(NULL, cctxt->style, templNode, + "xsl:template: Attribute 'name': The local part '%s' of " + "the value is not a valid NCName.\n", templ->name); + cctxt->style->errors++; + goto error; + } + if (nameURI != NULL) + templ->nameURI = xmlDictLookup(cctxt->style->dict, nameURI, -1); + curTempl = templ->next; + while (curTempl != NULL) { + if ((nameURI != NULL && xmlStrEqual(curTempl->name, templ->name) && + xmlStrEqual(curTempl->nameURI, nameURI) ) || + (nameURI == NULL && curTempl->nameURI == NULL && + xmlStrEqual(curTempl->name, templ->name))) + { + xsltTransformError(NULL, cctxt->style, templNode, + "xsl:template: error duplicate name '%s'\n", templ->name); + cctxt->style->errors++; + goto error; + } + curTempl = curTempl->next; + } + } + if (templNode->children != NULL) { + xsltParseTemplateContent(cctxt->style, templNode); + /* + * MAYBE TODO: Custom behaviour: In order to stay compatible with + * Xalan and MSXML(.NET), we could allow whitespace + * to appear before an xml:param element; this whitespace + * will additionally become part of the "template". + * NOTE that this is totally deviates from the spec, but + * is the de facto behaviour of Xalan and MSXML(.NET). + * Personally I wouldn't allow this, since if we have: + * + * + * + * + * ... the whitespace between every xsl:param would be + * added to the result tree. + */ + } + + templ->elem = templNode; + templ->content = templNode->children; + xsltAddTemplate(cctxt->style, templ, templ->mode, templ->modeURI); + +error: + xsltCompilerNodePop(cctxt, templNode); + return; +} + +#else /* XSLT_REFACTORED */ + +/** + * xsltParseStylesheetTemplate: + * @style: the XSLT stylesheet + * @template: the "template" element + * + * parse an XSLT stylesheet template building the associated structures + */ + +static void +xsltParseStylesheetTemplate(xsltStylesheetPtr style, xmlNodePtr template) { + xsltTemplatePtr ret; + xmlChar *prop; + xmlChar *mode = NULL; + xmlChar *modeURI = NULL; + double priority; + + if (template == NULL) + return; + + /* + * Create and link the structure + */ + ret = xsltNewTemplate(); + if (ret == NULL) + return; + ret->next = style->templates; + style->templates = ret; + ret->style = style; + + /* + * Get inherited namespaces + */ + /* + * TODO: Apply the optimized in-scope-namespace mechanism + * as for the other XSLT instructions. + */ + xsltGetInheritedNsList(style, ret, template); + + /* + * Get arguments + */ + prop = xmlGetNsProp(template, (const xmlChar *)"mode", NULL); + if (prop != NULL) { + const xmlChar *URI; + + /* + * TODO: Don't use xsltGetQNameURI(). + */ + URI = xsltGetQNameURI(template, &prop); + if (prop == NULL) { + if (style != NULL) style->errors++; + goto error; + } else { + mode = prop; + if (URI != NULL) + modeURI = xmlStrdup(URI); + } + ret->mode = xmlDictLookup(style->dict, mode, -1); + ret->modeURI = xmlDictLookup(style->dict, modeURI, -1); +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseStylesheetTemplate: mode %s\n", mode); +#endif + if (mode != NULL) xmlFree(mode); + if (modeURI != NULL) xmlFree(modeURI); + } + prop = xmlGetNsProp(template, (const xmlChar *)"match", NULL); + if (prop != NULL) { + if (ret->match != NULL) xmlFree(ret->match); + ret->match = prop; + } + + prop = xmlGetNsProp(template, (const xmlChar *)"priority", NULL); + if (prop != NULL) { + priority = xmlXPathStringEvalNumber(prop); + ret->priority = (float) priority; + xmlFree(prop); + } + + prop = xmlGetNsProp(template, (const xmlChar *)"name", NULL); + if (prop != NULL) { + const xmlChar *URI; + xsltTemplatePtr cur; + + /* + * TODO: Don't use xsltGetQNameURI(). + */ + URI = xsltGetQNameURI(template, &prop); + if (prop == NULL) { + if (style != NULL) style->errors++; + goto error; + } else { + if (xmlValidateNCName(prop,0)) { + xsltTransformError(NULL, style, template, + "xsl:template : error invalid name '%s'\n", prop); + if (style != NULL) style->errors++; + goto error; + } + ret->name = xmlDictLookup(style->dict, BAD_CAST prop, -1); + xmlFree(prop); + prop = NULL; + if (URI != NULL) + ret->nameURI = xmlDictLookup(style->dict, BAD_CAST URI, -1); + else + ret->nameURI = NULL; + cur = ret->next; + while (cur != NULL) { + if ((URI != NULL && xmlStrEqual(cur->name, ret->name) && + xmlStrEqual(cur->nameURI, URI) ) || + (URI == NULL && cur->nameURI == NULL && + xmlStrEqual(cur->name, ret->name))) { + xsltTransformError(NULL, style, template, + "xsl:template: error duplicate name '%s'\n", ret->name); + style->errors++; + goto error; + } + cur = cur->next; + } + } + } + + /* + * parse the content and register the pattern + */ + xsltParseTemplateContent(style, template); + ret->elem = template; + ret->content = template->children; + xsltAddTemplate(style, ret, ret->mode, ret->modeURI); + +error: + return; +} + +#endif /* else XSLT_REFACTORED */ + +#ifdef XSLT_REFACTORED + +/** + * xsltIncludeComp: + * @cctxt: the compilation contenxt + * @node: the xsl:include node + * + * Process the xslt include node on the source node + */ +static xsltStyleItemIncludePtr +xsltCompileXSLTIncludeElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) { + xsltStyleItemIncludePtr item; + + if ((cctxt == NULL) || (node == NULL)) + return(NULL); + + node->psvi = NULL; + item = (xsltStyleItemIncludePtr) xmlMalloc(sizeof(xsltStyleItemInclude)); + if (item == NULL) { + xsltTransformError(NULL, cctxt->style, node, + "xsltIncludeComp : malloc failed\n"); + cctxt->style->errors++; + return(NULL); + } + memset(item, 0, sizeof(xsltStyleItemInclude)); + + node->psvi = item; + item->inst = node; + item->type = XSLT_FUNC_INCLUDE; + + item->next = cctxt->style->preComps; + cctxt->style->preComps = (xsltElemPreCompPtr) item; + + return(item); +} + +/** + * xsltParseFindTopLevelElem: + */ +static int +xsltParseFindTopLevelElem(xsltCompilerCtxtPtr cctxt, + xmlNodePtr cur, + const xmlChar *name, + const xmlChar *namespaceURI, + int breakOnOtherElem, + xmlNodePtr *resultNode) +{ + if (name == NULL) + return(-1); + + *resultNode = NULL; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if ((cur->ns != NULL) && (cur->name != NULL)) { + if ((*(cur->name) == *name) && + xmlStrEqual(cur->name, name) && + xmlStrEqual(cur->ns->href, namespaceURI)) + { + *resultNode = cur; + return(1); + } + } + if (breakOnOtherElem) + break; + } + cur = cur->next; + } + *resultNode = cur; + return(0); +} + +static int +xsltParseTopLevelXSLTElem(xsltCompilerCtxtPtr cctxt, + xmlNodePtr node, + xsltStyleType type) +{ + int ret = 0; + + /* + * TODO: The reason why this function exists: + * due to historical reasons some of the + * top-level declarations are processed by functions + * in other files. Since we need still to set + * up the node-info and generate information like + * in-scope namespaces, this is a wrapper around + * those old parsing functions. + */ + xsltCompilerNodePush(cctxt, node); + if (node->nsDef != NULL) + cctxt->inode->inScopeNs = + xsltCompilerBuildInScopeNsList(cctxt, node); + cctxt->inode->type = type; + + switch (type) { + case XSLT_FUNC_INCLUDE: + { + int oldIsInclude; + + if (xsltCompileXSLTIncludeElem(cctxt, node) == NULL) + goto exit; + /* + * Mark this stylesheet tree as being currently included. + */ + oldIsInclude = cctxt->isInclude; + cctxt->isInclude = 1; + + if (xsltParseStylesheetInclude(cctxt->style, node) != 0) { + cctxt->style->errors++; + } + cctxt->isInclude = oldIsInclude; + } + break; + case XSLT_FUNC_PARAM: + xsltStylePreCompute(cctxt->style, node); + xsltParseGlobalParam(cctxt->style, node); + break; + case XSLT_FUNC_VARIABLE: + xsltStylePreCompute(cctxt->style, node); + xsltParseGlobalVariable(cctxt->style, node); + break; + case XSLT_FUNC_ATTRSET: + xsltParseStylesheetAttributeSet(cctxt->style, node); + break; + default: + xsltTransformError(NULL, cctxt->style, node, + "Internal error: (xsltParseTopLevelXSLTElem) " + "Cannot handle this top-level declaration.\n"); + cctxt->style->errors++; + ret = -1; + } + +exit: + xsltCompilerNodePop(cctxt, node); + + return(ret); +} + +#if 0 +static int +xsltParseRemoveWhitespace(xmlNodePtr node) +{ + if ((node == NULL) || (node->children == NULL)) + return(0); + else { + xmlNodePtr delNode = NULL, child = node->children; + + do { + if (delNode) { + xmlUnlinkNode(delNode); + xmlFreeNode(delNode); + delNode = NULL; + } + if (((child->type == XML_TEXT_NODE) || + (child->type == XML_CDATA_SECTION_NODE)) && + (IS_BLANK_NODE(child))) + delNode = child; + child = child->next; + } while (child != NULL); + if (delNode) { + xmlUnlinkNode(delNode); + xmlFreeNode(delNode); + delNode = NULL; + } + } + return(0); +} +#endif + +static int +xsltParseXSLTStylesheetElemCore(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) +{ +#ifdef WITH_XSLT_DEBUG_PARSING + int templates = 0; +#endif + xmlNodePtr cur, start = NULL; + xsltStylesheetPtr style; + + if ((cctxt == NULL) || (node == NULL) || + (node->type != XML_ELEMENT_NODE)) + return(-1); + + style = cctxt->style; + /* + * At this stage all import declarations of all stylesheet modules + * with the same stylesheet level have been processed. + * Now we can safely parse the rest of the declarations. + */ + if (IS_XSLT_ELEM_FAST(node) && IS_XSLT_NAME(node, "include")) + { + xsltDocumentPtr include; + /* + * URGENT TODO: Make this work with simplified stylesheets! + * I.e., when we won't find an xsl:stylesheet element. + */ + /* + * This is as include declaration. + */ + include = ((xsltStyleItemIncludePtr) node->psvi)->include; + if (include == NULL) { + /* TODO: raise error? */ + return(-1); + } + /* + * TODO: Actually an xsl:include should locate an embedded + * stylesheet as well; so the document-element won't always + * be the element where the actual stylesheet is rooted at. + * But such embedded stylesheets are not supported by Libxslt yet. + */ + node = xmlDocGetRootElement(include->doc); + if (node == NULL) { + return(-1); + } + } + + if (node->children == NULL) + return(0); + /* + * Push the xsl:stylesheet/xsl:transform element. + */ + xsltCompilerNodePush(cctxt, node); + cctxt->inode->isRoot = 1; + cctxt->inode->nsChanged = 0; + /* + * Start with the naked dummy info for literal result elements. + */ + cctxt->inode->litResElemInfo = cctxt->inodeList->litResElemInfo; + + /* + * In every case, we need to have + * the in-scope namespaces of the element, where the + * stylesheet is rooted at, regardless if it's an XSLT + * instruction or a literal result instruction (or if + * this is an embedded stylesheet). + */ + cctxt->inode->inScopeNs = + xsltCompilerBuildInScopeNsList(cctxt, node); + + /* + * Process attributes of xsl:stylesheet/xsl:transform. + * -------------------------------------------------- + * Allowed are: + * id = id + * extension-element-prefixes = tokens + * exclude-result-prefixes = tokens + * version = number (mandatory) + */ + if (xsltParseAttrXSLTVersion(cctxt, node, + XSLT_ELEMENT_CATEGORY_XSLT) == 0) + { + /* + * Attribute "version". + * XSLT 1.0: "An xsl:stylesheet element *must* have a version + * attribute, indicating the version of XSLT that the + * stylesheet requires". + * The root element of a simplified stylesheet must also have + * this attribute. + */ +#ifdef XSLT_REFACTORED_MANDATORY_VERSION + if (isXsltElem) + xsltTransformError(NULL, cctxt->style, node, + "The attribute 'version' is missing.\n"); + cctxt->style->errors++; +#else + /* OLD behaviour. */ + xsltTransformError(NULL, cctxt->style, node, + "xsl:version is missing: document may not be a stylesheet\n"); + cctxt->style->warnings++; +#endif + } + /* + * The namespaces declared by the attributes + * "extension-element-prefixes" and + * "exclude-result-prefixes" are local to *this* + * stylesheet tree; i.e., they are *not* visible to + * other stylesheet-modules, whether imported or included. + * + * Attribute "extension-element-prefixes". + */ + cctxt->inode->extElemNs = + xsltParseExtElemPrefixes(cctxt, node, NULL, + XSLT_ELEMENT_CATEGORY_XSLT); + /* + * Attribute "exclude-result-prefixes". + */ + cctxt->inode->exclResultNs = + xsltParseExclResultPrefixes(cctxt, node, NULL, + XSLT_ELEMENT_CATEGORY_XSLT); + /* + * Create/reuse info for the literal result element. + */ + if (cctxt->inode->nsChanged) + xsltLREInfoCreate(cctxt, node, 0); + /* + * Processed top-level elements: + * ---------------------------- + * xsl:variable, xsl:param (QName, in-scope ns, + * expression (vars allowed)) + * xsl:attribute-set (QName, in-scope ns) + * xsl:strip-space, xsl:preserve-space (XPath NameTests, + * in-scope ns) + * I *think* global scope, merge with includes + * xsl:output (QName, in-scope ns) + * xsl:key (QName, in-scope ns, pattern, + * expression (vars *not* allowed)) + * xsl:decimal-format (QName, needs in-scope ns) + * xsl:namespace-alias (in-scope ns) + * global scope, merge with includes + * xsl:template (last, QName, pattern) + * + * (whitespace-only text-nodes have *not* been removed + * yet; this will be done in xsltParseSequenceConstructor) + * + * Report misplaced child-nodes first. + */ + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_TEXT_NODE) { + xsltTransformError(NULL, style, cur, + "Misplaced text node (content: '%s').\n", + (cur->content != NULL) ? cur->content : BAD_CAST ""); + style->errors++; + } else if (cur->type != XML_ELEMENT_NODE) { + xsltTransformError(NULL, style, cur, "Misplaced node.\n"); + style->errors++; + } + cur = cur->next; + } + /* + * Skip xsl:import elements; they have been processed + * already. + */ + cur = node->children; + while ((cur != NULL) && xsltParseFindTopLevelElem(cctxt, cur, + BAD_CAST "import", XSLT_NAMESPACE, 1, &cur) == 1) + cur = cur->next; + if (cur == NULL) + goto exit; + + start = cur; + /* + * Process all top-level xsl:param elements. + */ + while ((cur != NULL) && + xsltParseFindTopLevelElem(cctxt, cur, + BAD_CAST "param", XSLT_NAMESPACE, 0, &cur) == 1) + { + xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_PARAM); + cur = cur->next; + } + /* + * Process all top-level xsl:variable elements. + */ + cur = start; + while ((cur != NULL) && + xsltParseFindTopLevelElem(cctxt, cur, + BAD_CAST "variable", XSLT_NAMESPACE, 0, &cur) == 1) + { + xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_VARIABLE); + cur = cur->next; + } + /* + * Process all the rest of top-level elements. + */ + cur = start; + while (cur != NULL) { + /* + * Process element nodes. + */ + if (cur->type == XML_ELEMENT_NODE) { + if (cur->ns == NULL) { + xsltTransformError(NULL, style, cur, + "Unexpected top-level element in no namespace.\n"); + style->errors++; + cur = cur->next; + continue; + } + /* + * Process all XSLT elements. + */ + if (IS_XSLT_ELEM_FAST(cur)) { + /* + * xsl:import is only allowed at the beginning. + */ + if (IS_XSLT_NAME(cur, "import")) { + xsltTransformError(NULL, style, cur, + "Misplaced xsl:import element.\n"); + style->errors++; + cur = cur->next; + continue; + } + /* + * TODO: Change the return type of the parsing functions + * to int. + */ + if (IS_XSLT_NAME(cur, "template")) { +#ifdef WITH_XSLT_DEBUG_PARSING + templates++; +#endif + /* + * TODO: Is the position of xsl:template in the + * tree significant? If not it would be easier to + * parse them at a later stage. + */ + xsltParseXSLTTemplate(cctxt, cur); + } else if (IS_XSLT_NAME(cur, "variable")) { + /* NOP; done already */ + } else if (IS_XSLT_NAME(cur, "param")) { + /* NOP; done already */ + } else if (IS_XSLT_NAME(cur, "include")) { + if (cur->psvi != NULL) + xsltParseXSLTStylesheetElemCore(cctxt, cur); + else { + xsltTransformError(NULL, style, cur, + "Internal error: " + "(xsltParseXSLTStylesheetElemCore) " + "The xsl:include element was not compiled.\n"); + style->errors++; + } + } else if (IS_XSLT_NAME(cur, "strip-space")) { + /* No node info needed. */ + xsltParseStylesheetStripSpace(style, cur); + } else if (IS_XSLT_NAME(cur, "preserve-space")) { + /* No node info needed. */ + xsltParseStylesheetPreserveSpace(style, cur); + } else if (IS_XSLT_NAME(cur, "output")) { + /* No node-info needed. */ + xsltParseStylesheetOutput(style, cur); + } else if (IS_XSLT_NAME(cur, "key")) { + /* TODO: node-info needed for expressions ? */ + xsltParseStylesheetKey(style, cur); + } else if (IS_XSLT_NAME(cur, "decimal-format")) { + /* No node-info needed. */ + xsltParseStylesheetDecimalFormat(style, cur); + } else if (IS_XSLT_NAME(cur, "attribute-set")) { + xsltParseTopLevelXSLTElem(cctxt, cur, + XSLT_FUNC_ATTRSET); + } else if (IS_XSLT_NAME(cur, "namespace-alias")) { + /* NOP; done already */ + } else { + if (cctxt->inode->forwardsCompat) { + /* + * Forwards-compatible mode: + * + * XSLT-1: "if it is a top-level element and + * XSLT 1.0 does not allow such elements as top-level + * elements, then the element must be ignored along + * with its content;" + */ + /* + * TODO: I don't think we should generate a warning. + */ + xsltTransformError(NULL, style, cur, + "Forwards-compatible mode: Ignoring unknown XSLT " + "element '%s'.\n", cur->name); + style->warnings++; + } else { + xsltTransformError(NULL, style, cur, + "Unknown XSLT element '%s'.\n", cur->name); + style->errors++; + } + } + } else { + xsltTopLevelFunction function; + + /* + * Process non-XSLT elements, which are in a + * non-NULL namespace. + */ + /* + * QUESTION: What does xsltExtModuleTopLevelLookup() + * do exactly? + */ + function = xsltExtModuleTopLevelLookup(cur->name, + cur->ns->href); + if (function != NULL) + function(style, cur); +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseXSLTStylesheetElemCore : User-defined " + "data element '%s'.\n", cur->name); +#endif + } + } + cur = cur->next; + } + +exit: + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "### END of parsing top-level elements of doc '%s'.\n", + node->doc->URL); + xsltGenericDebug(xsltGenericDebugContext, + "### Templates: %d\n", templates); +#ifdef XSLT_REFACTORED + xsltGenericDebug(xsltGenericDebugContext, + "### Max inodes: %d\n", cctxt->maxNodeInfos); + xsltGenericDebug(xsltGenericDebugContext, + "### Max LREs : %d\n", cctxt->maxLREs); +#endif /* XSLT_REFACTORED */ +#endif /* WITH_XSLT_DEBUG_PARSING */ + + xsltCompilerNodePop(cctxt, node); + return(0); +} + +/** + * xsltParseXSLTStylesheet: + * @cctxt: the compiler context + * @node: the xsl:stylesheet/xsl:transform element-node + * + * Parses the xsl:stylesheet and xsl:transform element. + * + * + * + * + * + * BIG TODO: The xsl:include stuff. + * + * Called by xsltParseStylesheetTree() + * + * Returns 0 on success, a positive result on errors and + * -1 on API or internal errors. + */ +static int +xsltParseXSLTStylesheetElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) +{ + xmlNodePtr cur, start; + + if ((cctxt == NULL) || (node == NULL)) + return(-1); + + if (node->children == NULL) + goto exit; + + /* + * Process top-level elements: + * xsl:import (must be first) + * xsl:include (this is just a pre-processing) + */ + cur = node->children; + /* + * Process xsl:import elements. + * XSLT 1.0: "The xsl:import element children must precede all + * other element children of an xsl:stylesheet element, + * including any xsl:include element children." + */ + while ((cur != NULL) && + xsltParseFindTopLevelElem(cctxt, cur, + BAD_CAST "import", XSLT_NAMESPACE, 1, &cur) == 1) + { + if (xsltParseStylesheetImport(cctxt->style, cur) != 0) { + cctxt->style->errors++; + } + cur = cur->next; + } + if (cur == NULL) + goto exit; + start = cur; + /* + * Pre-process all xsl:include elements. + */ + cur = start; + while ((cur != NULL) && + xsltParseFindTopLevelElem(cctxt, cur, + BAD_CAST "include", XSLT_NAMESPACE, 0, &cur) == 1) + { + xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_INCLUDE); + cur = cur->next; + } + /* + * Pre-process all xsl:namespace-alias elements. + * URGENT TODO: This won't work correctly: the order of included + * aliases and aliases defined here is significant. + */ + cur = start; + while ((cur != NULL) && + xsltParseFindTopLevelElem(cctxt, cur, + BAD_CAST "namespace-alias", XSLT_NAMESPACE, 0, &cur) == 1) + { + xsltNamespaceAlias(cctxt->style, cur); + cur = cur->next; + } + + if (cctxt->isInclude) { + /* + * If this stylesheet is intended for inclusion, then + * we will process only imports and includes. + */ + goto exit; + } + /* + * Now parse the rest of the top-level elements. + */ + xsltParseXSLTStylesheetElemCore(cctxt, node); +exit: + + return(0); +} + +#else /* XSLT_REFACTORED */ + +/** + * xsltParseStylesheetTop: + * @style: the XSLT stylesheet + * @top: the top level "stylesheet" or "transform" element + * + * scan the top level elements of an XSL stylesheet + */ +static void +xsltParseStylesheetTop(xsltStylesheetPtr style, xmlNodePtr top) { + xmlNodePtr cur; + xmlChar *prop; +#ifdef WITH_XSLT_DEBUG_PARSING + int templates = 0; +#endif + + if (top == NULL) + return; + + prop = xmlGetNsProp(top, (const xmlChar *)"version", NULL); + if (prop == NULL) { + xsltTransformError(NULL, style, top, + "xsl:version is missing: document may not be a stylesheet\n"); + if (style != NULL) style->warnings++; + } else { + if ((!xmlStrEqual(prop, (const xmlChar *)"1.0")) && + (!xmlStrEqual(prop, (const xmlChar *)"1.1"))) { + xsltTransformError(NULL, style, top, + "xsl:version: only 1.0 features are supported\n"); + /* TODO set up compatibility when not XSLT 1.0 */ + if (style != NULL) style->warnings++; + } + xmlFree(prop); + } + + cur = top->children; + + /* + * process xsl:import elements + */ + while (cur != NULL) { + if (IS_BLANK_NODE(cur)) { + cur = cur->next; + continue; + } + if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "import")) { + if (xsltParseStylesheetImport(style, cur) != 0) + if (style != NULL) style->errors++; + } else + break; + cur = cur->next; + } + + /* + * process other top-level elements + */ + while (cur != NULL) { + if (IS_BLANK_NODE(cur)) { + cur = cur->next; + continue; + } + if (cur->type == XML_TEXT_NODE) { + if (cur->content != NULL) { + xsltTransformError(NULL, style, cur, + "misplaced text node: '%s'\n", cur->content); + } + if (style != NULL) style->errors++; + cur = cur->next; + continue; + } + if ((cur->type == XML_ELEMENT_NODE) && (cur->ns == NULL)) { + xsltGenericError(xsltGenericErrorContext, + "Found a top-level element %s with null namespace URI\n", + cur->name); + if (style != NULL) style->errors++; + cur = cur->next; + continue; + } + if ((cur->type == XML_ELEMENT_NODE) && (!(IS_XSLT_ELEM(cur)))) { + xsltTopLevelFunction function; + + function = xsltExtModuleTopLevelLookup(cur->name, + cur->ns->href); + if (function != NULL) + function(style, cur); + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseStylesheetTop : found foreign element %s\n", + cur->name); +#endif + cur = cur->next; + continue; + } + if (IS_XSLT_NAME(cur, "import")) { + xsltTransformError(NULL, style, cur, + "xsltParseStylesheetTop: ignoring misplaced import element\n"); + if (style != NULL) style->errors++; + } else if (IS_XSLT_NAME(cur, "include")) { + if (xsltParseStylesheetInclude(style, cur) != 0) + if (style != NULL) style->errors++; + } else if (IS_XSLT_NAME(cur, "strip-space")) { + xsltParseStylesheetStripSpace(style, cur); + } else if (IS_XSLT_NAME(cur, "preserve-space")) { + xsltParseStylesheetPreserveSpace(style, cur); + } else if (IS_XSLT_NAME(cur, "output")) { + xsltParseStylesheetOutput(style, cur); + } else if (IS_XSLT_NAME(cur, "key")) { + xsltParseStylesheetKey(style, cur); + } else if (IS_XSLT_NAME(cur, "decimal-format")) { + xsltParseStylesheetDecimalFormat(style, cur); + } else if (IS_XSLT_NAME(cur, "attribute-set")) { + xsltParseStylesheetAttributeSet(style, cur); + } else if (IS_XSLT_NAME(cur, "variable")) { + xsltParseGlobalVariable(style, cur); + } else if (IS_XSLT_NAME(cur, "param")) { + xsltParseGlobalParam(style, cur); + } else if (IS_XSLT_NAME(cur, "template")) { +#ifdef WITH_XSLT_DEBUG_PARSING + templates++; +#endif + xsltParseStylesheetTemplate(style, cur); + } else if (IS_XSLT_NAME(cur, "namespace-alias")) { + xsltNamespaceAlias(style, cur); + } else { + /* + * BUG TODO: The version of the *doc* is irrelevant for + * the forwards-compatible mode. + */ + if ((style != NULL) && (style->doc->version != NULL) && + (!strncmp((const char *) style->doc->version, "1.0", 3))) { + xsltTransformError(NULL, style, cur, + "xsltParseStylesheetTop: unknown %s element\n", + cur->name); + if (style != NULL) style->errors++; + } + else { + /* do Forwards-Compatible Processing */ + xsltTransformError(NULL, style, cur, + "xsltParseStylesheetTop: ignoring unknown %s element\n", + cur->name); + if (style != NULL) style->warnings++; + } + } + cur = cur->next; + } +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "parsed %d templates\n", templates); +#endif +} + +#endif /* else of XSLT_REFACTORED */ + +#ifdef XSLT_REFACTORED +/** + * xsltParseSimplifiedStylesheetTree: + * + * @style: the stylesheet (TODO: Change this to the compiler context) + * @doc: the document containing the stylesheet. + * @node: the node where the stylesheet is rooted at + * + * Returns 0 in case of success, a positive result if an error occurred + * and -1 on API and internal errors. + */ +static int +xsltParseSimplifiedStylesheetTree(xsltCompilerCtxtPtr cctxt, + xmlDocPtr doc, + xmlNodePtr node) +{ + xsltTemplatePtr templ; + + if ((cctxt == NULL) || (node == NULL)) + return(-1); + + if (xsltParseAttrXSLTVersion(cctxt, node, 0) == XSLT_ELEMENT_CATEGORY_LRE) + { + /* + * TODO: Adjust report, since this might be an + * embedded stylesheet. + */ + xsltTransformError(NULL, cctxt->style, node, + "The attribute 'xsl:version' is missing; cannot identify " + "this document as an XSLT stylesheet document.\n"); + cctxt->style->errors++; + return(1); + } + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseSimplifiedStylesheetTree: document is stylesheet\n"); +#endif + + /* + * Create and link the template + */ + templ = xsltNewTemplate(); + if (templ == NULL) { + return(-1); + } + templ->next = cctxt->style->templates; + cctxt->style->templates = templ; + templ->match = xmlStrdup(BAD_CAST "/"); + + /* + * Note that we push the document-node in this special case. + */ + xsltCompilerNodePush(cctxt, (xmlNodePtr) doc); + /* + * In every case, we need to have + * the in-scope namespaces of the element, where the + * stylesheet is rooted at, regardless if it's an XSLT + * instruction or a literal result instruction (or if + * this is an embedded stylesheet). + */ + cctxt->inode->inScopeNs = + xsltCompilerBuildInScopeNsList(cctxt, node); + /* + * Parse the content and register the match-pattern. + */ + xsltParseSequenceConstructor(cctxt, node); + xsltCompilerNodePop(cctxt, (xmlNodePtr) doc); + + templ->elem = (xmlNodePtr) doc; + templ->content = node; + xsltAddTemplate(cctxt->style, templ, NULL, NULL); + cctxt->style->literal_result = 1; + return(0); +} + +#ifdef XSLT_REFACTORED_XSLT_NSCOMP +int +xsltRestoreDocumentNamespaces(xsltNsMapPtr ns, xmlDocPtr doc) +{ + if (doc == NULL) + return(-1); + /* + * Revert the changes we have applied to the namespace-URIs of + * ns-decls. + */ + while (ns != NULL) { + if ((ns->doc == doc) && (ns->ns != NULL)) { + ns->ns->href = ns->origNsName; + ns->origNsName = NULL; + ns->ns = NULL; + } + ns = ns->next; + } + return(0); +} +#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ + +/** + * xsltParseStylesheetProcess: + * @style: the XSLT stylesheet (the current stylesheet-level) + * @doc: and xmlDoc parsed XML + * + * Parses an XSLT stylesheet, adding the associated structures. + * Called by: + * xsltParseStylesheetImportedDoc() (xslt.c) + * xsltParseStylesheetInclude() (imports.c) + * + * Returns the value of the @style parameter if everything + * went right, NULL if something went amiss. + */ +xsltStylesheetPtr +xsltParseStylesheetProcess(xsltStylesheetPtr style, xmlDocPtr doc) +{ + xsltCompilerCtxtPtr cctxt; + xmlNodePtr cur; + int oldIsSimplifiedStylesheet; + + + if ((style == NULL) || (doc == NULL)) + return(NULL); + + cctxt = XSLT_CCTXT(style); + + cur = xmlDocGetRootElement(doc); + if (cur == NULL) { + xsltTransformError(NULL, style, (xmlNodePtr) doc, + "xsltParseStylesheetProcess : empty stylesheet\n"); + return(NULL); + } + oldIsSimplifiedStylesheet = cctxt->simplified; + + if ((IS_XSLT_ELEM(cur)) && + ((IS_XSLT_NAME(cur, "stylesheet")) || + (IS_XSLT_NAME(cur, "transform")))) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseStylesheetProcess : found stylesheet\n"); +#endif + cctxt->simplified = 0; + style->literal_result = 0; + } else { + cctxt->simplified = 1; + style->literal_result = 1; + } + /* + * Pre-process the stylesheet if not already done before. + * This will remove PIs and comments, merge adjacent + * text nodes, internalize strings, etc. + */ + if (! style->nopreproc) + xsltParsePreprocessStylesheetTree(cctxt, cur); + /* + * Parse and compile the stylesheet. + */ + if (style->literal_result == 0) { + if (xsltParseXSLTStylesheetElem(cctxt, cur) != 0) + return(NULL); + } else { + if (xsltParseSimplifiedStylesheetTree(cctxt, doc, cur) != 0) + return(NULL); + } + + cctxt->simplified = oldIsSimplifiedStylesheet; + + return(style); +} + +#else /* XSLT_REFACTORED */ + +xsltStylesheetPtr +xsltParseStylesheetProcess(xsltStylesheetPtr ret, xmlDocPtr doc) { + xmlNodePtr cur; + + if (doc == NULL) + return(NULL); + if (ret == NULL) + return(ret); + + /* + * First steps, remove blank nodes, + * locate the xsl:stylesheet element and the + * namespace declaration. + */ + cur = xmlDocGetRootElement(doc); + if (cur == NULL) { + xsltTransformError(NULL, ret, (xmlNodePtr) doc, + "xsltParseStylesheetProcess : empty stylesheet\n"); + return(NULL); + } + + if ((IS_XSLT_ELEM(cur)) && + ((IS_XSLT_NAME(cur, "stylesheet")) || + (IS_XSLT_NAME(cur, "transform")))) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseStylesheetProcess : found stylesheet\n"); +#endif + ret->literal_result = 0; + xsltParseStylesheetExcludePrefix(ret, cur, 1); + xsltParseStylesheetExtPrefix(ret, cur, 1); + } else { + xsltParseStylesheetExcludePrefix(ret, cur, 0); + xsltParseStylesheetExtPrefix(ret, cur, 0); + ret->literal_result = 1; + } + if (!ret->nopreproc) { + xsltPrecomputeStylesheet(ret, cur); + } + if (ret->literal_result == 0) { + xsltParseStylesheetTop(ret, cur); + } else { + xmlChar *prop; + xsltTemplatePtr template; + + /* + * the document itself might be the template, check xsl:version + */ + prop = xmlGetNsProp(cur, (const xmlChar *)"version", XSLT_NAMESPACE); + if (prop == NULL) { + xsltTransformError(NULL, ret, cur, + "xsltParseStylesheetProcess : document is not a stylesheet\n"); + return(NULL); + } + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseStylesheetProcess : document is stylesheet\n"); +#endif + + if (!xmlStrEqual(prop, (const xmlChar *)"1.0")) { + xsltTransformError(NULL, ret, cur, + "xsl:version: only 1.0 features are supported\n"); + /* TODO set up compatibility when not XSLT 1.0 */ + ret->warnings++; + } + xmlFree(prop); + + /* + * Create and link the template + */ + template = xsltNewTemplate(); + if (template == NULL) { + return(NULL); + } + template->next = ret->templates; + ret->templates = template; + template->match = xmlStrdup((const xmlChar *)"/"); + + /* + * parse the content and register the pattern + */ + xsltParseTemplateContent(ret, (xmlNodePtr) doc); + template->elem = (xmlNodePtr) doc; + template->content = doc->children; + xsltAddTemplate(ret, template, NULL, NULL); + ret->literal_result = 1; + } + + return(ret); +} + +#endif /* else of XSLT_REFACTORED */ + +/** + * xsltParseStylesheetImportedDoc: + * @doc: an xmlDoc parsed XML + * @style: pointer to the parent stylesheet (if it exists) + * + * parse an XSLT stylesheet building the associated structures + * except the processing not needed for imported documents. + * + * Returns a new XSLT stylesheet structure. + */ + +xsltStylesheetPtr +xsltParseStylesheetImportedDoc(xmlDocPtr doc, + xsltStylesheetPtr parentStyle) { + xsltStylesheetPtr retStyle; + + if (doc == NULL) + return(NULL); + + retStyle = xsltNewStylesheet(); + if (retStyle == NULL) + return(NULL); + /* + * Set the importing stylesheet module; also used to detect recursion. + */ + retStyle->parent = parentStyle; + /* + * Adjust the string dict. + */ + if (doc->dict != NULL) { + xmlDictFree(retStyle->dict); + retStyle->dict = doc->dict; +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing dictionary from %s for stylesheet\n", + doc->URL); +#endif + xmlDictReference(retStyle->dict); + } + + /* + * TODO: Eliminate xsltGatherNamespaces(); we must not restrict + * the stylesheet to containt distinct namespace prefixes. + */ + xsltGatherNamespaces(retStyle); + +#ifdef XSLT_REFACTORED + { + xsltCompilerCtxtPtr cctxt; + xsltStylesheetPtr oldCurSheet; + + if (parentStyle == NULL) { + xsltPrincipalStylesheetDataPtr principalData; + /* + * Principal stylesheet + * -------------------- + */ + retStyle->principal = retStyle; + /* + * Create extra data for the principal stylesheet. + */ + principalData = xsltNewPrincipalStylesheetData(); + if (principalData == NULL) { + xsltFreeStylesheet(retStyle); + return(NULL); + } + retStyle->principalData = principalData; + /* + * Create the compilation context + * ------------------------------ + * (only once; for the principal stylesheet). + * This is currently the only function where the + * compilation context is created. + */ + cctxt = xsltCompilationCtxtCreate(retStyle); + if (cctxt == NULL) { + xsltFreeStylesheet(retStyle); + return(NULL); + } + retStyle->compCtxt = (void *) cctxt; + cctxt->style = retStyle; + cctxt->dict = retStyle->dict; + cctxt->psData = principalData; + /* + * Push initial dummy node info. + */ + cctxt->depth = -1; + xsltCompilerNodePush(cctxt, (xmlNodePtr) doc); + } else { + /* + * Imported stylesheet. + */ + retStyle->principal = parentStyle->principal; + cctxt = parentStyle->compCtxt; + retStyle->compCtxt = cctxt; + } + /* + * Save the old and set the current stylesheet structure in the + * compilation context. + */ + oldCurSheet = cctxt->style; + cctxt->style = retStyle; + + retStyle->doc = doc; + xsltParseStylesheetProcess(retStyle, doc); + + cctxt->style = oldCurSheet; + if (parentStyle == NULL) { + /* + * Pop the initial dummy node info. + */ + xsltCompilerNodePop(cctxt, (xmlNodePtr) doc); + } else { + /* + * Clear the compilation context of imported + * stylesheets. + * TODO: really? + */ + /* retStyle->compCtxt = NULL; */ + } + /* + * Free the stylesheet if there were errors. + */ + if (retStyle != NULL) { + if (retStyle->errors != 0) { +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + /* + * Restore all changes made to namespace URIs of ns-decls. + */ + if (cctxt->psData->nsMap) + xsltRestoreDocumentNamespaces(cctxt->psData->nsMap, doc); +#endif + /* + * Detach the doc from the stylesheet; otherwise the doc + * will be freed in xsltFreeStylesheet(). + */ + retStyle->doc = NULL; + /* + * Cleanup the doc if its the main stylesheet. + */ + if (parentStyle == NULL) { + xsltCleanupStylesheetTree(doc, xmlDocGetRootElement(doc)); + if (retStyle->compCtxt != NULL) { + xsltCompilationCtxtFree(retStyle->compCtxt); + retStyle->compCtxt = NULL; + } + } + + xsltFreeStylesheet(retStyle); + retStyle = NULL; + } + } + } + +#else /* XSLT_REFACTORED */ + /* + * Old behaviour. + */ + retStyle->doc = doc; + xsltParseStylesheetProcess(retStyle, doc); + if (retStyle != NULL) { + if (retStyle->errors != 0) { + retStyle->doc = NULL; + if (parentStyle == NULL) + xsltCleanupStylesheetTree(doc, + xmlDocGetRootElement(doc)); + xsltFreeStylesheet(retStyle); + retStyle = NULL; + } + } +#endif /* else of XSLT_REFACTORED */ + + return(retStyle); +} + +/** + * xsltParseStylesheetDoc: + * @doc: and xmlDoc parsed XML + * + * parse an XSLT stylesheet building the associated structures + * + * Returns a new XSLT stylesheet structure. + */ + +xsltStylesheetPtr +xsltParseStylesheetDoc(xmlDocPtr doc) { + xsltStylesheetPtr ret; + + ret = xsltParseStylesheetImportedDoc(doc, NULL); + if (ret == NULL) + return(NULL); + + xsltResolveStylesheetAttributeSet(ret); +#ifdef XSLT_REFACTORED + /* + * Free the compilation context. + * TODO: Check if it's better to move this cleanup to + * xsltParseStylesheetImportedDoc(). + */ + if (ret->compCtxt != NULL) { + xsltCompilationCtxtFree(XSLT_CCTXT(ret)); + ret->compCtxt = NULL; + } +#endif + return(ret); +} + +/** + * xsltParseStylesheetFile: + * @filename: the filename/URL to the stylesheet + * + * Load and parse an XSLT stylesheet + * + * Returns a new XSLT stylesheet structure. + */ + +xsltStylesheetPtr +xsltParseStylesheetFile(const xmlChar* filename) { + xsltSecurityPrefsPtr sec; + xsltStylesheetPtr ret; + xmlDocPtr doc; + + + if (filename == NULL) + return(NULL); + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseStylesheetFile : parse %s\n", filename); +#endif + + /* + * Security framework check + */ + sec = xsltGetDefaultSecurityPrefs(); + if (sec != NULL) { + int res; + + res = xsltCheckRead(sec, NULL, filename); + if (res == 0) { + xsltTransformError(NULL, NULL, NULL, + "xsltParseStylesheetFile: read rights for %s denied\n", + filename); + return(NULL); + } + } + + doc = xsltDocDefaultLoader(filename, NULL, XSLT_PARSE_OPTIONS, + NULL, XSLT_LOAD_START); + if (doc == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltParseStylesheetFile : cannot parse %s\n", filename); + return(NULL); + } + ret = xsltParseStylesheetDoc(doc); + if (ret == NULL) { + xmlFreeDoc(doc); + return(NULL); + } + + return(ret); +} + +/************************************************************************ + * * + * Handling of Stylesheet PI * + * * + ************************************************************************/ + +#define CUR (*cur) +#define SKIP(val) cur += (val) +#define NXT(val) cur[(val)] +#define SKIP_BLANKS \ + while (IS_BLANK(CUR)) NEXT +#define NEXT ((*cur) ? cur++ : cur) + +/** + * xsltParseStylesheetPI: + * @value: the value of the PI + * + * This function checks that the type is text/xml and extracts + * the URI-Reference for the stylesheet + * + * Returns the URI-Reference for the stylesheet or NULL (it need to + * be freed by the caller) + */ +static xmlChar * +xsltParseStylesheetPI(const xmlChar *value) { + const xmlChar *cur; + const xmlChar *start; + xmlChar *val; + xmlChar tmp; + xmlChar *href = NULL; + int isXml = 0; + + if (value == NULL) + return(NULL); + + cur = value; + while (CUR != 0) { + SKIP_BLANKS; + if ((CUR == 't') && (NXT(1) == 'y') && (NXT(2) == 'p') && + (NXT(3) == 'e')) { + SKIP(4); + SKIP_BLANKS; + if (CUR != '=') + continue; + NEXT; + if ((CUR != '\'') && (CUR != '"')) + continue; + tmp = CUR; + NEXT; + start = cur; + while ((CUR != 0) && (CUR != tmp)) + NEXT; + if (CUR != tmp) + continue; + val = xmlStrndup(start, cur - start); + NEXT; + if (val == NULL) + return(NULL); + if ((xmlStrcasecmp(val, BAD_CAST "text/xml")) && + (xmlStrcasecmp(val, BAD_CAST "text/xsl"))) { + xmlFree(val); + break; + } + isXml = 1; + xmlFree(val); + } else if ((CUR == 'h') && (NXT(1) == 'r') && (NXT(2) == 'e') && + (NXT(3) == 'f')) { + SKIP(4); + SKIP_BLANKS; + if (CUR != '=') + continue; + NEXT; + if ((CUR != '\'') && (CUR != '"')) + continue; + tmp = CUR; + NEXT; + start = cur; + while ((CUR != 0) && (CUR != tmp)) + NEXT; + if (CUR != tmp) + continue; + if (href == NULL) + href = xmlStrndup(start, cur - start); + NEXT; + } else { + while ((CUR != 0) && (!IS_BLANK(CUR))) + NEXT; + } + + } + + if (!isXml) { + if (href != NULL) + xmlFree(href); + href = NULL; + } + return(href); +} + +/** + * xsltLoadStylesheetPI: + * @doc: a document to process + * + * This function tries to locate the stylesheet PI in the given document + * If found, and if contained within the document, it will extract + * that subtree to build the stylesheet to process @doc (doc itself will + * be modified). If found but referencing an external document it will + * attempt to load it and generate a stylesheet from it. In both cases, + * the resulting stylesheet and the document need to be freed once the + * transformation is done. + * + * Returns a new XSLT stylesheet structure or NULL if not found. + */ +xsltStylesheetPtr +xsltLoadStylesheetPI(xmlDocPtr doc) { + xmlNodePtr child; + xsltStylesheetPtr ret = NULL; + xmlChar *href = NULL; + xmlURIPtr URI; + + if (doc == NULL) + return(NULL); + + /* + * Find the text/xml stylesheet PI id any before the root + */ + child = doc->children; + while ((child != NULL) && (child->type != XML_ELEMENT_NODE)) { + if ((child->type == XML_PI_NODE) && + (xmlStrEqual(child->name, BAD_CAST "xml-stylesheet"))) { + href = xsltParseStylesheetPI(child->content); + if (href != NULL) + break; + } + child = child->next; + } + + /* + * If found check the href to select processing + */ + if (href != NULL) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltLoadStylesheetPI : found PI href=%s\n", href); +#endif + URI = xmlParseURI((const char *) href); + if (URI == NULL) { + xsltTransformError(NULL, NULL, child, + "xml-stylesheet : href %s is not valid\n", href); + xmlFree(href); + return(NULL); + } + if ((URI->fragment != NULL) && (URI->scheme == NULL) && + (URI->opaque == NULL) && (URI->authority == NULL) && + (URI->server == NULL) && (URI->user == NULL) && + (URI->path == NULL) && (URI->query == NULL)) { + xmlAttrPtr ID; + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltLoadStylesheetPI : Reference to ID %s\n", href); +#endif + if (URI->fragment[0] == '#') + ID = xmlGetID(doc, (const xmlChar *) &(URI->fragment[1])); + else + ID = xmlGetID(doc, (const xmlChar *) URI->fragment); + if (ID == NULL) { + xsltTransformError(NULL, NULL, child, + "xml-stylesheet : no ID %s found\n", URI->fragment); + } else { + xmlDocPtr fake; + xmlNodePtr subtree; + + /* + * move the subtree in a new document passed to + * the stylesheet analyzer + */ + subtree = ID->parent; + fake = xmlNewDoc(NULL); + if (fake != NULL) { + /* + * the dictionnary should be shared since nodes are + * moved over. + */ + fake->dict = doc->dict; + xmlDictReference(doc->dict); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing dictionary from %s for stylesheet\n", + doc->URL); +#endif + + xmlUnlinkNode(subtree); + xmlAddChild((xmlNodePtr) fake, subtree); + ret = xsltParseStylesheetDoc(fake); + if (ret == NULL) + xmlFreeDoc(fake); + } + } + } else { + xmlChar *URL, *base; + + /* + * Reference to an external stylesheet + */ + + base = xmlNodeGetBase(doc, (xmlNodePtr) doc); + URL = xmlBuildURI(href, base); + if (URL != NULL) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltLoadStylesheetPI : fetching %s\n", URL); +#endif + ret = xsltParseStylesheetFile(URL); + xmlFree(URL); + } else { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltLoadStylesheetPI : fetching %s\n", href); +#endif + ret = xsltParseStylesheetFile(href); + } + if (base != NULL) + xmlFree(base); + } + xmlFreeURI(URI); + xmlFree(href); + } + return(ret); +} diff --git a/libxslt/xsltInternals.h b/libxslt/xsltInternals.h index 89c068b3..fc60961c 100644 --- a/libxslt/xsltInternals.h +++ b/libxslt/xsltInternals.h @@ -1,1828 +1,1915 @@ -/* - * Summary: internal data structures, constants and functions - * Description: Internal data structures, constants and functions used - * by the XSLT engine. - * They are not part of the API or ABI, i.e. they can change - * without prior notice, use carefully. - * - * Copy: See Copyright for the status of this software. - * - * Author: Daniel Veillard - */ - -#ifndef __XML_XSLT_INTERNALS_H__ -#define __XML_XSLT_INTERNALS_H__ - -#include -#include -#include -#include -#include -#include -#include -#include "xsltexports.h" -#include "numbersInternals.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define XSLT_IS_TEXT_NODE(n) ((n != NULL) && \ - (((n)->type == XML_TEXT_NODE) || \ - ((n)->type == XML_CDATA_SECTION_NODE))) - - -#if 0 - -extern const xmlChar *xsltDocFragFake; - -#define XSLT_MARK_RES_TREE_FRAG(n) (n)->psvi = (void *) xsltDocFragFake; - -#define XSLT_IS_RES_TREE_FRAG(n) \ - ((n != NULL) && ((n)->type == XML_DOCUMENT_NODE) && \ - ((n)->psvi == xsltDocFragFake)) - -#else - -#define XSLT_MARK_RES_TREE_FRAG(n) \ - (n)->name = (char *) xmlStrdup(BAD_CAST " fake node libxslt"); - -#define XSLT_IS_RES_TREE_FRAG(n) \ - ((n != NULL) && ((n)->type == XML_DOCUMENT_NODE) && \ - ((n)->name != NULL) && ((n)->name[0] == ' ') && \ - xmlStrEqual(BAD_CAST (n)->name, BAD_CAST " fake node libxslt")) - -#endif - -/** - * XSLT_REFACTORED_KEYCOMP: - * - * Internal define to enable on-demand xsl:key computation. - */ -#define XSLT_REFACTORED_KEYCOMP - -/** - * XSLT_REFACTORED: - * - * Internal define to enable the refactored parts of Libxslt. - */ -/* #define XSLT_REFACTORED */ -/* ==================================================================== */ - -#ifdef XSLT_REFACTORED - -extern const xmlChar *xsltXSLTAttrMarker; - -/* TODO: REMOVE: #define XSLT_REFACTORED_EXCLRESNS */ - -/* TODO: REMOVE: #define XSLT_REFACTORED_NSALIAS */ - -/** - * XSLT_REFACTORED_XSLT_NSCOMP - * - * Internal define to enable the pointer-comparison of - * namespaces of XSLT elements. - */ -/* #define XSLT_REFACTORED_XSLT_NSCOMP */ - -/** - * XSLT_REFACTORED_XPATHCOMP - * - * Internal define to enable the optimization of the - * compilation of XPath expressions. - */ -#define XSLT_REFACTORED_XPATHCOMP - -#ifdef XSLT_REFACTORED_XSLT_NSCOMP - -extern const xmlChar *xsltConstNamespaceNameXSLT; - -#define IS_XSLT_ELEM_FAST(n) \ - (((n) != NULL) && ((n)->ns != NULL) && \ - ((n)->ns->href == xsltConstNamespaceNameXSLT)) - -#define IS_XSLT_ATTR_FAST(a) \ - (((a) != NULL) && ((a)->ns != NULL) && \ - ((a)->ns->href == xsltConstNamespaceNameXSLT)) - -#define XSLT_HAS_INTERNAL_NSMAP(s) \ - (((s) != NULL) && ((s)->principal) && \ - ((s)->principal->principalData) && \ - ((s)->principal->principalData->nsMap)) - -#define XSLT_GET_INTERNAL_NSMAP(s) ((s)->principal->principalData->nsMap) - -#else /* XSLT_REFACTORED_XSLT_NSCOMP */ - -#define IS_XSLT_ELEM_FAST(n) \ - (((n) != NULL) && ((n)->ns != NULL) && \ - (xmlStrEqual((n)->ns->href, XSLT_NAMESPACE))) - -#define IS_XSLT_ATTR_FAST(a) \ - (((a) != NULL) && ((a)->ns != NULL) && \ - (xmlStrEqual((a)->ns->href, XSLT_NAMESPACE))) - - -#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ - - -/** - * XSLT_REFACTORED_MANDATORY_VERSION: - * - * TODO: Currently disabled to surpress regression test failures, since - * the old behaviour was that a missing version attribute - * produced a only a warning and not an error, which was incerrect. - * So the regression tests need to be fixed if this is enabled. - */ -/* #define XSLT_REFACTORED_MANDATORY_VERSION */ - -/** - * xsltPointerList: - * - * Pointer-list for various purposes. - */ -typedef struct _xsltPointerList xsltPointerList; -typedef xsltPointerList *xsltPointerListPtr; -struct _xsltPointerList { - void **items; - int number; - int size; -}; - -#endif - -/** - * XSLT_REFACTORED_PARSING: - * - * Internal define to enable the refactored parts of Libxslt - * related to parsing. - */ -/* #define XSLT_REFACTORED_PARSING */ - -/** - * XSLT_MAX_SORT: - * - * Max number of specified xsl:sort on an element. - */ -#define XSLT_MAX_SORT 15 - -/** - * XSLT_PAT_NO_PRIORITY: - * - * Specific value for pattern without priority expressed. - */ -#define XSLT_PAT_NO_PRIORITY -12345789 - -/** - * xsltRuntimeExtra: - * - * Extra information added to the transformation context. - */ -typedef struct _xsltRuntimeExtra xsltRuntimeExtra; -typedef xsltRuntimeExtra *xsltRuntimeExtraPtr; -struct _xsltRuntimeExtra { - void *info; /* pointer to the extra data */ - xmlFreeFunc deallocate; /* pointer to the deallocation routine */ - union { /* dual-purpose field */ - void *ptr; /* data not needing deallocation */ - int ival; /* integer value storage */ - } val; -}; - -/** - * XSLT_RUNTIME_EXTRA_LST: - * @ctxt: the transformation context - * @nr: the index - * - * Macro used to access extra information stored in the context - */ -#define XSLT_RUNTIME_EXTRA_LST(ctxt, nr) (ctxt)->extras[(nr)].info -/** - * XSLT_RUNTIME_EXTRA_FREE: - * @ctxt: the transformation context - * @nr: the index - * - * Macro used to free extra information stored in the context - */ -#define XSLT_RUNTIME_EXTRA_FREE(ctxt, nr) (ctxt)->extras[(nr)].deallocate -/** - * XSLT_RUNTIME_EXTRA: - * @ctxt: the transformation context - * @nr: the index - * - * Macro used to define extra information stored in the context - */ -#define XSLT_RUNTIME_EXTRA(ctxt, nr, typ) (ctxt)->extras[(nr)].val.typ - -/** - * xsltTemplate: - * - * The in-memory structure corresponding to an XSLT Template. - */ -typedef struct _xsltTemplate xsltTemplate; -typedef xsltTemplate *xsltTemplatePtr; -struct _xsltTemplate { - struct _xsltTemplate *next;/* chained list sorted by priority */ - struct _xsltStylesheet *style;/* the containing stylesheet */ - xmlChar *match; /* the matching string */ - float priority; /* as given from the stylesheet, not computed */ - const xmlChar *name; /* the local part of the name QName */ - const xmlChar *nameURI; /* the URI part of the name QName */ - const xmlChar *mode;/* the local part of the mode QName */ - const xmlChar *modeURI;/* the URI part of the mode QName */ - xmlNodePtr content; /* the template replacement value */ - xmlNodePtr elem; /* the source element */ - - /* - * TODO: @inheritedNsNr and @inheritedNs won't be used in the - * refactored code. - */ - int inheritedNsNr; /* number of inherited namespaces */ - xmlNsPtr *inheritedNs;/* inherited non-excluded namespaces */ - - /* Profiling informations */ - int nbCalls; /* the number of time the template was called */ - unsigned long time; /* the time spent in this template */ -}; - -/** - * xsltDecimalFormat: - * - * Data structure of decimal-format. - */ -typedef struct _xsltDecimalFormat xsltDecimalFormat; -typedef xsltDecimalFormat *xsltDecimalFormatPtr; -struct _xsltDecimalFormat { - struct _xsltDecimalFormat *next; /* chained list */ - xmlChar *name; - /* Used for interpretation of pattern */ - xmlChar *digit; - xmlChar *patternSeparator; - /* May appear in result */ - xmlChar *minusSign; - xmlChar *infinity; - xmlChar *noNumber; /* Not-a-number */ - /* Used for interpretation of pattern and may appear in result */ - xmlChar *decimalPoint; - xmlChar *grouping; - xmlChar *percent; - xmlChar *permille; - xmlChar *zeroDigit; -}; - -/** - * xsltDocument: - * - * Data structure associated to a parsed document. - */ - -typedef struct _xsltDocument xsltDocument; -typedef xsltDocument *xsltDocumentPtr; -struct _xsltDocument { - struct _xsltDocument *next; /* documents are kept in a chained list */ - int main; /* is this the main document */ - xmlDocPtr doc; /* the parsed document */ - void *keys; /* key tables storage */ - struct _xsltDocument *includes; /* subsidiary includes */ - int preproc; /* pre-processing already done */ - int nbKeysComputed; -}; - -/** - * xsltKeyDef: - * - * Representation of an xsl:key. - */ -typedef struct _xsltKeyDef xsltKeyDef; -typedef xsltKeyDef *xsltKeyDefPtr; -struct _xsltKeyDef { - struct _xsltKeyDef *next; - xmlNodePtr inst; - xmlChar *name; - xmlChar *nameURI; - xmlChar *match; - xmlChar *use; - xmlXPathCompExprPtr comp; - xmlXPathCompExprPtr usecomp; - xmlNsPtr *nsList; /* the namespaces in scope */ - int nsNr; /* the number of namespaces in scope */ -}; - -/** - * xsltKeyTable: - * - * Holds the computed keys for key definitions of the same QName. - * Is owned by an xsltDocument. - */ -typedef struct _xsltKeyTable xsltKeyTable; -typedef xsltKeyTable *xsltKeyTablePtr; -struct _xsltKeyTable { - struct _xsltKeyTable *next; - xmlChar *name; - xmlChar *nameURI; - xmlHashTablePtr keys; -}; - -/* - * The in-memory structure corresponding to an XSLT Stylesheet. - * NOTE: most of the content is simply linked from the doc tree - * structure, no specific allocation is made. - */ -typedef struct _xsltStylesheet xsltStylesheet; -typedef xsltStylesheet *xsltStylesheetPtr; - -typedef struct _xsltTransformContext xsltTransformContext; -typedef xsltTransformContext *xsltTransformContextPtr; - -/** - * xsltElemPreComp: - * - * The in-memory structure corresponding to element precomputed data, - * designed to be extended by extension implementors. - */ -typedef struct _xsltElemPreComp xsltElemPreComp; -typedef xsltElemPreComp *xsltElemPreCompPtr; - -/** - * xsltTransformFunction: - * @ctxt: the XSLT transformation context - * @node: the input node - * @inst: the stylesheet node - * @comp: the compiled information from the stylesheet - * - * Signature of the function associated to elements part of the - * stylesheet language like xsl:if or xsl:apply-templates. - */ -typedef void (*xsltTransformFunction) (xsltTransformContextPtr ctxt, - xmlNodePtr node, - xmlNodePtr inst, - xsltElemPreCompPtr comp); - -/** - * xsltSortFunc: - * @ctxt: a transformation context - * @sorts: the node-set to sort - * @nbsorts: the number of sorts - * - * Signature of the function to use during sorting - */ -typedef void (*xsltSortFunc) (xsltTransformContextPtr ctxt, xmlNodePtr *sorts, - int nbsorts); - -typedef enum { - XSLT_FUNC_COPY=1, - XSLT_FUNC_SORT, - XSLT_FUNC_TEXT, - XSLT_FUNC_ELEMENT, - XSLT_FUNC_ATTRIBUTE, - XSLT_FUNC_COMMENT, - XSLT_FUNC_PI, - XSLT_FUNC_COPYOF, - XSLT_FUNC_VALUEOF, - XSLT_FUNC_NUMBER, - XSLT_FUNC_APPLYIMPORTS, - XSLT_FUNC_CALLTEMPLATE, - XSLT_FUNC_APPLYTEMPLATES, - XSLT_FUNC_CHOOSE, - XSLT_FUNC_IF, - XSLT_FUNC_FOREACH, - XSLT_FUNC_DOCUMENT, - XSLT_FUNC_WITHPARAM, - XSLT_FUNC_PARAM, - XSLT_FUNC_VARIABLE, - XSLT_FUNC_WHEN, - XSLT_FUNC_EXTENSION, -#ifdef XSLT_REFACTORED - XSLT_FUNC_OTHERWISE, - XSLT_FUNC_FALLBACK, - XSLT_FUNC_MESSAGE, - XSLT_FUNC_INCLUDE, - XSLT_FUNC_ATTRSET, - XSLT_FUNC_LITERAL_RESULT_ELEMENT, - XSLT_FUNC_UNKOWN_FORWARDS_COMPAT, -#endif -} xsltStyleType; - -/** - * xsltElemPreCompDeallocator: - * @comp: the #xsltElemPreComp to free up - * - * Deallocates an #xsltElemPreComp structure. - */ -typedef void (*xsltElemPreCompDeallocator) (xsltElemPreCompPtr comp); - -/** - * xsltElemPreComp: - * - * The basic structure for compiled items of the AST of the XSLT processor. - * This structure is also intended to be extended by extension implementors. - * TODO: This is somehow not nice, since it has a "free" field, which - * derived stylesheet-structs do not have. - */ -struct _xsltElemPreComp { - xsltElemPreCompPtr next; /* next item in the global chained - list hold by xsltStylesheet. */ - xsltStyleType type; /* type of the element */ - xsltTransformFunction func; /* handling function */ - xmlNodePtr inst; /* the node in the stylesheet's tree - corresponding to this item */ - - /* end of common part */ - xsltElemPreCompDeallocator free; /* the deallocator */ -}; - -/** - * xsltStylePreComp: - * - * The abstract basic structure for items of the XSLT processor. - * This includes: - * 1) compiled forms of XSLT instructions (xsl:if, xsl:attribute, etc.) - * 2) compiled forms of literal result elements - * 3) compiled forms of extension elements - */ -typedef struct _xsltStylePreComp xsltStylePreComp; -typedef xsltStylePreComp *xsltStylePreCompPtr; - -#ifdef XSLT_REFACTORED - -/* -* Some pointer-list utility functions. -*/ -XSLTPUBFUN xsltPointerListPtr XSLTCALL - xsltPointerListCreate (int initialSize); -XSLTPUBFUN void XSLTCALL - xsltPointerListFree (xsltPointerListPtr list); -XSLTPUBFUN void XSLTCALL - xsltPointerListClear (xsltPointerListPtr list); -XSLTPUBFUN int XSLTCALL - xsltPointerListAddSize (xsltPointerListPtr list, - void *item, - int initialSize); - -/************************************************************************ - * * - * Refactored structures * - * * - ************************************************************************/ - -typedef struct _xsltNsListContainer xsltNsListContainer; -typedef xsltNsListContainer *xsltNsListContainerPtr; -struct _xsltNsListContainer { - xmlNsPtr *list; - int totalNumber; - int xpathNumber; -}; - -/** - * XSLT_ITEM_COMPATIBILITY_FIELDS: - * - * Fields for API compatibility to the structure - * _xsltElemPreComp which is used for extension functions. - * TODO: Evaluate if we really need such a compatibility. - */ -#define XSLT_ITEM_COMPATIBILITY_FIELDS \ - xsltElemPreCompPtr next;\ - xsltStyleType type;\ - xsltTransformFunction func;\ - xmlNodePtr inst; - -/** - * XSLT_ITEM_NAVIGATION_FIELDS: - * - * Currently empty. - * TODO: It is intended to hold navigational fields in the future. - */ -#define XSLT_ITEM_NAVIGATION_FIELDS -/* - xsltStylePreCompPtr parent;\ - xsltStylePreCompPtr children;\ - xsltStylePreCompPtr nextItem; -*/ - -/** - * XSLT_ITEM_NSINSCOPE_FIELDS: - * - * The in-scope namespaces. - */ -#define XSLT_ITEM_NSINSCOPE_FIELDS xsltNsListContainerPtr inScopeNs; - -/** - * XSLT_ITEM_COMMON_FIELDS: - * - * Common fields used for all items. - */ -#define XSLT_ITEM_COMMON_FIELDS \ - XSLT_ITEM_COMPATIBILITY_FIELDS \ - XSLT_ITEM_NAVIGATION_FIELDS \ - XSLT_ITEM_NSINSCOPE_FIELDS - -/** - * _xsltStylePreComp: - * - * The abstract basic structure for items of the XSLT processor. - * This includes: - * 1) compiled forms of XSLT instructions (e.g. xsl:if, xsl:attribute, etc.) - * 2) compiled forms of literal result elements - * 3) various properties for XSLT instructions (e.g. xsl:when, - * xsl:with-param) - * - * REVISIT TODO: Keep this structure equal to the fields - * defined by XSLT_ITEM_COMMON_FIELDS - */ -struct _xsltStylePreComp { - xsltElemPreCompPtr next; /* next item in the global chained - list hold by xsltStylesheet */ - xsltStyleType type; /* type of the item */ - xsltTransformFunction func; /* handling function */ - xmlNodePtr inst; /* the node in the stylesheet's tree - corresponding to this item. */ - /* Currently no navigational fields. */ - xsltNsListContainerPtr inScopeNs; -}; - -/** - * xsltStyleBasicEmptyItem: - * - * Abstract structure only used as a short-cut for - * XSLT items with no extra fields. - * NOTE that it is intended that this structure looks the same as - * _xsltStylePreComp. - */ -typedef struct _xsltStyleBasicEmptyItem xsltStyleBasicEmptyItem; -typedef xsltStyleBasicEmptyItem *xsltStyleBasicEmptyItemPtr; - -struct _xsltStyleBasicEmptyItem { - XSLT_ITEM_COMMON_FIELDS -}; - -/** - * xsltStyleBasicExpressionItem: - * - * Abstract structure only used as a short-cut for - * XSLT items with just an expression. - */ -typedef struct _xsltStyleBasicExpressionItem xsltStyleBasicExpressionItem; -typedef xsltStyleBasicExpressionItem *xsltStyleBasicExpressionItemPtr; - -struct _xsltStyleBasicExpressionItem { - XSLT_ITEM_COMMON_FIELDS - - const xmlChar *select; /* TODO: Change this to "expression". */ - xmlXPathCompExprPtr comp; /* TODO: Change this to compExpr. */ -}; - -/************************************************************************ - * * - * XSLT-instructions/declarations * - * * - ************************************************************************/ - -/** - * xsltStyleItemElement: - * - * - * - * - * - */ -typedef struct _xsltStyleItemElement xsltStyleItemElement; -typedef xsltStyleItemElement *xsltStyleItemElementPtr; - -struct _xsltStyleItemElement { - XSLT_ITEM_COMMON_FIELDS - - const xmlChar *use; - int has_use; - const xmlChar *name; - int has_name; - const xmlChar *ns; - const xmlChar *nsPrefix; - int has_ns; -}; - -/** - * xsltStyleItemAttribute: - * - * - * - * - * - */ -typedef struct _xsltStyleItemAttribute xsltStyleItemAttribute; -typedef xsltStyleItemAttribute *xsltStyleItemAttributePtr; - -struct _xsltStyleItemAttribute { - XSLT_ITEM_COMMON_FIELDS - const xmlChar *name; - int has_name; - const xmlChar *ns; - const xmlChar *nsPrefix; - int has_ns; -}; - -/** - * xsltStyleItemText: - * - * - * - * - * - */ -typedef struct _xsltStyleItemText xsltStyleItemText; -typedef xsltStyleItemText *xsltStyleItemTextPtr; - -struct _xsltStyleItemText { - XSLT_ITEM_COMMON_FIELDS - int noescape; /* text */ -}; - -/** - * xsltStyleItemComment: - * - * - * - * - * - */ -typedef xsltStyleBasicEmptyItem xsltStyleItemComment; -typedef xsltStyleItemComment *xsltStyleItemCommentPtr; - -/** - * xsltStyleItemPI: - * - * - * - * - * - */ -typedef struct _xsltStyleItemPI xsltStyleItemPI; -typedef xsltStyleItemPI *xsltStyleItemPIPtr; - -struct _xsltStyleItemPI { - XSLT_ITEM_COMMON_FIELDS - const xmlChar *name; - int has_name; -}; - -/** - * xsltStyleItemApplyImports: - * - * - * - */ -typedef xsltStyleBasicEmptyItem xsltStyleItemApplyImports; -typedef xsltStyleItemApplyImports *xsltStyleItemApplyImportsPtr; - -/** - * xsltStyleItemApplyTemplates: - * - * - * - * - * - */ -typedef struct _xsltStyleItemApplyTemplates xsltStyleItemApplyTemplates; -typedef xsltStyleItemApplyTemplates *xsltStyleItemApplyTemplatesPtr; - -struct _xsltStyleItemApplyTemplates { - XSLT_ITEM_COMMON_FIELDS - - const xmlChar *mode; /* apply-templates */ - const xmlChar *modeURI; /* apply-templates */ - const xmlChar *select; /* sort, copy-of, value-of, apply-templates */ - xmlXPathCompExprPtr comp; /* a precompiled XPath expression */ - /* TODO: with-params */ -}; - -/** - * xsltStyleItemCallTemplate: - * - * - * - * - * - */ -typedef struct _xsltStyleItemCallTemplate xsltStyleItemCallTemplate; -typedef xsltStyleItemCallTemplate *xsltStyleItemCallTemplatePtr; - -struct _xsltStyleItemCallTemplate { - XSLT_ITEM_COMMON_FIELDS - - xsltTemplatePtr templ; /* call-template */ - const xmlChar *name; /* element, attribute, pi */ - int has_name; /* element, attribute, pi */ - const xmlChar *ns; /* element */ - int has_ns; /* element */ - /* TODO: with-params */ -}; - -/** - * xsltStyleItemCopy: - * - * - * - * - * - */ -typedef struct _xsltStyleItemCopy xsltStyleItemCopy; -typedef xsltStyleItemCopy *xsltStyleItemCopyPtr; - -struct _xsltStyleItemCopy { - XSLT_ITEM_COMMON_FIELDS - const xmlChar *use; /* copy, element */ - int has_use; /* copy, element */ -}; - -/** - * xsltStyleItemIf: - * - * - * - * - * - */ -typedef struct _xsltStyleItemIf xsltStyleItemIf; -typedef xsltStyleItemIf *xsltStyleItemIfPtr; - -struct _xsltStyleItemIf { - XSLT_ITEM_COMMON_FIELDS - - const xmlChar *test; /* if */ - xmlXPathCompExprPtr comp; /* a precompiled XPath expression */ -}; - - -/** - * xsltStyleItemCopyOf: - * - * - * - */ -typedef xsltStyleBasicExpressionItem xsltStyleItemCopyOf; -typedef xsltStyleItemCopyOf *xsltStyleItemCopyOfPtr; - -/** - * xsltStyleItemValueOf: - * - * - * - */ -typedef struct _xsltStyleItemValueOf xsltStyleItemValueOf; -typedef xsltStyleItemValueOf *xsltStyleItemValueOfPtr; - -struct _xsltStyleItemValueOf { - XSLT_ITEM_COMMON_FIELDS - - const xmlChar *select; - xmlXPathCompExprPtr comp; /* a precompiled XPath expression */ - int noescape; -}; - -/** - * xsltStyleItemNumber: - * - * - * - */ -typedef struct _xsltStyleItemNumber xsltStyleItemNumber; -typedef xsltStyleItemNumber *xsltStyleItemNumberPtr; - -struct _xsltStyleItemNumber { - XSLT_ITEM_COMMON_FIELDS - xsltNumberData numdata; /* number */ -}; - -/** - * xsltStyleItemChoose: - * - * - * - * - * - */ -typedef xsltStyleBasicEmptyItem xsltStyleItemChoose; -typedef xsltStyleItemChoose *xsltStyleItemChoosePtr; - -/** - * xsltStyleItemFallback: - * - * - * - * - * - */ -typedef xsltStyleBasicEmptyItem xsltStyleItemFallback; -typedef xsltStyleItemFallback *xsltStyleItemFallbackPtr; - -/** - * xsltStyleItemForEach: - * - * - * - * - * - */ -typedef xsltStyleBasicExpressionItem xsltStyleItemForEach; -typedef xsltStyleItemForEach *xsltStyleItemForEachPtr; - -/** - * xsltStyleItemMessage: - * - * - * - * - * - */ -typedef struct _xsltStyleItemMessage xsltStyleItemMessage; -typedef xsltStyleItemMessage *xsltStyleItemMessagePtr; - -struct _xsltStyleItemMessage { - XSLT_ITEM_COMMON_FIELDS - int terminate; -}; - -/** - * xsltStyleItemDocument: - * - * NOTE: This is not an instruction of XSLT 1.0. - */ -typedef struct _xsltStyleItemDocument xsltStyleItemDocument; -typedef xsltStyleItemDocument *xsltStyleItemDocumentPtr; - -struct _xsltStyleItemDocument { - XSLT_ITEM_COMMON_FIELDS - int ver11; /* assigned: in xsltDocumentComp; - read: nowhere; - TODO: Check if we need. */ - const xmlChar *filename; /* document URL */ - int has_filename; -}; - -/************************************************************************ - * * - * Non-instructions (actually properties of instructions/declarations) * - * * - ************************************************************************/ - -/** - * xsltStyleBasicItemVariable: - * - * Basic struct for xsl:variable, xsl:param and xsl:with-param. - * It's currently important to have equal fields, since - * xsltParseStylesheetCallerParam() is used with xsl:with-param from - * the xslt side and with xsl:param from the exslt side (in - * exsltFuncFunctionFunction()). - * - * FUTURE NOTE: In XSLT 2.0 xsl:param, xsl:variable and xsl:with-param - * have additional different fields. - */ -typedef struct _xsltStyleBasicItemVariable xsltStyleBasicItemVariable; -typedef xsltStyleBasicItemVariable *xsltStyleBasicItemVariablePtr; - -struct _xsltStyleBasicItemVariable { - XSLT_ITEM_COMMON_FIELDS - - const xmlChar *select; - xmlXPathCompExprPtr comp; - - const xmlChar *name; - int has_name; - const xmlChar *ns; - int has_ns; -}; - -/** - * xsltStyleItemVariable: - * - * - * - * - * - */ -typedef xsltStyleBasicItemVariable xsltStyleItemVariable; -typedef xsltStyleItemVariable *xsltStyleItemVariablePtr; - -/** - * xsltStyleItemParam: - * - * - * - * - * - */ -typedef xsltStyleBasicItemVariable xsltStyleItemParam; -typedef xsltStyleItemParam *xsltStyleItemParamPtr; - -/** - * xsltStyleItemWithParam: - * - * - * - * - */ -typedef xsltStyleBasicItemVariable xsltStyleItemWithParam; -typedef xsltStyleItemWithParam *xsltStyleItemWithParamPtr; - -/** - * xsltStyleItemSort: - * - * Reflects the XSLT xsl:sort item. - * Allowed parents: xsl:apply-templates, xsl:for-each - * - */ -typedef struct _xsltStyleItemSort xsltStyleItemSort; -typedef xsltStyleItemSort *xsltStyleItemSortPtr; - -struct _xsltStyleItemSort { - XSLT_ITEM_COMMON_FIELDS - - const xmlChar *stype; /* sort */ - int has_stype; /* sort */ - int number; /* sort */ - const xmlChar *order; /* sort */ - int has_order; /* sort */ - int descending; /* sort */ - const xmlChar *lang; /* sort */ - int has_lang; /* sort */ - const xmlChar *case_order; /* sort */ - int lower_first; /* sort */ - - const xmlChar *use; - int has_use; - - const xmlChar *select; /* sort, copy-of, value-of, apply-templates */ - - xmlXPathCompExprPtr comp; /* a precompiled XPath expression */ -}; - - -/** - * xsltStyleItemWhen: - * - * - * - * - * Allowed parent: xsl:choose - */ -typedef struct _xsltStyleItemWhen xsltStyleItemWhen; -typedef xsltStyleItemWhen *xsltStyleItemWhenPtr; - -struct _xsltStyleItemWhen { - XSLT_ITEM_COMMON_FIELDS - - const xmlChar *test; - xmlXPathCompExprPtr comp; -}; - -/** - * xsltStyleItemOtherwise: - * - * Allowed parent: xsl:choose - * - * - * - */ -typedef struct _xsltStyleItemOtherwise xsltStyleItemOtherwise; -typedef xsltStyleItemOtherwise *xsltStyleItemOtherwisePtr; - -struct _xsltStyleItemOtherwise { - XSLT_ITEM_COMMON_FIELDS -}; - -typedef struct _xsltStyleItemInclude xsltStyleItemInclude; -typedef xsltStyleItemInclude *xsltStyleItemIncludePtr; - -struct _xsltStyleItemInclude { - XSLT_ITEM_COMMON_FIELDS - xsltDocumentPtr include; -}; - -/************************************************************************ - * * - * XSLT elements in forwards-compatible mode * - * * - ************************************************************************/ - -typedef struct _xsltStyleItemUknown xsltStyleItemUknown; -typedef xsltStyleItemUknown *xsltStyleItemUknownPtr; -struct _xsltStyleItemUknown { - XSLT_ITEM_COMMON_FIELDS -}; - - -/************************************************************************ - * * - * Extension elements * - * * - ************************************************************************/ - -/* - * xsltStyleItemExtElement: - * - * Reflects extension elements. - * - * NOTE: Due to the fact that the structure xsltElemPreComp is most - * probably already heavily in use out there by users, so we cannot - * easily change it, we'll create an intermediate structure which will - * hold an xsltElemPreCompPtr. - * BIG NOTE: The only problem I see here is that the user processes the - * content of the stylesheet tree, possibly he'll lookup the node->psvi - * fields in order to find subsequent extension functions. - * In this case, the user's code will break, since the node->psvi - * field will hold now the xsltStyleItemExtElementPtr and not - * the xsltElemPreCompPtr. - * However the place where the structure is anchored in the node-tree, - * namely node->psvi, has beed already once been moved from node->_private - * to node->psvi, so we have a precedent here, which, I think, should allow - * us to change such semantics without headaches. - */ -typedef struct _xsltStyleItemExtElement xsltStyleItemExtElement; -typedef xsltStyleItemExtElement *xsltStyleItemExtElementPtr; -struct _xsltStyleItemExtElement { - XSLT_ITEM_COMMON_FIELDS - xsltElemPreCompPtr item; -}; - -/************************************************************************ - * * - * Literal result elements * - * * - ************************************************************************/ - -typedef struct _xsltEffectiveNs xsltEffectiveNs; -typedef xsltEffectiveNs *xsltEffectiveNsPtr; -struct _xsltEffectiveNs { - xsltEffectiveNsPtr nextInStore; /* storage next */ - xsltEffectiveNsPtr next; /* next item in the list */ - const xmlChar *prefix; - const xmlChar *nsName; - /* - * Indicates if eclared on the literal result element; dunno if really - * needed. - */ - int holdByElem; -}; - -/* - * Info for literal result elements. - * This will be set on the elem->psvi field and will be - * shared by literal result elements, which have the same - * excluded result namespaces; i.e., this *won't* be created uniquely - * for every literal result element. - */ -typedef struct _xsltStyleItemLRElementInfo xsltStyleItemLRElementInfo; -typedef xsltStyleItemLRElementInfo *xsltStyleItemLRElementInfoPtr; -struct _xsltStyleItemLRElementInfo { - XSLT_ITEM_COMMON_FIELDS - /* - * @effectiveNs is the set of effective ns-nodes - * on the literal result element, which will be added to the result - * element if not already existing in the result tree. - * This means that excluded namespaces (via exclude-result-prefixes, - * extension-element-prefixes and the XSLT namespace) not added - * to the set. - * Namespace-aliasing was applied on the @effectiveNs. - */ - xsltEffectiveNsPtr effectiveNs; - -}; - -#ifdef XSLT_REFACTORED - -typedef struct _xsltNsAlias xsltNsAlias; -typedef xsltNsAlias *xsltNsAliasPtr; -struct _xsltNsAlias { - xsltNsAliasPtr next; /* next in the list */ - xmlNsPtr literalNs; - xmlNsPtr targetNs; - xmlDocPtr docOfTargetNs; -}; -#endif - -#ifdef XSLT_REFACTORED_XSLT_NSCOMP - -typedef struct _xsltNsMap xsltNsMap; -typedef xsltNsMap *xsltNsMapPtr; -struct _xsltNsMap { - xsltNsMapPtr next; /* next in the list */ - xmlDocPtr doc; - xmlNodePtr elem; /* the element holding the ns-decl */ - xmlNsPtr ns; /* the xmlNs structure holding the XML namespace name */ - const xmlChar *origNsName; /* the original XML namespace name */ - const xmlChar *newNsName; /* the mapped XML namespace name */ -}; -#endif - -/************************************************************************ - * * - * Compile-time structures for *internal* use only * - * * - ************************************************************************/ - -typedef struct _xsltPrincipalStylesheetData xsltPrincipalStylesheetData; -typedef xsltPrincipalStylesheetData *xsltPrincipalStylesheetDataPtr; - -typedef struct _xsltNsList xsltNsList; -typedef xsltNsList *xsltNsListPtr; -struct _xsltNsList { - xsltNsListPtr next; /* next in the list */ - xmlNsPtr ns; -}; - -#define XSLT_ELEMENT_CATEGORY_XSLT 0 -#define XSLT_ELEMENT_CATEGORY_EXTENSION 1 -#define XSLT_ELEMENT_CATEGORY_LRE 2 - -/** - * xsltCompilerNodeInfo: - * - * Per-node information during compile-time. - */ -typedef struct _xsltCompilerNodeInfo xsltCompilerNodeInfo; -typedef xsltCompilerNodeInfo *xsltCompilerNodeInfoPtr; -struct _xsltCompilerNodeInfo { - xsltCompilerNodeInfoPtr next; - xsltCompilerNodeInfoPtr prev; - xmlNodePtr node; - int depth; - xsltTemplatePtr templ; /* The owning template */ - int category; /* XSLT element, LR-element or - extension element */ - xsltStyleType type; - xsltElemPreCompPtr item; /* The compiled information */ - /* The current in-scope namespaces */ - xsltNsListContainerPtr inScopeNs; - /* The current excluded result namespaces */ - xsltPointerListPtr exclResultNs; - /* The current extension instruction namespaces */ - xsltPointerListPtr extElemNs; - /* The current info for literal result elements. */ - xsltStyleItemLRElementInfoPtr litResElemInfo; - /* - * Set to 1 if in-scope namespaces changed, - * or excluded result namespaces changed, - * or extension element namespaces changed. - * This will trigger creation of new infos - * for literal result elements. - */ - int nsChanged; - int preserveWhitespace; - int stripWhitespace; - int isRoot; /* whether this is the stylesheet's root node */ - int forwardsCompat; /* whether forwards-compatible mode is enabled */ - /* whether the content of an extension element was processed */ - int extContentHandled; - /* the type of the current child */ - xsltStyleType curChildType; -}; - -#define XSLT_CCTXT(style) ((xsltCompilerCtxtPtr) style->compCtxt) - -typedef enum { - XSLT_ERROR_SEVERITY_ERROR = 0, - XSLT_ERROR_SEVERITY_WARNING -} xsltErrorSeverityType; - -typedef struct _xsltCompilerCtxt xsltCompilerCtxt; -typedef xsltCompilerCtxt *xsltCompilerCtxtPtr; -struct _xsltCompilerCtxt { - void *errorCtxt; /* user specific error context */ - /* - * used for error/warning reports; e.g. XSLT_ERROR_SEVERITY_WARNING */ - xsltErrorSeverityType errSeverity; - int warnings; /* TODO: number of warnings found at - compilation */ - int errors; /* TODO: number of errors found at - compilation */ - xmlDictPtr dict; - xsltStylesheetPtr style; - int simplified; /* whether this is a simplified stylesheet */ - /* TODO: structured/unstructured error contexts. */ - int depth; /* Current depth of processing */ - - xsltCompilerNodeInfoPtr inode; - xsltCompilerNodeInfoPtr inodeList; - xsltCompilerNodeInfoPtr inodeLast; - xsltPointerListPtr tmpList; /* Used for various purposes */ - /* - * The XSLT version as specified by the stylesheet's root element. - */ - int isInclude; - int hasForwardsCompat; /* whether forwards-compatible mode was used - in a parsing episode */ - int maxNodeInfos; /* TEMP TODO: just for the interest */ - int maxLREs; /* TEMP TODO: just for the interest */ - /* - * In order to keep the old behaviour, applying strict rules of - * the spec can be turned off. This has effect only on special - * mechanisms like whitespace-stripping in the stylesheet. - */ - int strict; - xsltPrincipalStylesheetDataPtr psData; -#ifdef XSLT_REFACTORED_XPATHCOMP - xmlXPathContextPtr xpathCtxt; -#endif - xsltStyleItemUknownPtr unknownItem; - int hasNsAliases; /* Indicator if there was an xsl:namespace-alias. */ - xsltNsAliasPtr nsAliases; -}; - -#else /* XSLT_REFACTORED */ -/* -* The old structures before refactoring. -*/ - -/** - * _xsltStylePreComp: - * - * The in-memory structure corresponding to XSLT stylesheet constructs - * precomputed data. - */ -struct _xsltStylePreComp { - xsltElemPreCompPtr next; /* chained list */ - xsltStyleType type; /* type of the element */ - xsltTransformFunction func; /* handling function */ - xmlNodePtr inst; /* the instruction */ - - /* - * Pre computed values. - */ - - const xmlChar *stype; /* sort */ - int has_stype; /* sort */ - int number; /* sort */ - const xmlChar *order; /* sort */ - int has_order; /* sort */ - int descending; /* sort */ - const xmlChar *lang; /* sort */ - int has_lang; /* sort */ - const xmlChar *case_order; /* sort */ - int lower_first; /* sort */ - - const xmlChar *use; /* copy, element */ - int has_use; /* copy, element */ - - int noescape; /* text */ - - const xmlChar *name; /* element, attribute, pi */ - int has_name; /* element, attribute, pi */ - const xmlChar *ns; /* element */ - int has_ns; /* element */ - - const xmlChar *mode; /* apply-templates */ - const xmlChar *modeURI; /* apply-templates */ - - const xmlChar *test; /* if */ - - xsltTemplatePtr templ; /* call-template */ - - const xmlChar *select; /* sort, copy-of, value-of, apply-templates */ - - int ver11; /* document */ - const xmlChar *filename; /* document URL */ - int has_filename; /* document */ - - xsltNumberData numdata; /* number */ - - xmlXPathCompExprPtr comp; /* a precompiled XPath expression */ - xmlNsPtr *nsList; /* the namespaces in scope */ - int nsNr; /* the number of namespaces in scope */ -}; - -#endif /* XSLT_REFACTORED */ - -/* - * The in-memory structure corresponding to an XSLT Variable - * or Param. - */ -typedef struct _xsltStackElem xsltStackElem; -typedef xsltStackElem *xsltStackElemPtr; -struct _xsltStackElem { - struct _xsltStackElem *next;/* chained list */ - xsltStylePreCompPtr comp; /* the compiled form */ - int computed; /* was the evaluation done */ - const xmlChar *name; /* the local part of the name QName */ - const xmlChar *nameURI; /* the URI part of the name QName */ - const xmlChar *select; /* the eval string */ - xmlNodePtr tree; /* the tree if no eval string or the location */ - xmlXPathObjectPtr value; /* The value if computed */ -}; - -#ifdef XSLT_REFACTORED - -struct _xsltPrincipalStylesheetData { - /* - * Namespace dictionary for ns-prefixes and ns-names: - * TODO: Shared between stylesheets, and XPath mechanisms. - * Not used yet. - */ - xmlDictPtr namespaceDict; - /* - * Global list of in-scope namespaces. - */ - xsltPointerListPtr inScopeNamespaces; - /* - * Global list of information for [xsl:]excluded-result-prefixes. - */ - xsltPointerListPtr exclResultNamespaces; - /* - * Global list of information for [xsl:]extension-element-prefixes. - */ - xsltPointerListPtr extElemNamespaces; - xsltEffectiveNsPtr effectiveNs; -#ifdef XSLT_REFACTORED_XSLT_NSCOMP - /* - * Namespace name map to get rid of string comparison of namespace names. - */ - xsltNsMapPtr nsMap; -#endif -}; - - -#endif -/* - * Note that we added a @compCtxt field to anchor an stylesheet compilation - * context, since, due to historical reasons, various compile-time function - * take only the stylesheet as argument and not a compilation context. - */ -struct _xsltStylesheet { - /* - * The stylesheet import relation is kept as a tree. - */ - struct _xsltStylesheet *parent; - struct _xsltStylesheet *next; - struct _xsltStylesheet *imports; - - xsltDocumentPtr docList; /* the include document list */ - - /* - * General data on the style sheet document. - */ - xmlDocPtr doc; /* the parsed XML stylesheet */ - xmlHashTablePtr stripSpaces;/* the hash table of the strip-space and - preserve space elements */ - int stripAll; /* strip-space * (1) preserve-space * (-1) */ - xmlHashTablePtr cdataSection;/* the hash table of the cdata-section */ - - /* - * Global variable or parameters. - */ - xsltStackElemPtr variables; /* linked list of param and variables */ - - /* - * Template descriptions. - */ - xsltTemplatePtr templates; /* the ordered list of templates */ - void *templatesHash; /* hash table or wherever compiled templates - informations are stored */ - void *rootMatch; /* template based on / */ - void *keyMatch; /* template based on key() */ - void *elemMatch; /* template based on * */ - void *attrMatch; /* template based on @* */ - void *parentMatch; /* template based on .. */ - void *textMatch; /* template based on text() */ - void *piMatch; /* template based on processing-instruction() */ - void *commentMatch; /* template based on comment() */ - - /* - * Namespace aliases. - * NOTE: Not used in the refactored code. - */ - xmlHashTablePtr nsAliases; /* the namespace alias hash tables */ - - /* - * Attribute sets. - */ - xmlHashTablePtr attributeSets;/* the attribute sets hash tables */ - - /* - * Namespaces. - * TODO: Eliminate this. - */ - xmlHashTablePtr nsHash; /* the set of namespaces in use: - ATTENTION: This is used for - execution of XPath expressions; unfortunately - it restricts the stylesheet to have distinct - prefixes. - TODO: We need to get rid of this. - */ - void *nsDefs; /* ATTENTION TODO: This is currently used to store - xsltExtDefPtr (in extensions.c) and - *not* xmlNsPtr. - */ - - /* - * Key definitions. - */ - void *keys; /* key definitions */ - - /* - * Output related stuff. - */ - xmlChar *method; /* the output method */ - xmlChar *methodURI; /* associated namespace if any */ - xmlChar *version; /* version string */ - xmlChar *encoding; /* encoding string */ - int omitXmlDeclaration; /* omit-xml-declaration = "yes" | "no" */ - - /* - * Number formatting. - */ - xsltDecimalFormatPtr decimalFormat; - int standalone; /* standalone = "yes" | "no" */ - xmlChar *doctypePublic; /* doctype-public string */ - xmlChar *doctypeSystem; /* doctype-system string */ - int indent; /* should output being indented */ - xmlChar *mediaType; /* media-type string */ - - /* - * Precomputed blocks. - */ - xsltElemPreCompPtr preComps;/* list of precomputed blocks */ - int warnings; /* number of warnings found at compilation */ - int errors; /* number of errors found at compilation */ - - xmlChar *exclPrefix; /* last excluded prefixes */ - xmlChar **exclPrefixTab; /* array of excluded prefixes */ - int exclPrefixNr; /* number of excluded prefixes in scope */ - int exclPrefixMax; /* size of the array */ - - void *_private; /* user defined data */ - - /* - * Extensions. - */ - xmlHashTablePtr extInfos; /* the extension data */ - int extrasNr; /* the number of extras required */ - - /* - * For keeping track of nested includes - */ - xsltDocumentPtr includes; /* points to last nested include */ - - /* - * dictionary: shared between stylesheet, context and documents. - */ - xmlDictPtr dict; - /* - * precompiled attribute value templates. - */ - void *attVTs; - /* - * if namespace-alias has an alias for the default stylesheet prefix - * NOTE: Not used in the refactored code. - */ - const xmlChar *defaultAlias; - /* - * bypass pre-processing (already done) (used in imports) - */ - int nopreproc; - /* - * all document text strings were internalized - */ - int internalized; - /* - * Literal Result Element as Stylesheet c.f. section 2.3 - */ - int literal_result; - /* - * The principal stylesheet - */ - xsltStylesheetPtr principal; -#ifdef XSLT_REFACTORED - /* - * Compilation context used during compile-time. - */ - xsltCompilerCtxtPtr compCtxt; /* TODO: Change this to (void *). */ - - xsltPrincipalStylesheetDataPtr principalData; -#endif -}; - -/* - * The in-memory structure corresponding to an XSLT Transformation. - */ -typedef enum { - XSLT_OUTPUT_XML = 0, - XSLT_OUTPUT_HTML, - XSLT_OUTPUT_TEXT -} xsltOutputType; - -typedef enum { - XSLT_STATE_OK = 0, - XSLT_STATE_ERROR, - XSLT_STATE_STOPPED -} xsltTransformState; - -struct _xsltTransformContext { - xsltStylesheetPtr style; /* the stylesheet used */ - xsltOutputType type; /* the type of output */ - - xsltTemplatePtr templ; /* the current template */ - int templNr; /* Nb of templates in the stack */ - int templMax; /* Size of the templtes stack */ - xsltTemplatePtr *templTab; /* the template stack */ - - xsltStackElemPtr vars; /* the current variable list */ - int varsNr; /* Nb of variable list in the stack */ - int varsMax; /* Size of the variable list stack */ - xsltStackElemPtr *varsTab; /* the variable list stack */ - int varsBase; /* the var base for current templ */ - - /* - * Extensions - */ - xmlHashTablePtr extFunctions; /* the extension functions */ - xmlHashTablePtr extElements; /* the extension elements */ - xmlHashTablePtr extInfos; /* the extension data */ - - const xmlChar *mode; /* the current mode */ - const xmlChar *modeURI; /* the current mode URI */ - - xsltDocumentPtr docList; /* the document list */ - - xsltDocumentPtr document; /* the current document */ - xmlNodePtr node; /* the current node being processed */ - xmlNodeSetPtr nodeList; /* the current node list */ - /* xmlNodePtr current; the node */ - - xmlDocPtr output; /* the resulting document */ - xmlNodePtr insert; /* the insertion node */ - - xmlXPathContextPtr xpathCtxt; /* the XPath context */ - xsltTransformState state; /* the current state */ - - /* - * Global variables - */ - xmlHashTablePtr globalVars; /* the global variables and params */ - - xmlNodePtr inst; /* the instruction in the stylesheet */ - - int xinclude; /* should XInclude be processed */ - - const char * outputFile; /* the output URI if known */ - - int profile; /* is this run profiled */ - long prof; /* the current profiled value */ - int profNr; /* Nb of templates in the stack */ - int profMax; /* Size of the templtaes stack */ - long *profTab; /* the profile template stack */ - - void *_private; /* user defined data */ - - int extrasNr; /* the number of extras used */ - int extrasMax; /* the number of extras allocated */ - xsltRuntimeExtraPtr extras; /* extra per runtime informations */ - - xsltDocumentPtr styleList; /* the stylesheet docs list */ - void * sec; /* the security preferences if any */ - - xmlGenericErrorFunc error; /* a specific error handler */ - void * errctx; /* context for the error handler */ - - xsltSortFunc sortfunc; /* a ctxt specific sort routine */ - - /* - * handling of temporary Result Value Tree - */ - xmlDocPtr tmpRVT; /* list of RVT without persistance */ - xmlDocPtr persistRVT; /* list of persistant RVTs */ - int ctxtflags; /* context processing flags */ - - /* - * Speed optimization when coalescing text nodes - */ - const xmlChar *lasttext; /* last text node content */ - unsigned int lasttsize; /* last text node size */ - unsigned int lasttuse; /* last text node use */ - /* - * Per Context Debugging - */ - int debugStatus; /* the context level debug status */ - unsigned long* traceCode; /* pointer to the variable holding the mask */ - - int parserOptions; /* parser options xmlParserOption */ - - /* - * dictionnary: shared between stylesheet, context and documents. - */ - xmlDictPtr dict; - /* - * temporary storage for doc ptr, currently only used for - * global var evaluation - */ - xmlDocPtr tmpDoc; - /* - * all document text strings are internalized - */ - int internalized; - int nbKeys; - int hasTemplKeyPatterns; -}; - -/** - * CHECK_STOPPED: - * - * Macro to check if the XSLT processing should be stopped. - * Will return from the function. - */ -#define CHECK_STOPPED if (ctxt->state == XSLT_STATE_STOPPED) return; - -/** - * CHECK_STOPPEDE: - * - * Macro to check if the XSLT processing should be stopped. - * Will goto the error: label. - */ -#define CHECK_STOPPEDE if (ctxt->state == XSLT_STATE_STOPPED) goto error; - -/** - * CHECK_STOPPED0: - * - * Macro to check if the XSLT processing should be stopped. - * Will return from the function with a 0 value. - */ -#define CHECK_STOPPED0 if (ctxt->state == XSLT_STATE_STOPPED) return(0); - -/* - * The macro XML_CAST_FPTR is a hack to avoid a gcc warning about - * possible incompatibilities between function pointers and object - * pointers. It is defined in libxml/hash.h within recent versions - * of libxml2, but is put here for compatibility. - */ -#ifndef XML_CAST_FPTR -/** - * XML_CAST_FPTR: - * @fptr: pointer to a function - * - * Macro to do a casting from an object pointer to a - * function pointer without encountering a warning from - * gcc - * - * #define XML_CAST_FPTR(fptr) (*(void **)(&fptr)) - * This macro violated ISO C aliasing rules (gcc4 on s390 broke) - * so it is disabled now - */ - -#define XML_CAST_FPTR(fptr) fptr -#endif -/* - * Functions associated to the internal types -xsltDecimalFormatPtr xsltDecimalFormatGetByName(xsltStylesheetPtr sheet, - xmlChar *name); - */ -XSLTPUBFUN xsltStylesheetPtr XSLTCALL - xsltNewStylesheet (void); -XSLTPUBFUN xsltStylesheetPtr XSLTCALL - xsltParseStylesheetFile (const xmlChar* filename); -XSLTPUBFUN void XSLTCALL - xsltFreeStylesheet (xsltStylesheetPtr style); -XSLTPUBFUN int XSLTCALL - xsltIsBlank (xmlChar *str); -XSLTPUBFUN void XSLTCALL - xsltFreeStackElemList (xsltStackElemPtr elem); -XSLTPUBFUN xsltDecimalFormatPtr XSLTCALL - xsltDecimalFormatGetByName(xsltStylesheetPtr style, - xmlChar *name); - -XSLTPUBFUN xsltStylesheetPtr XSLTCALL - xsltParseStylesheetProcess(xsltStylesheetPtr ret, - xmlDocPtr doc); -XSLTPUBFUN void XSLTCALL - xsltParseStylesheetOutput(xsltStylesheetPtr style, - xmlNodePtr cur); -XSLTPUBFUN xsltStylesheetPtr XSLTCALL - xsltParseStylesheetDoc (xmlDocPtr doc); -XSLTPUBFUN xsltStylesheetPtr XSLTCALL - xsltParseStylesheetImportedDoc(xmlDocPtr doc, - xsltStylesheetPtr style); -XSLTPUBFUN xsltStylesheetPtr XSLTCALL - xsltLoadStylesheetPI (xmlDocPtr doc); -XSLTPUBFUN void XSLTCALL - xsltNumberFormat (xsltTransformContextPtr ctxt, - xsltNumberDataPtr data, - xmlNodePtr node); -XSLTPUBFUN xmlXPathError XSLTCALL - xsltFormatNumberConversion(xsltDecimalFormatPtr self, - xmlChar *format, - double number, - xmlChar **result); - -XSLTPUBFUN void XSLTCALL - xsltParseTemplateContent(xsltStylesheetPtr style, - xmlNodePtr templ); -XSLTPUBFUN int XSLTCALL - xsltAllocateExtra (xsltStylesheetPtr style); -XSLTPUBFUN int XSLTCALL - xsltAllocateExtraCtxt (xsltTransformContextPtr ctxt); -/* - * Extra functions for Result Value Trees - */ -XSLTPUBFUN xmlDocPtr XSLTCALL - xsltCreateRVT (xsltTransformContextPtr ctxt); -XSLTPUBFUN int XSLTCALL - xsltRegisterTmpRVT (xsltTransformContextPtr ctxt, - xmlDocPtr RVT); -XSLTPUBFUN int XSLTCALL - xsltRegisterPersistRVT (xsltTransformContextPtr ctxt, - xmlDocPtr RVT); -XSLTPUBFUN void XSLTCALL - xsltFreeRVTs (xsltTransformContextPtr ctxt); - -/* - * Extra functions for Attribute Value Templates - */ -XSLTPUBFUN void XSLTCALL - xsltCompileAttr (xsltStylesheetPtr style, - xmlAttrPtr attr); -XSLTPUBFUN xmlChar * XSLTCALL - xsltEvalAVT (xsltTransformContextPtr ctxt, - void *avt, - xmlNodePtr node); -XSLTPUBFUN void XSLTCALL - xsltFreeAVTList (void *avt); - -/* - * Extra function for successful xsltCleanupGlobals / xsltInit sequence. - */ - -XSLTPUBFUN void XSLTCALL - xsltUninit (void); - -/************************************************************************ - * * - * Compile-time functions for *internal* use only * - * * - ************************************************************************/ - -#ifdef XSLT_REFACTORED -XSLTPUBFUN void XSLTCALL - xsltParseSequenceConstructor( - xsltCompilerCtxtPtr cctxt, - xmlNodePtr start); -XSLTPUBFUN int XSLTCALL - xsltParseAnyXSLTElem (xsltCompilerCtxtPtr cctxt, - xmlNodePtr elem); -#ifdef XSLT_REFACTORED_XSLT_NSCOMP -XSLTPUBFUN int XSLTCALL - xsltRestoreDocumentNamespaces( - xsltNsMapPtr ns, - xmlDocPtr doc); -#endif -#endif /* XSLT_REFACTORED */ - -/************************************************************************ - * * - * Transformation-time functions for *internal* use only * - * * - ************************************************************************/ -XSLTPUBFUN int XSLTCALL - xsltInitCtxtKey (xsltTransformContextPtr ctxt, - xsltDocumentPtr doc, - xsltKeyDefPtr keyd); - -#ifdef __cplusplus -} -#endif - -#endif /* __XML_XSLT_H__ */ - +/* + * Summary: internal data structures, constants and functions + * Description: Internal data structures, constants and functions used + * by the XSLT engine. + * They are not part of the API or ABI, i.e. they can change + * without prior notice, use carefully. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_INTERNALS_H__ +#define __XML_XSLT_INTERNALS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include "xsltexports.h" +#include "numbersInternals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* #define XSLT_DEBUG_PROFILE_CACHE */ + +#define XSLT_IS_TEXT_NODE(n) ((n != NULL) && \ + (((n)->type == XML_TEXT_NODE) || \ + ((n)->type == XML_CDATA_SECTION_NODE))) + + +#define XSLT_MARK_RES_TREE_FRAG(n) \ + (n)->name = (char *) xmlStrdup(BAD_CAST " fake node libxslt"); + +#define XSLT_IS_RES_TREE_FRAG(n) \ + ((n != NULL) && ((n)->type == XML_DOCUMENT_NODE) && \ + ((n)->name != NULL) && ((n)->name[0] == ' ')) + +/** + * XSLT_REFACTORED_KEYCOMP: + * + * Internal define to enable on-demand xsl:key computation. + */ +#define XSLT_REFACTORED_KEYCOMP + +/** + * XSLT_FAST_IF: + * + * Internal define to enable usage of xmlXPathCompiledEvalToBoolean() + * for XSLT "tests"; e.g. in + */ +#define XSLT_FAST_IF + +/** + * XSLT_REFACTORED: + * + * Internal define to enable the refactored parts of Libxslt. + */ +/* #define XSLT_REFACTORED */ +/* ==================================================================== */ + +#define XSLT_REFACTORED_VARS + +#ifdef XSLT_REFACTORED + +extern const xmlChar *xsltXSLTAttrMarker; + + +/* TODO: REMOVE: #define XSLT_REFACTORED_EXCLRESNS */ + +/* TODO: REMOVE: #define XSLT_REFACTORED_NSALIAS */ + +/** + * XSLT_REFACTORED_XSLT_NSCOMP + * + * Internal define to enable the pointer-comparison of + * namespaces of XSLT elements. + */ +/* #define XSLT_REFACTORED_XSLT_NSCOMP */ + +/** + * XSLT_REFACTORED_XPATHCOMP + * + * Internal define to enable the optimization of the + * compilation of XPath expressions. + */ +#define XSLT_REFACTORED_XPATHCOMP + +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + +extern const xmlChar *xsltConstNamespaceNameXSLT; + +#define IS_XSLT_ELEM_FAST(n) \ + (((n) != NULL) && ((n)->ns != NULL) && \ + ((n)->ns->href == xsltConstNamespaceNameXSLT)) + +#define IS_XSLT_ATTR_FAST(a) \ + (((a) != NULL) && ((a)->ns != NULL) && \ + ((a)->ns->href == xsltConstNamespaceNameXSLT)) + +#define XSLT_HAS_INTERNAL_NSMAP(s) \ + (((s) != NULL) && ((s)->principal) && \ + ((s)->principal->principalData) && \ + ((s)->principal->principalData->nsMap)) + +#define XSLT_GET_INTERNAL_NSMAP(s) ((s)->principal->principalData->nsMap) + +#else /* XSLT_REFACTORED_XSLT_NSCOMP */ + +#define IS_XSLT_ELEM_FAST(n) \ + (((n) != NULL) && ((n)->ns != NULL) && \ + (xmlStrEqual((n)->ns->href, XSLT_NAMESPACE))) + +#define IS_XSLT_ATTR_FAST(a) \ + (((a) != NULL) && ((a)->ns != NULL) && \ + (xmlStrEqual((a)->ns->href, XSLT_NAMESPACE))) + + +#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ + + +/** + * XSLT_REFACTORED_MANDATORY_VERSION: + * + * TODO: Currently disabled to surpress regression test failures, since + * the old behaviour was that a missing version attribute + * produced a only a warning and not an error, which was incerrect. + * So the regression tests need to be fixed if this is enabled. + */ +/* #define XSLT_REFACTORED_MANDATORY_VERSION */ + +/** + * xsltPointerList: + * + * Pointer-list for various purposes. + */ +typedef struct _xsltPointerList xsltPointerList; +typedef xsltPointerList *xsltPointerListPtr; +struct _xsltPointerList { + void **items; + int number; + int size; +}; + +#endif + +/** + * XSLT_REFACTORED_PARSING: + * + * Internal define to enable the refactored parts of Libxslt + * related to parsing. + */ +/* #define XSLT_REFACTORED_PARSING */ + +/** + * XSLT_MAX_SORT: + * + * Max number of specified xsl:sort on an element. + */ +#define XSLT_MAX_SORT 15 + +/** + * XSLT_PAT_NO_PRIORITY: + * + * Specific value for pattern without priority expressed. + */ +#define XSLT_PAT_NO_PRIORITY -12345789 + +/** + * xsltRuntimeExtra: + * + * Extra information added to the transformation context. + */ +typedef struct _xsltRuntimeExtra xsltRuntimeExtra; +typedef xsltRuntimeExtra *xsltRuntimeExtraPtr; +struct _xsltRuntimeExtra { + void *info; /* pointer to the extra data */ + xmlFreeFunc deallocate; /* pointer to the deallocation routine */ + union { /* dual-purpose field */ + void *ptr; /* data not needing deallocation */ + int ival; /* integer value storage */ + } val; +}; + +/** + * XSLT_RUNTIME_EXTRA_LST: + * @ctxt: the transformation context + * @nr: the index + * + * Macro used to access extra information stored in the context + */ +#define XSLT_RUNTIME_EXTRA_LST(ctxt, nr) (ctxt)->extras[(nr)].info +/** + * XSLT_RUNTIME_EXTRA_FREE: + * @ctxt: the transformation context + * @nr: the index + * + * Macro used to free extra information stored in the context + */ +#define XSLT_RUNTIME_EXTRA_FREE(ctxt, nr) (ctxt)->extras[(nr)].deallocate +/** + * XSLT_RUNTIME_EXTRA: + * @ctxt: the transformation context + * @nr: the index + * + * Macro used to define extra information stored in the context + */ +#define XSLT_RUNTIME_EXTRA(ctxt, nr, typ) (ctxt)->extras[(nr)].val.typ + +/** + * xsltTemplate: + * + * The in-memory structure corresponding to an XSLT Template. + */ +typedef struct _xsltTemplate xsltTemplate; +typedef xsltTemplate *xsltTemplatePtr; +struct _xsltTemplate { + struct _xsltTemplate *next;/* chained list sorted by priority */ + struct _xsltStylesheet *style;/* the containing stylesheet */ + xmlChar *match; /* the matching string */ + float priority; /* as given from the stylesheet, not computed */ + const xmlChar *name; /* the local part of the name QName */ + const xmlChar *nameURI; /* the URI part of the name QName */ + const xmlChar *mode;/* the local part of the mode QName */ + const xmlChar *modeURI;/* the URI part of the mode QName */ + xmlNodePtr content; /* the template replacement value */ + xmlNodePtr elem; /* the source element */ + + /* + * TODO: @inheritedNsNr and @inheritedNs won't be used in the + * refactored code. + */ + int inheritedNsNr; /* number of inherited namespaces */ + xmlNsPtr *inheritedNs;/* inherited non-excluded namespaces */ + + /* Profiling informations */ + int nbCalls; /* the number of time the template was called */ + unsigned long time; /* the time spent in this template */ + void *params; /* xsl:param instructions */ +}; + +/** + * xsltDecimalFormat: + * + * Data structure of decimal-format. + */ +typedef struct _xsltDecimalFormat xsltDecimalFormat; +typedef xsltDecimalFormat *xsltDecimalFormatPtr; +struct _xsltDecimalFormat { + struct _xsltDecimalFormat *next; /* chained list */ + xmlChar *name; + /* Used for interpretation of pattern */ + xmlChar *digit; + xmlChar *patternSeparator; + /* May appear in result */ + xmlChar *minusSign; + xmlChar *infinity; + xmlChar *noNumber; /* Not-a-number */ + /* Used for interpretation of pattern and may appear in result */ + xmlChar *decimalPoint; + xmlChar *grouping; + xmlChar *percent; + xmlChar *permille; + xmlChar *zeroDigit; +}; + +/** + * xsltDocument: + * + * Data structure associated to a parsed document. + */ +typedef struct _xsltDocument xsltDocument; +typedef xsltDocument *xsltDocumentPtr; +struct _xsltDocument { + struct _xsltDocument *next; /* documents are kept in a chained list */ + int main; /* is this the main document */ + xmlDocPtr doc; /* the parsed document */ + void *keys; /* key tables storage */ + struct _xsltDocument *includes; /* subsidiary includes */ + int preproc; /* pre-processing already done */ + int nbKeysComputed; +}; + +/** + * xsltKeyDef: + * + * Representation of an xsl:key. + */ +typedef struct _xsltKeyDef xsltKeyDef; +typedef xsltKeyDef *xsltKeyDefPtr; +struct _xsltKeyDef { + struct _xsltKeyDef *next; + xmlNodePtr inst; + xmlChar *name; + xmlChar *nameURI; + xmlChar *match; + xmlChar *use; + xmlXPathCompExprPtr comp; + xmlXPathCompExprPtr usecomp; + xmlNsPtr *nsList; /* the namespaces in scope */ + int nsNr; /* the number of namespaces in scope */ +}; + +/** + * xsltKeyTable: + * + * Holds the computed keys for key definitions of the same QName. + * Is owned by an xsltDocument. + */ +typedef struct _xsltKeyTable xsltKeyTable; +typedef xsltKeyTable *xsltKeyTablePtr; +struct _xsltKeyTable { + struct _xsltKeyTable *next; + xmlChar *name; + xmlChar *nameURI; + xmlHashTablePtr keys; +}; + +/* + * The in-memory structure corresponding to an XSLT Stylesheet. + * NOTE: most of the content is simply linked from the doc tree + * structure, no specific allocation is made. + */ +typedef struct _xsltStylesheet xsltStylesheet; +typedef xsltStylesheet *xsltStylesheetPtr; + +typedef struct _xsltTransformContext xsltTransformContext; +typedef xsltTransformContext *xsltTransformContextPtr; + +/** + * xsltElemPreComp: + * + * The in-memory structure corresponding to element precomputed data, + * designed to be extended by extension implementors. + */ +typedef struct _xsltElemPreComp xsltElemPreComp; +typedef xsltElemPreComp *xsltElemPreCompPtr; + +/** + * xsltTransformFunction: + * @ctxt: the XSLT transformation context + * @node: the input node + * @inst: the stylesheet node + * @comp: the compiled information from the stylesheet + * + * Signature of the function associated to elements part of the + * stylesheet language like xsl:if or xsl:apply-templates. + */ +typedef void (*xsltTransformFunction) (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltElemPreCompPtr comp); + +/** + * xsltSortFunc: + * @ctxt: a transformation context + * @sorts: the node-set to sort + * @nbsorts: the number of sorts + * + * Signature of the function to use during sorting + */ +typedef void (*xsltSortFunc) (xsltTransformContextPtr ctxt, xmlNodePtr *sorts, + int nbsorts); + +typedef enum { + XSLT_FUNC_COPY=1, + XSLT_FUNC_SORT, + XSLT_FUNC_TEXT, + XSLT_FUNC_ELEMENT, + XSLT_FUNC_ATTRIBUTE, + XSLT_FUNC_COMMENT, + XSLT_FUNC_PI, + XSLT_FUNC_COPYOF, + XSLT_FUNC_VALUEOF, + XSLT_FUNC_NUMBER, + XSLT_FUNC_APPLYIMPORTS, + XSLT_FUNC_CALLTEMPLATE, + XSLT_FUNC_APPLYTEMPLATES, + XSLT_FUNC_CHOOSE, + XSLT_FUNC_IF, + XSLT_FUNC_FOREACH, + XSLT_FUNC_DOCUMENT, + XSLT_FUNC_WITHPARAM, + XSLT_FUNC_PARAM, + XSLT_FUNC_VARIABLE, + XSLT_FUNC_WHEN, + XSLT_FUNC_EXTENSION, +#ifdef XSLT_REFACTORED + XSLT_FUNC_OTHERWISE, + XSLT_FUNC_FALLBACK, + XSLT_FUNC_MESSAGE, + XSLT_FUNC_INCLUDE, + XSLT_FUNC_ATTRSET, + XSLT_FUNC_LITERAL_RESULT_ELEMENT, + XSLT_FUNC_UNKOWN_FORWARDS_COMPAT, +#endif +} xsltStyleType; + +/** + * xsltElemPreCompDeallocator: + * @comp: the #xsltElemPreComp to free up + * + * Deallocates an #xsltElemPreComp structure. + */ +typedef void (*xsltElemPreCompDeallocator) (xsltElemPreCompPtr comp); + +/** + * xsltElemPreComp: + * + * The basic structure for compiled items of the AST of the XSLT processor. + * This structure is also intended to be extended by extension implementors. + * TODO: This is somehow not nice, since it has a "free" field, which + * derived stylesheet-structs do not have. + */ +struct _xsltElemPreComp { + xsltElemPreCompPtr next; /* next item in the global chained + list hold by xsltStylesheet. */ + xsltStyleType type; /* type of the element */ + xsltTransformFunction func; /* handling function */ + xmlNodePtr inst; /* the node in the stylesheet's tree + corresponding to this item */ + + /* end of common part */ + xsltElemPreCompDeallocator free; /* the deallocator */ +}; + +/** + * xsltStylePreComp: + * + * The abstract basic structure for items of the XSLT processor. + * This includes: + * 1) compiled forms of XSLT instructions (xsl:if, xsl:attribute, etc.) + * 2) compiled forms of literal result elements + * 3) compiled forms of extension elements + */ +typedef struct _xsltStylePreComp xsltStylePreComp; +typedef xsltStylePreComp *xsltStylePreCompPtr; + +#ifdef XSLT_REFACTORED + +/* +* Some pointer-list utility functions. +*/ +XSLTPUBFUN xsltPointerListPtr XSLTCALL + xsltPointerListCreate (int initialSize); +XSLTPUBFUN void XSLTCALL + xsltPointerListFree (xsltPointerListPtr list); +XSLTPUBFUN void XSLTCALL + xsltPointerListClear (xsltPointerListPtr list); +XSLTPUBFUN int XSLTCALL + xsltPointerListAddSize (xsltPointerListPtr list, + void *item, + int initialSize); + +/************************************************************************ + * * + * Refactored structures * + * * + ************************************************************************/ + +typedef struct _xsltNsListContainer xsltNsListContainer; +typedef xsltNsListContainer *xsltNsListContainerPtr; +struct _xsltNsListContainer { + xmlNsPtr *list; + int totalNumber; + int xpathNumber; +}; + +/** + * XSLT_ITEM_COMPATIBILITY_FIELDS: + * + * Fields for API compatibility to the structure + * _xsltElemPreComp which is used for extension functions. + * Note that @next is used for storage; it does not reflect a next + * sibling in the tree. + * TODO: Evaluate if we really need such a compatibility. + */ +#define XSLT_ITEM_COMPATIBILITY_FIELDS \ + xsltElemPreCompPtr next;\ + xsltStyleType type;\ + xsltTransformFunction func;\ + xmlNodePtr inst; + +/** + * XSLT_ITEM_NAVIGATION_FIELDS: + * + * Currently empty. + * TODO: It is intended to hold navigational fields in the future. + */ +#define XSLT_ITEM_NAVIGATION_FIELDS +/* + xsltStylePreCompPtr parent;\ + xsltStylePreCompPtr children;\ + xsltStylePreCompPtr nextItem; +*/ + +/** + * XSLT_ITEM_NSINSCOPE_FIELDS: + * + * The in-scope namespaces. + */ +#define XSLT_ITEM_NSINSCOPE_FIELDS xsltNsListContainerPtr inScopeNs; + +/** + * XSLT_ITEM_COMMON_FIELDS: + * + * Common fields used for all items. + */ +#define XSLT_ITEM_COMMON_FIELDS \ + XSLT_ITEM_COMPATIBILITY_FIELDS \ + XSLT_ITEM_NAVIGATION_FIELDS \ + XSLT_ITEM_NSINSCOPE_FIELDS + +/** + * _xsltStylePreComp: + * + * The abstract basic structure for items of the XSLT processor. + * This includes: + * 1) compiled forms of XSLT instructions (e.g. xsl:if, xsl:attribute, etc.) + * 2) compiled forms of literal result elements + * 3) various properties for XSLT instructions (e.g. xsl:when, + * xsl:with-param) + * + * REVISIT TODO: Keep this structure equal to the fields + * defined by XSLT_ITEM_COMMON_FIELDS + */ +struct _xsltStylePreComp { + xsltElemPreCompPtr next; /* next item in the global chained + list hold by xsltStylesheet */ + xsltStyleType type; /* type of the item */ + xsltTransformFunction func; /* handling function */ + xmlNodePtr inst; /* the node in the stylesheet's tree + corresponding to this item. */ + /* Currently no navigational fields. */ + xsltNsListContainerPtr inScopeNs; +}; + +/** + * xsltStyleBasicEmptyItem: + * + * Abstract structure only used as a short-cut for + * XSLT items with no extra fields. + * NOTE that it is intended that this structure looks the same as + * _xsltStylePreComp. + */ +typedef struct _xsltStyleBasicEmptyItem xsltStyleBasicEmptyItem; +typedef xsltStyleBasicEmptyItem *xsltStyleBasicEmptyItemPtr; + +struct _xsltStyleBasicEmptyItem { + XSLT_ITEM_COMMON_FIELDS +}; + +/** + * xsltStyleBasicExpressionItem: + * + * Abstract structure only used as a short-cut for + * XSLT items with just an expression. + */ +typedef struct _xsltStyleBasicExpressionItem xsltStyleBasicExpressionItem; +typedef xsltStyleBasicExpressionItem *xsltStyleBasicExpressionItemPtr; + +struct _xsltStyleBasicExpressionItem { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *select; /* TODO: Change this to "expression". */ + xmlXPathCompExprPtr comp; /* TODO: Change this to compExpr. */ +}; + +/************************************************************************ + * * + * XSLT-instructions/declarations * + * * + ************************************************************************/ + +/** + * xsltStyleItemElement: + * + * + * + * + * + */ +typedef struct _xsltStyleItemElement xsltStyleItemElement; +typedef xsltStyleItemElement *xsltStyleItemElementPtr; + +struct _xsltStyleItemElement { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *use; + int has_use; + const xmlChar *name; + int has_name; + const xmlChar *ns; + const xmlChar *nsPrefix; + int has_ns; +}; + +/** + * xsltStyleItemAttribute: + * + * + * + * + * + */ +typedef struct _xsltStyleItemAttribute xsltStyleItemAttribute; +typedef xsltStyleItemAttribute *xsltStyleItemAttributePtr; + +struct _xsltStyleItemAttribute { + XSLT_ITEM_COMMON_FIELDS + const xmlChar *name; + int has_name; + const xmlChar *ns; + const xmlChar *nsPrefix; + int has_ns; +}; + +/** + * xsltStyleItemText: + * + * + * + * + * + */ +typedef struct _xsltStyleItemText xsltStyleItemText; +typedef xsltStyleItemText *xsltStyleItemTextPtr; + +struct _xsltStyleItemText { + XSLT_ITEM_COMMON_FIELDS + int noescape; /* text */ +}; + +/** + * xsltStyleItemComment: + * + * + * + * + * + */ +typedef xsltStyleBasicEmptyItem xsltStyleItemComment; +typedef xsltStyleItemComment *xsltStyleItemCommentPtr; + +/** + * xsltStyleItemPI: + * + * + * + * + * + */ +typedef struct _xsltStyleItemPI xsltStyleItemPI; +typedef xsltStyleItemPI *xsltStyleItemPIPtr; + +struct _xsltStyleItemPI { + XSLT_ITEM_COMMON_FIELDS + const xmlChar *name; + int has_name; +}; + +/** + * xsltStyleItemApplyImports: + * + * + * + */ +typedef xsltStyleBasicEmptyItem xsltStyleItemApplyImports; +typedef xsltStyleItemApplyImports *xsltStyleItemApplyImportsPtr; + +/** + * xsltStyleItemApplyTemplates: + * + * + * + * + * + */ +typedef struct _xsltStyleItemApplyTemplates xsltStyleItemApplyTemplates; +typedef xsltStyleItemApplyTemplates *xsltStyleItemApplyTemplatesPtr; + +struct _xsltStyleItemApplyTemplates { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *mode; /* apply-templates */ + const xmlChar *modeURI; /* apply-templates */ + const xmlChar *select; /* sort, copy-of, value-of, apply-templates */ + xmlXPathCompExprPtr comp; /* a precompiled XPath expression */ + /* TODO: with-params */ +}; + +/** + * xsltStyleItemCallTemplate: + * + * + * + * + * + */ +typedef struct _xsltStyleItemCallTemplate xsltStyleItemCallTemplate; +typedef xsltStyleItemCallTemplate *xsltStyleItemCallTemplatePtr; + +struct _xsltStyleItemCallTemplate { + XSLT_ITEM_COMMON_FIELDS + + xsltTemplatePtr templ; /* call-template */ + const xmlChar *name; /* element, attribute, pi */ + int has_name; /* element, attribute, pi */ + const xmlChar *ns; /* element */ + int has_ns; /* element */ + /* TODO: with-params */ +}; + +/** + * xsltStyleItemCopy: + * + * + * + * + * + */ +typedef struct _xsltStyleItemCopy xsltStyleItemCopy; +typedef xsltStyleItemCopy *xsltStyleItemCopyPtr; + +struct _xsltStyleItemCopy { + XSLT_ITEM_COMMON_FIELDS + const xmlChar *use; /* copy, element */ + int has_use; /* copy, element */ +}; + +/** + * xsltStyleItemIf: + * + * + * + * + * + */ +typedef struct _xsltStyleItemIf xsltStyleItemIf; +typedef xsltStyleItemIf *xsltStyleItemIfPtr; + +struct _xsltStyleItemIf { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *test; /* if */ + xmlXPathCompExprPtr comp; /* a precompiled XPath expression */ +}; + + +/** + * xsltStyleItemCopyOf: + * + * + * + */ +typedef xsltStyleBasicExpressionItem xsltStyleItemCopyOf; +typedef xsltStyleItemCopyOf *xsltStyleItemCopyOfPtr; + +/** + * xsltStyleItemValueOf: + * + * + * + */ +typedef struct _xsltStyleItemValueOf xsltStyleItemValueOf; +typedef xsltStyleItemValueOf *xsltStyleItemValueOfPtr; + +struct _xsltStyleItemValueOf { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *select; + xmlXPathCompExprPtr comp; /* a precompiled XPath expression */ + int noescape; +}; + +/** + * xsltStyleItemNumber: + * + * + * + */ +typedef struct _xsltStyleItemNumber xsltStyleItemNumber; +typedef xsltStyleItemNumber *xsltStyleItemNumberPtr; + +struct _xsltStyleItemNumber { + XSLT_ITEM_COMMON_FIELDS + xsltNumberData numdata; /* number */ +}; + +/** + * xsltStyleItemChoose: + * + * + * + * + * + */ +typedef xsltStyleBasicEmptyItem xsltStyleItemChoose; +typedef xsltStyleItemChoose *xsltStyleItemChoosePtr; + +/** + * xsltStyleItemFallback: + * + * + * + * + * + */ +typedef xsltStyleBasicEmptyItem xsltStyleItemFallback; +typedef xsltStyleItemFallback *xsltStyleItemFallbackPtr; + +/** + * xsltStyleItemForEach: + * + * + * + * + * + */ +typedef xsltStyleBasicExpressionItem xsltStyleItemForEach; +typedef xsltStyleItemForEach *xsltStyleItemForEachPtr; + +/** + * xsltStyleItemMessage: + * + * + * + * + * + */ +typedef struct _xsltStyleItemMessage xsltStyleItemMessage; +typedef xsltStyleItemMessage *xsltStyleItemMessagePtr; + +struct _xsltStyleItemMessage { + XSLT_ITEM_COMMON_FIELDS + int terminate; +}; + +/** + * xsltStyleItemDocument: + * + * NOTE: This is not an instruction of XSLT 1.0. + */ +typedef struct _xsltStyleItemDocument xsltStyleItemDocument; +typedef xsltStyleItemDocument *xsltStyleItemDocumentPtr; + +struct _xsltStyleItemDocument { + XSLT_ITEM_COMMON_FIELDS + int ver11; /* assigned: in xsltDocumentComp; + read: nowhere; + TODO: Check if we need. */ + const xmlChar *filename; /* document URL */ + int has_filename; +}; + +/************************************************************************ + * * + * Non-instructions (actually properties of instructions/declarations) * + * * + ************************************************************************/ + +/** + * xsltStyleBasicItemVariable: + * + * Basic struct for xsl:variable, xsl:param and xsl:with-param. + * It's currently important to have equal fields, since + * xsltParseStylesheetCallerParam() is used with xsl:with-param from + * the xslt side and with xsl:param from the exslt side (in + * exsltFuncFunctionFunction()). + * + * FUTURE NOTE: In XSLT 2.0 xsl:param, xsl:variable and xsl:with-param + * have additional different fields. + */ +typedef struct _xsltStyleBasicItemVariable xsltStyleBasicItemVariable; +typedef xsltStyleBasicItemVariable *xsltStyleBasicItemVariablePtr; + +struct _xsltStyleBasicItemVariable { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *select; + xmlXPathCompExprPtr comp; + + const xmlChar *name; + int has_name; + const xmlChar *ns; + int has_ns; +}; + +/** + * xsltStyleItemVariable: + * + * + * + * + * + */ +typedef xsltStyleBasicItemVariable xsltStyleItemVariable; +typedef xsltStyleItemVariable *xsltStyleItemVariablePtr; + +/** + * xsltStyleItemParam: + * + * + * + * + * + */ +typedef struct _xsltStyleItemParam xsltStyleItemParam; +typedef xsltStyleItemParam *xsltStyleItemParamPtr; + +struct _xsltStyleItemParam { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *select; + xmlXPathCompExprPtr comp; + + const xmlChar *name; + int has_name; + const xmlChar *ns; + int has_ns; +}; + +/** + * xsltStyleItemWithParam: + * + * + * + * + */ +typedef xsltStyleBasicItemVariable xsltStyleItemWithParam; +typedef xsltStyleItemWithParam *xsltStyleItemWithParamPtr; + +/** + * xsltStyleItemSort: + * + * Reflects the XSLT xsl:sort item. + * Allowed parents: xsl:apply-templates, xsl:for-each + * + */ +typedef struct _xsltStyleItemSort xsltStyleItemSort; +typedef xsltStyleItemSort *xsltStyleItemSortPtr; + +struct _xsltStyleItemSort { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *stype; /* sort */ + int has_stype; /* sort */ + int number; /* sort */ + const xmlChar *order; /* sort */ + int has_order; /* sort */ + int descending; /* sort */ + const xmlChar *lang; /* sort */ + int has_lang; /* sort */ + const xmlChar *case_order; /* sort */ + int lower_first; /* sort */ + + const xmlChar *use; + int has_use; + + const xmlChar *select; /* sort, copy-of, value-of, apply-templates */ + + xmlXPathCompExprPtr comp; /* a precompiled XPath expression */ +}; + + +/** + * xsltStyleItemWhen: + * + * + * + * + * Allowed parent: xsl:choose + */ +typedef struct _xsltStyleItemWhen xsltStyleItemWhen; +typedef xsltStyleItemWhen *xsltStyleItemWhenPtr; + +struct _xsltStyleItemWhen { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *test; + xmlXPathCompExprPtr comp; +}; + +/** + * xsltStyleItemOtherwise: + * + * Allowed parent: xsl:choose + * + * + * + */ +typedef struct _xsltStyleItemOtherwise xsltStyleItemOtherwise; +typedef xsltStyleItemOtherwise *xsltStyleItemOtherwisePtr; + +struct _xsltStyleItemOtherwise { + XSLT_ITEM_COMMON_FIELDS +}; + +typedef struct _xsltStyleItemInclude xsltStyleItemInclude; +typedef xsltStyleItemInclude *xsltStyleItemIncludePtr; + +struct _xsltStyleItemInclude { + XSLT_ITEM_COMMON_FIELDS + xsltDocumentPtr include; +}; + +/************************************************************************ + * * + * XSLT elements in forwards-compatible mode * + * * + ************************************************************************/ + +typedef struct _xsltStyleItemUknown xsltStyleItemUknown; +typedef xsltStyleItemUknown *xsltStyleItemUknownPtr; +struct _xsltStyleItemUknown { + XSLT_ITEM_COMMON_FIELDS +}; + + +/************************************************************************ + * * + * Extension elements * + * * + ************************************************************************/ + +/* + * xsltStyleItemExtElement: + * + * Reflects extension elements. + * + * NOTE: Due to the fact that the structure xsltElemPreComp is most + * probably already heavily in use out there by users, so we cannot + * easily change it, we'll create an intermediate structure which will + * hold an xsltElemPreCompPtr. + * BIG NOTE: The only problem I see here is that the user processes the + * content of the stylesheet tree, possibly he'll lookup the node->psvi + * fields in order to find subsequent extension functions. + * In this case, the user's code will break, since the node->psvi + * field will hold now the xsltStyleItemExtElementPtr and not + * the xsltElemPreCompPtr. + * However the place where the structure is anchored in the node-tree, + * namely node->psvi, has beed already once been moved from node->_private + * to node->psvi, so we have a precedent here, which, I think, should allow + * us to change such semantics without headaches. + */ +typedef struct _xsltStyleItemExtElement xsltStyleItemExtElement; +typedef xsltStyleItemExtElement *xsltStyleItemExtElementPtr; +struct _xsltStyleItemExtElement { + XSLT_ITEM_COMMON_FIELDS + xsltElemPreCompPtr item; +}; + +/************************************************************************ + * * + * Literal result elements * + * * + ************************************************************************/ + +typedef struct _xsltEffectiveNs xsltEffectiveNs; +typedef xsltEffectiveNs *xsltEffectiveNsPtr; +struct _xsltEffectiveNs { + xsltEffectiveNsPtr nextInStore; /* storage next */ + xsltEffectiveNsPtr next; /* next item in the list */ + const xmlChar *prefix; + const xmlChar *nsName; + /* + * Indicates if eclared on the literal result element; dunno if really + * needed. + */ + int holdByElem; +}; + +/* + * Info for literal result elements. + * This will be set on the elem->psvi field and will be + * shared by literal result elements, which have the same + * excluded result namespaces; i.e., this *won't* be created uniquely + * for every literal result element. + */ +typedef struct _xsltStyleItemLRElementInfo xsltStyleItemLRElementInfo; +typedef xsltStyleItemLRElementInfo *xsltStyleItemLRElementInfoPtr; +struct _xsltStyleItemLRElementInfo { + XSLT_ITEM_COMMON_FIELDS + /* + * @effectiveNs is the set of effective ns-nodes + * on the literal result element, which will be added to the result + * element if not already existing in the result tree. + * This means that excluded namespaces (via exclude-result-prefixes, + * extension-element-prefixes and the XSLT namespace) not added + * to the set. + * Namespace-aliasing was applied on the @effectiveNs. + */ + xsltEffectiveNsPtr effectiveNs; + +}; + +#ifdef XSLT_REFACTORED + +typedef struct _xsltNsAlias xsltNsAlias; +typedef xsltNsAlias *xsltNsAliasPtr; +struct _xsltNsAlias { + xsltNsAliasPtr next; /* next in the list */ + xmlNsPtr literalNs; + xmlNsPtr targetNs; + xmlDocPtr docOfTargetNs; +}; +#endif + +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + +typedef struct _xsltNsMap xsltNsMap; +typedef xsltNsMap *xsltNsMapPtr; +struct _xsltNsMap { + xsltNsMapPtr next; /* next in the list */ + xmlDocPtr doc; + xmlNodePtr elem; /* the element holding the ns-decl */ + xmlNsPtr ns; /* the xmlNs structure holding the XML namespace name */ + const xmlChar *origNsName; /* the original XML namespace name */ + const xmlChar *newNsName; /* the mapped XML namespace name */ +}; +#endif + +/************************************************************************ + * * + * Compile-time structures for *internal* use only * + * * + ************************************************************************/ + +typedef struct _xsltPrincipalStylesheetData xsltPrincipalStylesheetData; +typedef xsltPrincipalStylesheetData *xsltPrincipalStylesheetDataPtr; + +typedef struct _xsltNsList xsltNsList; +typedef xsltNsList *xsltNsListPtr; +struct _xsltNsList { + xsltNsListPtr next; /* next in the list */ + xmlNsPtr ns; +}; + +/* +* xsltVarInfo: +* +* Used at compilation time for parameters and variables. +*/ +typedef struct _xsltVarInfo xsltVarInfo; +typedef xsltVarInfo *xsltVarInfoPtr; +struct _xsltVarInfo { + xsltVarInfoPtr next; /* next in the list */ + xsltVarInfoPtr prev; + int depth; /* the depth in the tree */ + const xmlChar *name; + const xmlChar *nsName; +}; + +#define XSLT_ELEMENT_CATEGORY_XSLT 0 +#define XSLT_ELEMENT_CATEGORY_EXTENSION 1 +#define XSLT_ELEMENT_CATEGORY_LRE 2 + +/** + * xsltCompilerNodeInfo: + * + * Per-node information during compile-time. + */ +typedef struct _xsltCompilerNodeInfo xsltCompilerNodeInfo; +typedef xsltCompilerNodeInfo *xsltCompilerNodeInfoPtr; +struct _xsltCompilerNodeInfo { + xsltCompilerNodeInfoPtr next; + xsltCompilerNodeInfoPtr prev; + xmlNodePtr node; + int depth; + xsltTemplatePtr templ; /* The owning template */ + int category; /* XSLT element, LR-element or + extension element */ + xsltStyleType type; + xsltElemPreCompPtr item; /* The compiled information */ + /* The current in-scope namespaces */ + xsltNsListContainerPtr inScopeNs; + /* The current excluded result namespaces */ + xsltPointerListPtr exclResultNs; + /* The current extension instruction namespaces */ + xsltPointerListPtr extElemNs; + + /* The current info for literal result elements. */ + xsltStyleItemLRElementInfoPtr litResElemInfo; + /* + * Set to 1 if in-scope namespaces changed, + * or excluded result namespaces changed, + * or extension element namespaces changed. + * This will trigger creation of new infos + * for literal result elements. + */ + int nsChanged; + int preserveWhitespace; + int stripWhitespace; + int isRoot; /* whether this is the stylesheet's root node */ + int forwardsCompat; /* whether forwards-compatible mode is enabled */ + /* whether the content of an extension element was processed */ + int extContentHandled; + /* the type of the current child */ + xsltStyleType curChildType; +}; + +#define XSLT_CCTXT(style) ((xsltCompilerCtxtPtr) style->compCtxt) + +typedef enum { + XSLT_ERROR_SEVERITY_ERROR = 0, + XSLT_ERROR_SEVERITY_WARNING +} xsltErrorSeverityType; + +typedef struct _xsltCompilerCtxt xsltCompilerCtxt; +typedef xsltCompilerCtxt *xsltCompilerCtxtPtr; +struct _xsltCompilerCtxt { + void *errorCtxt; /* user specific error context */ + /* + * used for error/warning reports; e.g. XSLT_ERROR_SEVERITY_WARNING */ + xsltErrorSeverityType errSeverity; + int warnings; /* TODO: number of warnings found at + compilation */ + int errors; /* TODO: number of errors found at + compilation */ + xmlDictPtr dict; + xsltStylesheetPtr style; + int simplified; /* whether this is a simplified stylesheet */ + /* TODO: structured/unstructured error contexts. */ + int depth; /* Current depth of processing */ + + xsltCompilerNodeInfoPtr inode; + xsltCompilerNodeInfoPtr inodeList; + xsltCompilerNodeInfoPtr inodeLast; + xsltPointerListPtr tmpList; /* Used for various purposes */ + /* + * The XSLT version as specified by the stylesheet's root element. + */ + int isInclude; + int hasForwardsCompat; /* whether forwards-compatible mode was used + in a parsing episode */ + int maxNodeInfos; /* TEMP TODO: just for the interest */ + int maxLREs; /* TEMP TODO: just for the interest */ + /* + * In order to keep the old behaviour, applying strict rules of + * the spec can be turned off. This has effect only on special + * mechanisms like whitespace-stripping in the stylesheet. + */ + int strict; + xsltPrincipalStylesheetDataPtr psData; +#ifdef XSLT_REFACTORED_XPATHCOMP + xmlXPathContextPtr xpathCtxt; +#endif + xsltStyleItemUknownPtr unknownItem; + int hasNsAliases; /* Indicator if there was an xsl:namespace-alias. */ + xsltNsAliasPtr nsAliases; + xsltVarInfoPtr ivars; /* Storage of local in-scope variables/params. */ + xsltVarInfoPtr ivar; /* topmost local variable/param. */ +}; + +#else /* XSLT_REFACTORED */ +/* +* The old structures before refactoring. +*/ + +/** + * _xsltStylePreComp: + * + * The in-memory structure corresponding to XSLT stylesheet constructs + * precomputed data. + */ +struct _xsltStylePreComp { + xsltElemPreCompPtr next; /* chained list */ + xsltStyleType type; /* type of the element */ + xsltTransformFunction func; /* handling function */ + xmlNodePtr inst; /* the instruction */ + + /* + * Pre computed values. + */ + + const xmlChar *stype; /* sort */ + int has_stype; /* sort */ + int number; /* sort */ + const xmlChar *order; /* sort */ + int has_order; /* sort */ + int descending; /* sort */ + const xmlChar *lang; /* sort */ + int has_lang; /* sort */ + const xmlChar *case_order; /* sort */ + int lower_first; /* sort */ + + const xmlChar *use; /* copy, element */ + int has_use; /* copy, element */ + + int noescape; /* text */ + + const xmlChar *name; /* element, attribute, pi */ + int has_name; /* element, attribute, pi */ + const xmlChar *ns; /* element */ + int has_ns; /* element */ + + const xmlChar *mode; /* apply-templates */ + const xmlChar *modeURI; /* apply-templates */ + + const xmlChar *test; /* if */ + + xsltTemplatePtr templ; /* call-template */ + + const xmlChar *select; /* sort, copy-of, value-of, apply-templates */ + + int ver11; /* document */ + const xmlChar *filename; /* document URL */ + int has_filename; /* document */ + + xsltNumberData numdata; /* number */ + + xmlXPathCompExprPtr comp; /* a precompiled XPath expression */ + xmlNsPtr *nsList; /* the namespaces in scope */ + int nsNr; /* the number of namespaces in scope */ +}; + +#endif /* XSLT_REFACTORED */ + + +#define XSLT_VAR_GLOBAL 1<<0 +#define XSLT_VAR_IN_SELECT 1<<1 +#define XSLT_TCTXT_VARIABLE(c) ((xsltStackElemPtr) (c)->contextVariable) +/* + * The in-memory structure corresponding to an XSLT Variable + * or Param. + */ +typedef struct _xsltStackElem xsltStackElem; +typedef xsltStackElem *xsltStackElemPtr; +struct _xsltStackElem { + struct _xsltStackElem *next;/* chained list */ + xsltStylePreCompPtr comp; /* the compiled form */ + int computed; /* was the evaluation done */ + const xmlChar *name; /* the local part of the name QName */ + const xmlChar *nameURI; /* the URI part of the name QName */ + const xmlChar *select; /* the eval string */ + xmlNodePtr tree; /* the sequence constructor if no eval + string or the location */ + xmlXPathObjectPtr value; /* The value if computed */ + xmlDocPtr fragment; /* The Result Tree Fragments (needed for XSLT 1.0) + which are bound to the variable's lifetime. */ + int level; /* the depth in the tree; + -1 if persistent (e.g. a given xsl:with-param) */ + xsltTransformContextPtr context; /* The transformation context; needed to cache + the variables */ + int flags; +}; + +#ifdef XSLT_REFACTORED + +struct _xsltPrincipalStylesheetData { + /* + * Namespace dictionary for ns-prefixes and ns-names: + * TODO: Shared between stylesheets, and XPath mechanisms. + * Not used yet. + */ + xmlDictPtr namespaceDict; + /* + * Global list of in-scope namespaces. + */ + xsltPointerListPtr inScopeNamespaces; + /* + * Global list of information for [xsl:]excluded-result-prefixes. + */ + xsltPointerListPtr exclResultNamespaces; + /* + * Global list of information for [xsl:]extension-element-prefixes. + */ + xsltPointerListPtr extElemNamespaces; + xsltEffectiveNsPtr effectiveNs; +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + /* + * Namespace name map to get rid of string comparison of namespace names. + */ + xsltNsMapPtr nsMap; +#endif +}; + + +#endif +/* + * Note that we added a @compCtxt field to anchor an stylesheet compilation + * context, since, due to historical reasons, various compile-time function + * take only the stylesheet as argument and not a compilation context. + */ +struct _xsltStylesheet { + /* + * The stylesheet import relation is kept as a tree. + */ + struct _xsltStylesheet *parent; + struct _xsltStylesheet *next; + struct _xsltStylesheet *imports; + + xsltDocumentPtr docList; /* the include document list */ + + /* + * General data on the style sheet document. + */ + xmlDocPtr doc; /* the parsed XML stylesheet */ + xmlHashTablePtr stripSpaces;/* the hash table of the strip-space and + preserve space elements */ + int stripAll; /* strip-space * (1) preserve-space * (-1) */ + xmlHashTablePtr cdataSection;/* the hash table of the cdata-section */ + + /* + * Global variable or parameters. + */ + xsltStackElemPtr variables; /* linked list of param and variables */ + + /* + * Template descriptions. + */ + xsltTemplatePtr templates; /* the ordered list of templates */ + void *templatesHash; /* hash table or wherever compiled templates + informations are stored */ + void *rootMatch; /* template based on / */ + void *keyMatch; /* template based on key() */ + void *elemMatch; /* template based on * */ + void *attrMatch; /* template based on @* */ + void *parentMatch; /* template based on .. */ + void *textMatch; /* template based on text() */ + void *piMatch; /* template based on processing-instruction() */ + void *commentMatch; /* template based on comment() */ + + /* + * Namespace aliases. + * NOTE: Not used in the refactored code. + */ + xmlHashTablePtr nsAliases; /* the namespace alias hash tables */ + + /* + * Attribute sets. + */ + xmlHashTablePtr attributeSets;/* the attribute sets hash tables */ + + /* + * Namespaces. + * TODO: Eliminate this. + */ + xmlHashTablePtr nsHash; /* the set of namespaces in use: + ATTENTION: This is used for + execution of XPath expressions; unfortunately + it restricts the stylesheet to have distinct + prefixes. + TODO: We need to get rid of this. + */ + void *nsDefs; /* ATTENTION TODO: This is currently used to store + xsltExtDefPtr (in extensions.c) and + *not* xmlNsPtr. + */ + + /* + * Key definitions. + */ + void *keys; /* key definitions */ + + /* + * Output related stuff. + */ + xmlChar *method; /* the output method */ + xmlChar *methodURI; /* associated namespace if any */ + xmlChar *version; /* version string */ + xmlChar *encoding; /* encoding string */ + int omitXmlDeclaration; /* omit-xml-declaration = "yes" | "no" */ + + /* + * Number formatting. + */ + xsltDecimalFormatPtr decimalFormat; + int standalone; /* standalone = "yes" | "no" */ + xmlChar *doctypePublic; /* doctype-public string */ + xmlChar *doctypeSystem; /* doctype-system string */ + int indent; /* should output being indented */ + xmlChar *mediaType; /* media-type string */ + + /* + * Precomputed blocks. + */ + xsltElemPreCompPtr preComps;/* list of precomputed blocks */ + int warnings; /* number of warnings found at compilation */ + int errors; /* number of errors found at compilation */ + + xmlChar *exclPrefix; /* last excluded prefixes */ + xmlChar **exclPrefixTab; /* array of excluded prefixes */ + int exclPrefixNr; /* number of excluded prefixes in scope */ + int exclPrefixMax; /* size of the array */ + + void *_private; /* user defined data */ + + /* + * Extensions. + */ + xmlHashTablePtr extInfos; /* the extension data */ + int extrasNr; /* the number of extras required */ + + /* + * For keeping track of nested includes + */ + xsltDocumentPtr includes; /* points to last nested include */ + + /* + * dictionary: shared between stylesheet, context and documents. + */ + xmlDictPtr dict; + /* + * precompiled attribute value templates. + */ + void *attVTs; + /* + * if namespace-alias has an alias for the default stylesheet prefix + * NOTE: Not used in the refactored code. + */ + const xmlChar *defaultAlias; + /* + * bypass pre-processing (already done) (used in imports) + */ + int nopreproc; + /* + * all document text strings were internalized + */ + int internalized; + /* + * Literal Result Element as Stylesheet c.f. section 2.3 + */ + int literal_result; + /* + * The principal stylesheet + */ + xsltStylesheetPtr principal; +#ifdef XSLT_REFACTORED + /* + * Compilation context used during compile-time. + */ + xsltCompilerCtxtPtr compCtxt; /* TODO: Change this to (void *). */ + + xsltPrincipalStylesheetDataPtr principalData; +#endif +}; + +typedef struct _xsltTransformCache xsltTransformCache; +typedef xsltTransformCache *xsltTransformCachePtr; +struct _xsltTransformCache { + xmlDocPtr RVT; + int nbRVT; + xsltStackElemPtr stackItems; + int nbStackItems; +#ifdef XSLT_DEBUG_PROFILE_CACHE + int dbgCachedRVTs; + int dbgReusedRVTs; + int dbgCachedVars; + int dbgReusedVars; +#endif +}; + +/* + * The in-memory structure corresponding to an XSLT Transformation. + */ +typedef enum { + XSLT_OUTPUT_XML = 0, + XSLT_OUTPUT_HTML, + XSLT_OUTPUT_TEXT +} xsltOutputType; + +typedef enum { + XSLT_STATE_OK = 0, + XSLT_STATE_ERROR, + XSLT_STATE_STOPPED +} xsltTransformState; + +struct _xsltTransformContext { + xsltStylesheetPtr style; /* the stylesheet used */ + xsltOutputType type; /* the type of output */ + + xsltTemplatePtr templ; /* the current template */ + int templNr; /* Nb of templates in the stack */ + int templMax; /* Size of the templtes stack */ + xsltTemplatePtr *templTab; /* the template stack */ + + xsltStackElemPtr vars; /* the current variable list */ + int varsNr; /* Nb of variable list in the stack */ + int varsMax; /* Size of the variable list stack */ + xsltStackElemPtr *varsTab; /* the variable list stack */ + int varsBase; /* the var base for current templ */ + + /* + * Extensions + */ + xmlHashTablePtr extFunctions; /* the extension functions */ + xmlHashTablePtr extElements; /* the extension elements */ + xmlHashTablePtr extInfos; /* the extension data */ + + const xmlChar *mode; /* the current mode */ + const xmlChar *modeURI; /* the current mode URI */ + + xsltDocumentPtr docList; /* the document list */ + + xsltDocumentPtr document; /* the current source document; can be NULL if an RTF */ + xmlNodePtr node; /* the current node being processed */ + xmlNodeSetPtr nodeList; /* the current node list */ + /* xmlNodePtr current; the node */ + + xmlDocPtr output; /* the resulting document */ + xmlNodePtr insert; /* the insertion node */ + + xmlXPathContextPtr xpathCtxt; /* the XPath context */ + xsltTransformState state; /* the current state */ + + /* + * Global variables + */ + xmlHashTablePtr globalVars; /* the global variables and params */ + + xmlNodePtr inst; /* the instruction in the stylesheet */ + + int xinclude; /* should XInclude be processed */ + + const char * outputFile; /* the output URI if known */ + + int profile; /* is this run profiled */ + long prof; /* the current profiled value */ + int profNr; /* Nb of templates in the stack */ + int profMax; /* Size of the templtaes stack */ + long *profTab; /* the profile template stack */ + + void *_private; /* user defined data */ + + int extrasNr; /* the number of extras used */ + int extrasMax; /* the number of extras allocated */ + xsltRuntimeExtraPtr extras; /* extra per runtime informations */ + + xsltDocumentPtr styleList; /* the stylesheet docs list */ + void * sec; /* the security preferences if any */ + + xmlGenericErrorFunc error; /* a specific error handler */ + void * errctx; /* context for the error handler */ + + xsltSortFunc sortfunc; /* a ctxt specific sort routine */ + + /* + * handling of temporary Result Value Tree + * (XSLT 1.0 term: "Result Tree Fragment") + */ + xmlDocPtr tmpRVT; /* list of RVT without persistance */ + xmlDocPtr persistRVT; /* list of persistant RVTs */ + int ctxtflags; /* context processing flags */ + + /* + * Speed optimization when coalescing text nodes + */ + const xmlChar *lasttext; /* last text node content */ + unsigned int lasttsize; /* last text node size */ + unsigned int lasttuse; /* last text node use */ + /* + * Per Context Debugging + */ + int debugStatus; /* the context level debug status */ + unsigned long* traceCode; /* pointer to the variable holding the mask */ + + int parserOptions; /* parser options xmlParserOption */ + + /* + * dictionnary: shared between stylesheet, context and documents. + */ + xmlDictPtr dict; + /* + * The current source doc; one of: the initial source doc, a RTF + * or a source doc aquired via the document() function. + */ + xmlDocPtr tmpDoc; + /* + * all document text strings are internalized + */ + int internalized; + int nbKeys; + int hasTemplKeyPatterns; + xsltTemplatePtr currentTemplateRule; /* the Current Template Rule */ + xmlNodePtr initialContextNode; + xmlDocPtr initialContextDoc; + xsltTransformCachePtr cache; + void *contextVariable; /* the current variable item */ + xmlDocPtr localRVT; /* list of local tree fragments; will be freed when + the instruction which created the fragment + exits */ + xmlDocPtr localRVTBase; +}; + +/** + * CHECK_STOPPED: + * + * Macro to check if the XSLT processing should be stopped. + * Will return from the function. + */ +#define CHECK_STOPPED if (ctxt->state == XSLT_STATE_STOPPED) return; + +/** + * CHECK_STOPPEDE: + * + * Macro to check if the XSLT processing should be stopped. + * Will goto the error: label. + */ +#define CHECK_STOPPEDE if (ctxt->state == XSLT_STATE_STOPPED) goto error; + +/** + * CHECK_STOPPED0: + * + * Macro to check if the XSLT processing should be stopped. + * Will return from the function with a 0 value. + */ +#define CHECK_STOPPED0 if (ctxt->state == XSLT_STATE_STOPPED) return(0); + +/* + * The macro XML_CAST_FPTR is a hack to avoid a gcc warning about + * possible incompatibilities between function pointers and object + * pointers. It is defined in libxml/hash.h within recent versions + * of libxml2, but is put here for compatibility. + */ +#ifndef XML_CAST_FPTR +/** + * XML_CAST_FPTR: + * @fptr: pointer to a function + * + * Macro to do a casting from an object pointer to a + * function pointer without encountering a warning from + * gcc + * + * #define XML_CAST_FPTR(fptr) (*(void **)(&fptr)) + * This macro violated ISO C aliasing rules (gcc4 on s390 broke) + * so it is disabled now + */ + +#define XML_CAST_FPTR(fptr) fptr +#endif +/* + * Functions associated to the internal types +xsltDecimalFormatPtr xsltDecimalFormatGetByName(xsltStylesheetPtr sheet, + xmlChar *name); + */ +XSLTPUBFUN xsltStylesheetPtr XSLTCALL + xsltNewStylesheet (void); +XSLTPUBFUN xsltStylesheetPtr XSLTCALL + xsltParseStylesheetFile (const xmlChar* filename); +XSLTPUBFUN void XSLTCALL + xsltFreeStylesheet (xsltStylesheetPtr style); +XSLTPUBFUN int XSLTCALL + xsltIsBlank (xmlChar *str); +XSLTPUBFUN void XSLTCALL + xsltFreeStackElemList (xsltStackElemPtr elem); +XSLTPUBFUN xsltDecimalFormatPtr XSLTCALL + xsltDecimalFormatGetByName(xsltStylesheetPtr style, + xmlChar *name); + +XSLTPUBFUN xsltStylesheetPtr XSLTCALL + xsltParseStylesheetProcess(xsltStylesheetPtr ret, + xmlDocPtr doc); +XSLTPUBFUN void XSLTCALL + xsltParseStylesheetOutput(xsltStylesheetPtr style, + xmlNodePtr cur); +XSLTPUBFUN xsltStylesheetPtr XSLTCALL + xsltParseStylesheetDoc (xmlDocPtr doc); +XSLTPUBFUN xsltStylesheetPtr XSLTCALL + xsltParseStylesheetImportedDoc(xmlDocPtr doc, + xsltStylesheetPtr style); +XSLTPUBFUN xsltStylesheetPtr XSLTCALL + xsltLoadStylesheetPI (xmlDocPtr doc); +XSLTPUBFUN void XSLTCALL + xsltNumberFormat (xsltTransformContextPtr ctxt, + xsltNumberDataPtr data, + xmlNodePtr node); +XSLTPUBFUN xmlXPathError XSLTCALL + xsltFormatNumberConversion(xsltDecimalFormatPtr self, + xmlChar *format, + double number, + xmlChar **result); + +XSLTPUBFUN void XSLTCALL + xsltParseTemplateContent(xsltStylesheetPtr style, + xmlNodePtr templ); +XSLTPUBFUN int XSLTCALL + xsltAllocateExtra (xsltStylesheetPtr style); +XSLTPUBFUN int XSLTCALL + xsltAllocateExtraCtxt (xsltTransformContextPtr ctxt); +/* + * Extra functions for Result Value Trees + */ +XSLTPUBFUN xmlDocPtr XSLTCALL + xsltCreateRVT (xsltTransformContextPtr ctxt); +XSLTPUBFUN int XSLTCALL + xsltRegisterTmpRVT (xsltTransformContextPtr ctxt, + xmlDocPtr RVT); +XSLTPUBFUN int XSLTCALL + xsltRegisterLocalRVT (xsltTransformContextPtr ctxt, + xmlDocPtr RVT); +XSLTPUBFUN int XSLTCALL + xsltRegisterPersistRVT (xsltTransformContextPtr ctxt, + xmlDocPtr RVT); +XSLTPUBFUN int XSLTCALL + xsltExtensionInstructionResultRegister( + xsltTransformContextPtr ctxt, + xmlXPathObjectPtr obj); +XSLTPUBFUN int XSLTCALL + xsltExtensionInstructionResultFinalize( + xsltTransformContextPtr ctxt); +XSLTPUBFUN void XSLTCALL + xsltFreeRVTs (xsltTransformContextPtr ctxt); +XSLTPUBFUN void XSLTCALL + xsltReleaseRVT (xsltTransformContextPtr ctxt, + xmlDocPtr RVT); +XSLTPUBFUN int XSLTCALL + xsltTransStorageAdd (xsltTransformContextPtr ctxt, + void *id, + void *data); +XSLTPUBFUN void * XSLTCALL + xsltTransStorageRemove (xsltTransformContextPtr ctxt, + void *id); + +/* + * Extra functions for Attribute Value Templates + */ +XSLTPUBFUN void XSLTCALL + xsltCompileAttr (xsltStylesheetPtr style, + xmlAttrPtr attr); +XSLTPUBFUN xmlChar * XSLTCALL + xsltEvalAVT (xsltTransformContextPtr ctxt, + void *avt, + xmlNodePtr node); +XSLTPUBFUN void XSLTCALL + xsltFreeAVTList (void *avt); + +/* + * Extra function for successful xsltCleanupGlobals / xsltInit sequence. + */ + +XSLTPUBFUN void XSLTCALL + xsltUninit (void); + +/************************************************************************ + * * + * Compile-time functions for *internal* use only * + * * + ************************************************************************/ + +#ifdef XSLT_REFACTORED +XSLTPUBFUN void XSLTCALL + xsltParseSequenceConstructor( + xsltCompilerCtxtPtr cctxt, + xmlNodePtr start); +XSLTPUBFUN int XSLTCALL + xsltParseAnyXSLTElem (xsltCompilerCtxtPtr cctxt, + xmlNodePtr elem); +#ifdef XSLT_REFACTORED_XSLT_NSCOMP +XSLTPUBFUN int XSLTCALL + xsltRestoreDocumentNamespaces( + xsltNsMapPtr ns, + xmlDocPtr doc); +#endif +#endif /* XSLT_REFACTORED */ + +/************************************************************************ + * * + * Transformation-time functions for *internal* use only * + * * + ************************************************************************/ +XSLTPUBFUN int XSLTCALL + xsltInitCtxtKey (xsltTransformContextPtr ctxt, + xsltDocumentPtr doc, + xsltKeyDefPtr keyd); + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_H__ */ + diff --git a/libxslt/xsltutils.c b/libxslt/xsltutils.c index ed3aeb28..5ac608ab 100644 --- a/libxslt/xsltutils.c +++ b/libxslt/xsltutils.c @@ -1,2239 +1,2246 @@ -/* - * xsltutils.c: Utilities for the XSL Transformation 1.0 engine - * - * Reference: - * http://www.w3.org/TR/1999/REC-xslt-19991116 - * - * See Copyright for the status of this software. - * - * daniel@veillard.com - */ - -#define IN_LIBXSLT -#include "libxslt.h" - -#include -#include -#ifdef HAVE_SYS_TIME_H -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#include - -#include -#include -#include -#include -#include -#include "xsltutils.h" -#include "templates.h" -#include "xsltInternals.h" -#include "imports.h" -#include "transform.h" - -/* gettimeofday on Windows ??? */ -#if defined(WIN32) && !defined(__CYGWIN__) -#ifdef _MSC_VER -#include -#pragma comment(lib, "ws2_32.lib") -#define gettimeofday(p1,p2) -#define HAVE_GETTIMEOFDAY -#define XSLT_WIN32_PERFORMANCE_COUNTER -#endif /* _MS_VER */ -#endif /* WIN32 */ - -#ifdef XSLT_NEED_TRIO -#include "trio.h" -#define vsnprintf trio_vsnprintf -#endif - -/************************************************************************ - * * - * Convenience function * - * * - ************************************************************************/ - -/** - * xsltGetCNsProp: - * @style: the stylesheet - * @node: the node - * @name: the attribute name - * @nameSpace: the URI of the namespace - * - * Similar to xmlGetNsProp() but with a slightly different semantic - * - * Search and get the value of an attribute associated to a node - * This attribute has to be anchored in the namespace specified, - * or has no namespace and the element is in that namespace. - * - * This does the entity substitution. - * This function looks in DTD attribute declaration for #FIXED or - * default declaration values unless DTD use has been turned off. - * - * Returns the attribute value or NULL if not found. The string is allocated - * in the stylesheet dictionnary. - */ -const xmlChar * -xsltGetCNsProp(xsltStylesheetPtr style, xmlNodePtr node, - const xmlChar *name, const xmlChar *nameSpace) { - xmlAttrPtr prop; - xmlDocPtr doc; - xmlNsPtr ns; - xmlChar *tmp; - const xmlChar *ret; - - if ((node == NULL) || (style == NULL) || (style->dict == NULL)) - return(NULL); - - prop = node->properties; - if (nameSpace == NULL) { - return xmlGetProp(node, name); - } - while (prop != NULL) { - /* - * One need to have - * - same attribute names - * - and the attribute carrying that namespace - */ - if ((xmlStrEqual(prop->name, name)) && - (((prop->ns == NULL) && (node->ns != NULL) && - (xmlStrEqual(node->ns->href, nameSpace))) || - ((prop->ns != NULL) && - (xmlStrEqual(prop->ns->href, nameSpace))))) { - - tmp = xmlNodeListGetString(node->doc, prop->children, 1); - if (tmp == NULL) - ret = xmlDictLookup(style->dict, BAD_CAST "", 0); - else { - ret = xmlDictLookup(style->dict, tmp, -1); - xmlFree(tmp); - } - return ret; - } - prop = prop->next; - } - tmp = NULL; - /* - * Check if there is a default declaration in the internal - * or external subsets - */ - doc = node->doc; - if (doc != NULL) { - if (doc->intSubset != NULL) { - xmlAttributePtr attrDecl; - - attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name); - if ((attrDecl == NULL) && (doc->extSubset != NULL)) - attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name); - - if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) { - /* - * The DTD declaration only allows a prefix search - */ - ns = xmlSearchNs(doc, node, attrDecl->prefix); - if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace))) - return(xmlDictLookup(style->dict, - attrDecl->defaultValue, -1)); - } - } - } - return(NULL); -} -/** - * xsltGetNsProp: - * @node: the node - * @name: the attribute name - * @nameSpace: the URI of the namespace - * - * Similar to xmlGetNsProp() but with a slightly different semantic - * - * Search and get the value of an attribute associated to a node - * This attribute has to be anchored in the namespace specified, - * or has no namespace and the element is in that namespace. - * - * This does the entity substitution. - * This function looks in DTD attribute declaration for #FIXED or - * default declaration values unless DTD use has been turned off. - * - * Returns the attribute value or NULL if not found. - * It's up to the caller to free the memory. - */ -xmlChar * -xsltGetNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) { - xmlAttrPtr prop; - xmlDocPtr doc; - xmlNsPtr ns; - - if (node == NULL) - return(NULL); - - prop = node->properties; - /* - * TODO: Substitute xmlGetProp() for xmlGetNsProp(), since the former - * is not namespace-aware and will return an attribute with equal - * name regardless of its namespace. - * Example: - * - * So this would return "myName" even if an attribute @name - * in the XSLT was requested. - */ - if (nameSpace == NULL) - return(xmlGetProp(node, name)); - while (prop != NULL) { - /* - * One need to have - * - same attribute names - * - and the attribute carrying that namespace - */ - if ((xmlStrEqual(prop->name, name)) && - (((prop->ns == NULL) && (node->ns != NULL) && - (xmlStrEqual(node->ns->href, nameSpace))) || - ((prop->ns != NULL) && - (xmlStrEqual(prop->ns->href, nameSpace))))) { - xmlChar *ret; - - ret = xmlNodeListGetString(node->doc, prop->children, 1); - if (ret == NULL) return(xmlStrdup((xmlChar *)"")); - return(ret); - } - prop = prop->next; - } - - /* - * Check if there is a default declaration in the internal - * or external subsets - */ - doc = node->doc; - if (doc != NULL) { - if (doc->intSubset != NULL) { - xmlAttributePtr attrDecl; - - attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name); - if ((attrDecl == NULL) && (doc->extSubset != NULL)) - attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name); - - if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) { - /* - * The DTD declaration only allows a prefix search - */ - ns = xmlSearchNs(doc, node, attrDecl->prefix); - if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace))) - return(xmlStrdup(attrDecl->defaultValue)); - } - } - } - return(NULL); -} - -/** - * xsltGetUTF8Char: - * @utf: a sequence of UTF-8 encoded bytes - * @len: a pointer to @bytes len - * - * Read one UTF8 Char from @utf - * Function copied from libxml2 xmlGetUTF8Char() ... to discard ultimately - * and use the original API - * - * Returns the char value or -1 in case of error and update @len with the - * number of bytes used - */ -int -xsltGetUTF8Char(const unsigned char *utf, int *len) { - unsigned int c; - - if (utf == NULL) - goto error; - if (len == NULL) - goto error; - if (*len < 1) - goto error; - - c = utf[0]; - if (c & 0x80) { - if (*len < 2) - goto error; - if ((utf[1] & 0xc0) != 0x80) - goto error; - if ((c & 0xe0) == 0xe0) { - if (*len < 3) - goto error; - if ((utf[2] & 0xc0) != 0x80) - goto error; - if ((c & 0xf0) == 0xf0) { - if (*len < 4) - goto error; - if ((c & 0xf8) != 0xf0 || (utf[3] & 0xc0) != 0x80) - goto error; - *len = 4; - /* 4-byte code */ - c = (utf[0] & 0x7) << 18; - c |= (utf[1] & 0x3f) << 12; - c |= (utf[2] & 0x3f) << 6; - c |= utf[3] & 0x3f; - } else { - /* 3-byte code */ - *len = 3; - c = (utf[0] & 0xf) << 12; - c |= (utf[1] & 0x3f) << 6; - c |= utf[2] & 0x3f; - } - } else { - /* 2-byte code */ - *len = 2; - c = (utf[0] & 0x1f) << 6; - c |= utf[1] & 0x3f; - } - } else { - /* 1-byte code */ - *len = 1; - } - return(c); - -error: - if (len != NULL) - *len = 0; - return(-1); -} - -#ifdef XSLT_REFACTORED - -/** - * xsltPointerListAddSize: - * @list: the pointer list structure - * @item: the item to be stored - * @initialSize: the initial size of the list - * - * Adds an item to the list. - * - * Returns the position of the added item in the list or - * -1 in case of an error. - */ -int -xsltPointerListAddSize(xsltPointerListPtr list, - void *item, - int initialSize) -{ - if (list->items == NULL) { - if (initialSize <= 0) - initialSize = 1; - list->items = (void **) xmlMalloc( - initialSize * sizeof(void *)); - if (list->items == NULL) { - xsltGenericError(xsltGenericErrorContext, - "xsltPointerListAddSize: memory allocation failure.\n"); - return(-1); - } - list->number = 0; - list->size = initialSize; - } else if (list->size <= list->number) { - list->size *= 2; - list->items = (void **) xmlRealloc(list->items, - list->size * sizeof(void *)); - if (list->items == NULL) { - xsltGenericError(xsltGenericErrorContext, - "xsltPointerListAddSize: memory re-allocation failure.\n"); - list->size = 0; - return(-1); - } - } - list->items[list->number++] = item; - return(0); -} - -/** - * xsltPointerListCreate: - * - * Creates an xsltPointerList structure. - * - * Returns a xsltPointerList structure or NULL in case of an error. - */ -xsltPointerListPtr -xsltPointerListCreate(int initialSize) -{ - xsltPointerListPtr ret; - - ret = xmlMalloc(sizeof(xsltPointerList)); - if (ret == NULL) { - xsltGenericError(xsltGenericErrorContext, - "xsltPointerListCreate: memory allocation failure.\n"); - return (NULL); - } - memset(ret, 0, sizeof(xsltPointerList)); - if (initialSize > 0) { - xsltPointerListAddSize(ret, NULL, initialSize); - ret->number = 0; - } - return (ret); -} - -/** - * xsltPointerListFree: - * - * Frees the xsltPointerList structure. This does not free - * the content of the list. - */ -void -xsltPointerListFree(xsltPointerListPtr list) -{ - if (list == NULL) - return; - if (list->items != NULL) - xmlFree(list->items); - xmlFree(list); -} - -/** - * xsltPointerListFree: - * - * Resets the list, but does not free the allocated array - * and does not free the content of the list. - */ -void -xsltPointerListClear(xsltPointerListPtr list) -{ - if (list->items != NULL) { - xmlFree(list->items); - list->items = NULL; - } - list->number = 0; - list->size = 0; -} - -#endif /* XSLT_REFACTORED */ - -/************************************************************************ - * * - * Handling of XSLT stylesheets messages * - * * - ************************************************************************/ - -/** - * xsltMessage: - * @ctxt: an XSLT processing context - * @node: The current node - * @inst: The node containing the message instruction - * - * Process and xsl:message construct - */ -void -xsltMessage(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst) { - xmlChar *prop, *message; - int terminate = 0; - - if ((ctxt == NULL) || (inst == NULL)) - return; - - prop = xmlGetNsProp(inst, (const xmlChar *)"terminate", NULL); - if (prop != NULL) { - if (xmlStrEqual(prop, (const xmlChar *)"yes")) { - terminate = 1; - } else if (xmlStrEqual(prop, (const xmlChar *)"no")) { - terminate = 0; - } else { - xsltGenericError(xsltGenericErrorContext, - "xsl:message : terminate expecting 'yes' or 'no'\n"); - ctxt->state = XSLT_STATE_ERROR; - } - xmlFree(prop); - } - message = xsltEvalTemplateString(ctxt, node, inst); - if (message != NULL) { - int len = xmlStrlen(message); - - xsltGenericError(xsltGenericErrorContext, "%s", - (const char *)message); - if ((len > 0) && (message[len - 1] != '\n')) - xsltGenericError(xsltGenericErrorContext, "\n"); - xmlFree(message); - } - if (terminate) - ctxt->state = XSLT_STATE_STOPPED; -} - -/************************************************************************ - * * - * Handling of out of context errors * - * * - ************************************************************************/ - -#define XSLT_GET_VAR_STR(msg, str) { \ - int size; \ - int chars; \ - char *larger; \ - va_list ap; \ - \ - str = (char *) xmlMalloc(150); \ - if (str == NULL) \ - return; \ - \ - size = 150; \ - \ - while (1) { \ - va_start(ap, msg); \ - chars = vsnprintf(str, size, msg, ap); \ - va_end(ap); \ - if ((chars > -1) && (chars < size)) \ - break; \ - if (chars > -1) \ - size += chars + 1; \ - else \ - size += 100; \ - if ((larger = (char *) xmlRealloc(str, size)) == NULL) {\ - xmlFree(str); \ - return; \ - } \ - str = larger; \ - } \ -} -/** - * xsltGenericErrorDefaultFunc: - * @ctx: an error context - * @msg: the message to display/transmit - * @...: extra parameters for the message display - * - * Default handler for out of context error messages. - */ -static void -xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { - va_list args; - - if (xsltGenericErrorContext == NULL) - xsltGenericErrorContext = (void *) stderr; - - va_start(args, msg); - vfprintf((FILE *)xsltGenericErrorContext, msg, args); - va_end(args); -} - -xmlGenericErrorFunc xsltGenericError = xsltGenericErrorDefaultFunc; -void *xsltGenericErrorContext = NULL; - - -/** - * xsltSetGenericErrorFunc: - * @ctx: the new error handling context - * @handler: the new handler function - * - * Function to reset the handler and the error context for out of - * context error messages. - * This simply means that @handler will be called for subsequent - * error messages while not parsing nor validating. And @ctx will - * be passed as first argument to @handler - * One can simply force messages to be emitted to another FILE * than - * stderr by setting @ctx to this file handle and @handler to NULL. - */ -void -xsltSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) { - xsltGenericErrorContext = ctx; - if (handler != NULL) - xsltGenericError = handler; - else - xsltGenericError = xsltGenericErrorDefaultFunc; -} - -/** - * xsltGenericDebugDefaultFunc: - * @ctx: an error context - * @msg: the message to display/transmit - * @...: extra parameters for the message display - * - * Default handler for out of context error messages. - */ -static void -xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { - va_list args; - - if (xsltGenericDebugContext == NULL) - return; - - va_start(args, msg); - vfprintf((FILE *)xsltGenericDebugContext, msg, args); - va_end(args); -} - -xmlGenericErrorFunc xsltGenericDebug = xsltGenericDebugDefaultFunc; -void *xsltGenericDebugContext = NULL; - - -/** - * xsltSetGenericDebugFunc: - * @ctx: the new error handling context - * @handler: the new handler function - * - * Function to reset the handler and the error context for out of - * context error messages. - * This simply means that @handler will be called for subsequent - * error messages while not parsing or validating. And @ctx will - * be passed as first argument to @handler - * One can simply force messages to be emitted to another FILE * than - * stderr by setting @ctx to this file handle and @handler to NULL. - */ -void -xsltSetGenericDebugFunc(void *ctx, xmlGenericErrorFunc handler) { - xsltGenericDebugContext = ctx; - if (handler != NULL) - xsltGenericDebug = handler; - else - xsltGenericDebug = xsltGenericDebugDefaultFunc; -} - -/** - * xsltPrintErrorContext: - * @ctxt: the transformation context - * @style: the stylesheet - * @node: the current node being processed - * - * Display the context of an error. - */ -void -xsltPrintErrorContext(xsltTransformContextPtr ctxt, - xsltStylesheetPtr style, xmlNodePtr node) { - int line = 0; - const xmlChar *file = NULL; - const xmlChar *name = NULL; - const char *type = "error"; - xmlGenericErrorFunc error = xsltGenericError; - void *errctx = xsltGenericErrorContext; - - if (ctxt != NULL) { - ctxt->state = XSLT_STATE_ERROR; - if (ctxt->error != NULL) { - error = ctxt->error; - errctx = ctxt->errctx; - } - } - if ((node == NULL) && (ctxt != NULL)) - node = ctxt->inst; - - if (node != NULL) { - if ((node->type == XML_DOCUMENT_NODE) || - (node->type == XML_HTML_DOCUMENT_NODE)) { - xmlDocPtr doc = (xmlDocPtr) node; - - file = doc->URL; - } else { - line = xmlGetLineNo(node); - if ((node->doc != NULL) && (node->doc->URL != NULL)) - file = node->doc->URL; - if (node->name != NULL) - name = node->name; - } - } - - if (ctxt != NULL) - type = "runtime error"; - else if (style != NULL) { -#ifdef XSLT_REFACTORED - if (XSLT_CCTXT(style)->errSeverity == XSLT_ERROR_SEVERITY_WARNING) - type = "compilation warning"; - else - type = "compilation error"; -#else - type = "compilation error"; -#endif - } - - if ((file != NULL) && (line != 0) && (name != NULL)) - error(errctx, "%s: file %s line %d element %s\n", - type, file, line, name); - else if ((file != NULL) && (name != NULL)) - error(errctx, "%s: file %s element %s\n", type, file, name); - else if ((file != NULL) && (line != 0)) - error(errctx, "%s: file %s line %d\n", type, file, line); - else if (file != NULL) - error(errctx, "%s: file %s\n", type, file); - else if (name != NULL) - error(errctx, "%s: element %s\n", type, name); - else - error(errctx, "%s\n", type); -} - -/** - * xsltSetTransformErrorFunc: - * @ctxt: the XSLT transformation context - * @ctx: the new error handling context - * @handler: the new handler function - * - * Function to reset the handler and the error context for out of - * context error messages specific to a given XSLT transromation. - * - * This simply means that @handler will be called for subsequent - * error messages while running the transformation. - */ -void -xsltSetTransformErrorFunc(xsltTransformContextPtr ctxt, - void *ctx, xmlGenericErrorFunc handler) -{ - ctxt->error = handler; - ctxt->errctx = ctx; -} - -/** - * xsltTransformError: - * @ctxt: an XSLT transformation context - * @style: the XSLT stylesheet used - * @node: the current node in the stylesheet - * @msg: the message to display/transmit - * @...: extra parameters for the message display - * - * Display and format an error messages, gives file, line, position and - * extra parameters, will use the specific transformation context if available - */ -void -xsltTransformError(xsltTransformContextPtr ctxt, - xsltStylesheetPtr style, - xmlNodePtr node, - const char *msg, ...) { - xmlGenericErrorFunc error = xsltGenericError; - void *errctx = xsltGenericErrorContext; - char * str; - - if (ctxt != NULL) { - ctxt->state = XSLT_STATE_ERROR; - if (ctxt->error != NULL) { - error = ctxt->error; - errctx = ctxt->errctx; - } - } - if ((node == NULL) && (ctxt != NULL)) - node = ctxt->inst; - xsltPrintErrorContext(ctxt, style, node); - XSLT_GET_VAR_STR(msg, str); - error(errctx, "%s", str); - if (str != NULL) - xmlFree(str); -} - -/************************************************************************ - * * - * QNames * - * * - ************************************************************************/ - -/** - * xsltSplitQName: - * @dict: a dictionnary - * @name: the full QName - * @prefix: the return value - * - * Split QNames into prefix and local names, both allocated from a dictionnary. - * - * Returns: the localname or NULL in case of error. - */ -const xmlChar * -xsltSplitQName(xmlDictPtr dict, const xmlChar *name, const xmlChar **prefix) { - int len = 0; - const xmlChar *ret = NULL; - - *prefix = NULL; - if ((name == NULL) || (dict == NULL)) return(NULL); - if (name[0] == ':') - return(xmlDictLookup(dict, name, -1)); - while ((name[len] != 0) && (name[len] != ':')) len++; - if (name[len] == 0) return(xmlDictLookup(dict, name, -1)); - *prefix = xmlDictLookup(dict, name, len); - ret = xmlDictLookup(dict, &name[len + 1], -1); - return(ret); -} - -/** - * xsltGetQNameURI: - * @node: the node holding the QName - * @name: pointer to the initial QName value - * - * This function analyzes @name, if the name contains a prefix, - * the function seaches the associated namespace in scope for it. - * It will also replace @name value with the NCName, the old value being - * freed. - * Errors in the prefix lookup are signalled by setting @name to NULL. - * - * NOTE: the namespace returned is a pointer to the place where it is - * defined and hence has the same lifespan as the document holding it. - * - * Returns the namespace URI if there is a prefix, or NULL if @name is - * not prefixed. - */ -const xmlChar * -xsltGetQNameURI(xmlNodePtr node, xmlChar ** name) -{ - int len = 0; - xmlChar *qname; - xmlNsPtr ns; - - if (name == NULL) - return(NULL); - qname = *name; - if ((qname == NULL) || (*qname == 0)) - return(NULL); - if (node == NULL) { - xsltGenericError(xsltGenericErrorContext, - "QName: no element for namespace lookup %s\n", - qname); - xmlFree(qname); - *name = NULL; - return(NULL); - } - - /* nasty but valid */ - if (qname[0] == ':') - return(NULL); - - /* - * we are not trying to validate but just to cut, and yes it will - * work even if this is a set of UTF-8 encoded chars - */ - while ((qname[len] != 0) && (qname[len] != ':')) - len++; - - if (qname[len] == 0) - return(NULL); - - /* - * handle xml: separately, this one is magical - */ - if ((qname[0] == 'x') && (qname[1] == 'm') && - (qname[2] == 'l') && (qname[3] == ':')) { - if (qname[4] == 0) - return(NULL); - *name = xmlStrdup(&qname[4]); - xmlFree(qname); - return(XML_XML_NAMESPACE); - } - - qname[len] = 0; - ns = xmlSearchNs(node->doc, node, qname); - if (ns == NULL) { - xsltGenericError(xsltGenericErrorContext, - "%s:%s : no namespace bound to prefix %s\n", - qname, &qname[len + 1], qname); - *name = NULL; - xmlFree(qname); - return(NULL); - } - *name = xmlStrdup(&qname[len + 1]); - xmlFree(qname); - return(ns->href); -} - -/** - * xsltGetQNameURI2: - * @style: stylesheet pointer - * @node: the node holding the QName - * @name: pointer to the initial QName value - * - * This function is similar to xsltGetQNameURI, but is used when - * @name is a dictionary entry. - * - * Returns the namespace URI if there is a prefix, or NULL if @name is - * not prefixed. - */ -const xmlChar * -xsltGetQNameURI2(xsltStylesheetPtr style, xmlNodePtr node, - const xmlChar **name) { - int len = 0; - xmlChar *qname; - xmlNsPtr ns; - - if (name == NULL) - return(NULL); - qname = (xmlChar *)*name; - if ((qname == NULL) || (*qname == 0)) - return(NULL); - if (node == NULL) { - xsltGenericError(xsltGenericErrorContext, - "QName: no element for namespace lookup %s\n", - qname); - *name = NULL; - return(NULL); - } - - /* - * we are not trying to validate but just to cut, and yes it will - * work even if this is a set of UTF-8 encoded chars - */ - while ((qname[len] != 0) && (qname[len] != ':')) - len++; - - if (qname[len] == 0) - return(NULL); - - /* - * handle xml: separately, this one is magical - */ - if ((qname[0] == 'x') && (qname[1] == 'm') && - (qname[2] == 'l') && (qname[3] == ':')) { - if (qname[4] == 0) - return(NULL); - *name = xmlDictLookup(style->dict, &qname[4], -1); - return(XML_XML_NAMESPACE); - } - - qname = xmlStrndup(*name, len); - ns = xmlSearchNs(node->doc, node, qname); - if (ns == NULL) { - xsltGenericError(xsltGenericErrorContext, - "%s : no namespace bound to prefix %s\n", - *name, qname); - *name = NULL; - xmlFree(qname); - return(NULL); - } - *name = xmlDictLookup(style->dict, (*name)+len+1, -1); - xmlFree(qname); - return(ns->href); -} - -/************************************************************************ - * * - * Sorting * - * * - ************************************************************************/ - -/** - * xsltDocumentSortFunction: - * @list: the node set - * - * reorder the current node list @list accordingly to the document order - * This function is slow, obsolete and should not be used anymore. - */ -void -xsltDocumentSortFunction(xmlNodeSetPtr list) { - int i, j; - int len, tst; - xmlNodePtr node; - - if (list == NULL) - return; - len = list->nodeNr; - if (len <= 1) - return; - /* TODO: sort is really not optimized, does it needs to ? */ - for (i = 0;i < len -1;i++) { - for (j = i + 1; j < len; j++) { - tst = xmlXPathCmpNodes(list->nodeTab[i], list->nodeTab[j]); - if (tst == -1) { - node = list->nodeTab[i]; - list->nodeTab[i] = list->nodeTab[j]; - list->nodeTab[j] = node; - } - } - } -} - -/** - * xsltComputeSortResult: - * @ctxt: a XSLT process context - * @sort: node list - * - * reorder the current node list accordingly to the set of sorting - * requirement provided by the array of nodes. - * - * Returns a ordered XPath nodeset or NULL in case of error. - */ -xmlXPathObjectPtr * -xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) { -#ifdef XSLT_REFACTORED - xsltStyleItemSortPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - xmlXPathObjectPtr *results = NULL; - xmlNodeSetPtr list = NULL; - xmlXPathObjectPtr res; - int len = 0; - int i; - xmlNodePtr oldNode; - xmlNodePtr oldInst; - int oldPos, oldSize ; - int oldNsNr; - xmlNsPtr *oldNamespaces; - - comp = sort->psvi; - if (comp == NULL) { - xsltGenericError(xsltGenericErrorContext, - "xsl:sort : compilation failed\n"); - return(NULL); - } - - if ((comp->select == NULL) || (comp->comp == NULL)) - return(NULL); - - list = ctxt->nodeList; - if ((list == NULL) || (list->nodeNr <= 1)) - return(NULL); - - len = list->nodeNr; - - /* TODO: xsl:sort lang attribute */ - /* TODO: xsl:sort case-order attribute */ - - - results = xmlMalloc(len * sizeof(xmlXPathObjectPtr)); - if (results == NULL) { - xsltGenericError(xsltGenericErrorContext, - "xsltComputeSortResult: memory allocation failure\n"); - return(NULL); - } - - oldNode = ctxt->node; - oldInst = ctxt->inst; - oldPos = ctxt->xpathCtxt->proximityPosition; - oldSize = ctxt->xpathCtxt->contextSize; - oldNsNr = ctxt->xpathCtxt->nsNr; - oldNamespaces = ctxt->xpathCtxt->namespaces; - for (i = 0;i < len;i++) { - ctxt->inst = sort; - ctxt->xpathCtxt->contextSize = len; - ctxt->xpathCtxt->proximityPosition = i + 1; - ctxt->node = list->nodeTab[i]; - ctxt->xpathCtxt->node = ctxt->node; -#ifdef XSLT_REFACTORED - if (comp->inScopeNs != NULL) { - ctxt->xpathCtxt->namespaces = comp->inScopeNs->list; - ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber; - } else { - ctxt->xpathCtxt->namespaces = NULL; - ctxt->xpathCtxt->nsNr = 0; - } -#else - ctxt->xpathCtxt->namespaces = comp->nsList; - ctxt->xpathCtxt->nsNr = comp->nsNr; -#endif - res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt); - if (res != NULL) { - if (res->type != XPATH_STRING) - res = xmlXPathConvertString(res); - if (comp->number) - res = xmlXPathConvertNumber(res); - res->index = i; /* Save original pos for dupl resolv */ - if (comp->number) { - if (res->type == XPATH_NUMBER) { - results[i] = res; - } else { -#ifdef WITH_XSLT_DEBUG_PROCESS - xsltGenericDebug(xsltGenericDebugContext, - "xsltComputeSortResult: select didn't evaluate to a number\n"); -#endif - results[i] = NULL; - } - } else { - if (res->type == XPATH_STRING) { - results[i] = res; - } else { -#ifdef WITH_XSLT_DEBUG_PROCESS - xsltGenericDebug(xsltGenericDebugContext, - "xsltComputeSortResult: select didn't evaluate to a string\n"); -#endif - results[i] = NULL; - } - } - } else { - ctxt->state = XSLT_STATE_STOPPED; - results[i] = NULL; - } - } - ctxt->node = oldNode; - ctxt->inst = oldInst; - ctxt->xpathCtxt->contextSize = oldSize; - ctxt->xpathCtxt->proximityPosition = oldPos; - ctxt->xpathCtxt->nsNr = oldNsNr; - ctxt->xpathCtxt->namespaces = oldNamespaces; - - return(results); -} - -/** - * xsltDefaultSortFunction: - * @ctxt: a XSLT process context - * @sorts: array of sort nodes - * @nbsorts: the number of sorts in the array - * - * reorder the current node list accordingly to the set of sorting - * requirement provided by the arry of nodes. - */ -void -xsltDefaultSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts, - int nbsorts) { -#ifdef XSLT_REFACTORED - xsltStyleItemSortPtr comp; -#else - xsltStylePreCompPtr comp; -#endif - xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT]; - xmlXPathObjectPtr *results = NULL, *res; - xmlNodeSetPtr list = NULL; - int descending, number, desc, numb; - int len = 0; - int i, j, incr; - int tst; - int depth; - xmlNodePtr node; - xmlXPathObjectPtr tmp; - int tempstype[XSLT_MAX_SORT], temporder[XSLT_MAX_SORT]; - - if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) || - (nbsorts >= XSLT_MAX_SORT)) - return; - if (sorts[0] == NULL) - return; - comp = sorts[0]->psvi; - if (comp == NULL) - return; - - list = ctxt->nodeList; - if ((list == NULL) || (list->nodeNr <= 1)) - return; /* nothing to do */ - - for (j = 0; j < nbsorts; j++) { - comp = sorts[j]->psvi; - tempstype[j] = 0; - if ((comp->stype == NULL) && (comp->has_stype != 0)) { - comp->stype = - xsltEvalAttrValueTemplate(ctxt, sorts[j], - (const xmlChar *) "data-type", - XSLT_NAMESPACE); - if (comp->stype != NULL) { - tempstype[j] = 1; - if (xmlStrEqual(comp->stype, (const xmlChar *) "text")) - comp->number = 0; - else if (xmlStrEqual(comp->stype, (const xmlChar *) "number")) - comp->number = 1; - else { - xsltTransformError(ctxt, NULL, sorts[j], - "xsltDoSortFunction: no support for data-type = %s\n", - comp->stype); - comp->number = 0; /* use default */ - } - } - } - temporder[j] = 0; - if ((comp->order == NULL) && (comp->has_order != 0)) { - comp->order = xsltEvalAttrValueTemplate(ctxt, sorts[j], - (const xmlChar *) "order", - XSLT_NAMESPACE); - if (comp->order != NULL) { - temporder[j] = 1; - if (xmlStrEqual(comp->order, (const xmlChar *) "ascending")) - comp->descending = 0; - else if (xmlStrEqual(comp->order, - (const xmlChar *) "descending")) - comp->descending = 1; - else { - xsltTransformError(ctxt, NULL, sorts[j], - "xsltDoSortFunction: invalid value %s for order\n", - comp->order); - comp->descending = 0; /* use default */ - } - } - } - } - - len = list->nodeNr; - - resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]); - for (i = 1;i < XSLT_MAX_SORT;i++) - resultsTab[i] = NULL; - - results = resultsTab[0]; - - comp = sorts[0]->psvi; - descending = comp->descending; - number = comp->number; - if (results == NULL) - return; - - /* Shell's sort of node-set */ - for (incr = len / 2; incr > 0; incr /= 2) { - for (i = incr; i < len; i++) { - j = i - incr; - if (results[i] == NULL) - continue; - - while (j >= 0) { - if (results[j] == NULL) - tst = 1; - else { - if (number) { - /* We make NaN smaller than number in accordance - with XSLT spec */ - if (xmlXPathIsNaN(results[j]->floatval)) { - if (xmlXPathIsNaN(results[j + incr]->floatval)) - tst = 0; - else - tst = -1; - } else if (xmlXPathIsNaN(results[j + incr]->floatval)) - tst = 1; - else if (results[j]->floatval == - results[j + incr]->floatval) - tst = 0; - else if (results[j]->floatval > - results[j + incr]->floatval) - tst = 1; - else tst = -1; - } else { - tst = xmlStrcmp(results[j]->stringval, - results[j + incr]->stringval); - } - if (descending) - tst = -tst; - } - if (tst == 0) { - /* - * Okay we need to use multi level sorts - */ - depth = 1; - while (depth < nbsorts) { - if (sorts[depth] == NULL) - break; - comp = sorts[depth]->psvi; - if (comp == NULL) - break; - desc = comp->descending; - numb = comp->number; - - /* - * Compute the result of the next level for the - * full set, this might be optimized ... or not - */ - if (resultsTab[depth] == NULL) - resultsTab[depth] = xsltComputeSortResult(ctxt, - sorts[depth]); - res = resultsTab[depth]; - if (res == NULL) - break; - if (res[j] == NULL) { - if (res[j+incr] != NULL) - tst = 1; - } else { - if (numb) { - /* We make NaN smaller than number in - accordance with XSLT spec */ - if (xmlXPathIsNaN(res[j]->floatval)) { - if (xmlXPathIsNaN(res[j + - incr]->floatval)) - tst = 0; - else - tst = -1; - } else if (xmlXPathIsNaN(res[j + incr]-> - floatval)) - tst = 1; - else if (res[j]->floatval == res[j + incr]-> - floatval) - tst = 0; - else if (res[j]->floatval > - res[j + incr]->floatval) - tst = 1; - else tst = -1; - } else { - tst = xmlStrcmp(res[j]->stringval, - res[j + incr]->stringval); - } - if (desc) - tst = -tst; - } - - /* - * if we still can't differenciate at this level - * try one level deeper. - */ - if (tst != 0) - break; - depth++; - } - } - if (tst == 0) { - tst = results[j]->index > results[j + incr]->index; - } - if (tst > 0) { - tmp = results[j]; - results[j] = results[j + incr]; - results[j + incr] = tmp; - node = list->nodeTab[j]; - list->nodeTab[j] = list->nodeTab[j + incr]; - list->nodeTab[j + incr] = node; - depth = 1; - while (depth < nbsorts) { - if (sorts[depth] == NULL) - break; - if (resultsTab[depth] == NULL) - break; - res = resultsTab[depth]; - tmp = res[j]; - res[j] = res[j + incr]; - res[j + incr] = tmp; - depth++; - } - j -= incr; - } else - break; - } - } - } - - for (j = 0; j < nbsorts; j++) { - comp = sorts[j]->psvi; - if (tempstype[j] == 1) { - /* The data-type needs to be recomputed each time */ - xmlFree((void *)(comp->stype)); - comp->stype = NULL; - } - if (temporder[j] == 1) { - /* The order needs to be recomputed each time */ - xmlFree((void *)(comp->order)); - comp->order = NULL; - } - if (resultsTab[j] != NULL) { - for (i = 0;i < len;i++) - xmlXPathFreeObject(resultsTab[j][i]); - xmlFree(resultsTab[j]); - } - } -} - - -static xsltSortFunc xsltSortFunction = xsltDefaultSortFunction; - -/** - * xsltDoSortFunction: - * @ctxt: a XSLT process context - * @sorts: array of sort nodes - * @nbsorts: the number of sorts in the array - * - * reorder the current node list accordingly to the set of sorting - * requirement provided by the arry of nodes. - * This is a wrapper function, the actual function used is specified - * using xsltSetCtxtSortFunc() to set the context specific sort function, - * or xsltSetSortFunc() to set the global sort function. - * If a sort function is set on the context, this will get called. - * Otherwise the global sort function is called. - */ -void -xsltDoSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr * sorts, - int nbsorts) -{ - if (ctxt->sortfunc != NULL) - (ctxt->sortfunc)(ctxt, sorts, nbsorts); - else if (xsltSortFunction != NULL) - xsltSortFunction(ctxt, sorts, nbsorts); -} - -/** - * xsltSetSortFunc: - * @handler: the new handler function - * - * Function to reset the global handler for XSLT sorting. - * If the handler is NULL, the default sort function will be used. - */ -void -xsltSetSortFunc(xsltSortFunc handler) { - if (handler != NULL) - xsltSortFunction = handler; - else - xsltSortFunction = xsltDefaultSortFunction; -} - -/** - * xsltSetCtxtSortFunc: - * @ctxt: a XSLT process context - * @handler: the new handler function - * - * Function to set the handler for XSLT sorting - * for the specified context. - * If the handler is NULL, then the global - * sort function will be called - */ -void -xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt, xsltSortFunc handler) { - ctxt->sortfunc = handler; -} - -/************************************************************************ - * * - * Parsing options * - * * - ************************************************************************/ - -/** - * xsltSetCtxtParseOptions: - * @ctxt: a XSLT process context - * @options: a combination of libxml2 xmlParserOption - * - * Change the default parser option passed by the XSLT engine to the - * parser when using document() loading. - * - * Returns the previous options or -1 in case of error - */ -int -xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt, int options) -{ - int oldopts; - - if (ctxt == NULL) - return(-1); - oldopts = ctxt->parserOptions; - ctxt->parserOptions = options; - return(oldopts); -} - -/************************************************************************ - * * - * Output * - * * - ************************************************************************/ - -/** - * xsltSaveResultTo: - * @buf: an output buffer - * @result: the result xmlDocPtr - * @style: the stylesheet - * - * Save the result @result obtained by applying the @style stylesheet - * to an I/O output channel @buf - * - * Returns the number of byte written or -1 in case of failure. - */ -int -xsltSaveResultTo(xmlOutputBufferPtr buf, xmlDocPtr result, - xsltStylesheetPtr style) { - const xmlChar *encoding; - int base; - const xmlChar *method; - int indent; - - if ((buf == NULL) || (result == NULL) || (style == NULL)) - return(-1); - if ((result->children == NULL) || - ((result->children->type == XML_DTD_NODE) && - (result->children->next == NULL))) - return(0); - - if ((style->methodURI != NULL) && - ((style->method == NULL) || - (!xmlStrEqual(style->method, (const xmlChar *) "xhtml")))) { - xsltGenericError(xsltGenericErrorContext, - "xsltSaveResultTo : unknown ouput method\n"); - return(-1); - } - - base = buf->written; - - XSLT_GET_IMPORT_PTR(method, style, method) - XSLT_GET_IMPORT_PTR(encoding, style, encoding) - XSLT_GET_IMPORT_INT(indent, style, indent); - - if ((method == NULL) && (result->type == XML_HTML_DOCUMENT_NODE)) - method = (const xmlChar *) "html"; - - if ((method != NULL) && - (xmlStrEqual(method, (const xmlChar *) "html"))) { - if (encoding != NULL) { - htmlSetMetaEncoding(result, (const xmlChar *) encoding); - } else { - htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8"); - } - if (indent == -1) - indent = 1; - htmlDocContentDumpFormatOutput(buf, result, (const char *) encoding, - indent); - xmlOutputBufferFlush(buf); - } else if ((method != NULL) && - (xmlStrEqual(method, (const xmlChar *) "xhtml"))) { - if (encoding != NULL) { - htmlSetMetaEncoding(result, (const xmlChar *) encoding); - } else { - htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8"); - } - htmlDocContentDumpOutput(buf, result, (const char *) encoding); - xmlOutputBufferFlush(buf); - } else if ((method != NULL) && - (xmlStrEqual(method, (const xmlChar *) "text"))) { - xmlNodePtr cur; - - cur = result->children; - while (cur != NULL) { - if (cur->type == XML_TEXT_NODE) - xmlOutputBufferWriteString(buf, (const char *) cur->content); - - /* - * Skip to next node - */ - if (cur->children != NULL) { - if ((cur->children->type != XML_ENTITY_DECL) && - (cur->children->type != XML_ENTITY_REF_NODE) && - (cur->children->type != XML_ENTITY_NODE)) { - cur = cur->children; - continue; - } - } - if (cur->next != NULL) { - cur = cur->next; - continue; - } - - do { - cur = cur->parent; - if (cur == NULL) - break; - if (cur == (xmlNodePtr) style->doc) { - cur = NULL; - break; - } - if (cur->next != NULL) { - cur = cur->next; - break; - } - } while (cur != NULL); - } - xmlOutputBufferFlush(buf); - } else { - int omitXmlDecl; - int standalone; - - XSLT_GET_IMPORT_INT(omitXmlDecl, style, omitXmlDeclaration); - XSLT_GET_IMPORT_INT(standalone, style, standalone); - - if (omitXmlDecl != 1) { - xmlOutputBufferWriteString(buf, "version != NULL) - xmlBufferWriteQuotedString(buf->buffer, result->version); - else - xmlOutputBufferWriteString(buf, "\"1.0\""); - if (encoding == NULL) { - if (result->encoding != NULL) - encoding = result->encoding; - else if (result->charset != XML_CHAR_ENCODING_UTF8) - encoding = (const xmlChar *) - xmlGetCharEncodingName((xmlCharEncoding) - result->charset); - } - if (encoding != NULL) { - xmlOutputBufferWriteString(buf, " encoding="); - xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding); - } - switch (standalone) { - case 0: - xmlOutputBufferWriteString(buf, " standalone=\"no\""); - break; - case 1: - xmlOutputBufferWriteString(buf, " standalone=\"yes\""); - break; - default: - break; - } - xmlOutputBufferWriteString(buf, "?>\n"); - } - if (result->children != NULL) { - xmlNodePtr child = result->children; - - while (child != NULL) { - xmlNodeDumpOutput(buf, result, child, 0, (indent == 1), - (const char *) encoding); - if ((child->type == XML_DTD_NODE) || - ((child->type == XML_COMMENT_NODE) && - (child->next != NULL))) - xmlOutputBufferWriteString(buf, "\n"); - child = child->next; - } - xmlOutputBufferWriteString(buf, "\n"); - } - xmlOutputBufferFlush(buf); - } - return(buf->written - base); -} - -/** - * xsltSaveResultToFilename: - * @URL: a filename or URL - * @result: the result xmlDocPtr - * @style: the stylesheet - * @compression: the compression factor (0 - 9 included) - * - * Save the result @result obtained by applying the @style stylesheet - * to a file or @URL - * - * Returns the number of byte written or -1 in case of failure. - */ -int -xsltSaveResultToFilename(const char *URL, xmlDocPtr result, - xsltStylesheetPtr style, int compression) { - xmlOutputBufferPtr buf; - const xmlChar *encoding; - int ret; - - if ((URL == NULL) || (result == NULL) || (style == NULL)) - return(-1); - if (result->children == NULL) - return(0); - - XSLT_GET_IMPORT_PTR(encoding, style, encoding) - if (encoding != NULL) { - xmlCharEncodingHandlerPtr encoder; - - encoder = xmlFindCharEncodingHandler((char *)encoding); - if ((encoder != NULL) && - (xmlStrEqual((const xmlChar *)encoder->name, - (const xmlChar *) "UTF-8"))) - encoder = NULL; - buf = xmlOutputBufferCreateFilename(URL, encoder, compression); - } else { - buf = xmlOutputBufferCreateFilename(URL, NULL, compression); - } - if (buf == NULL) - return(-1); - xsltSaveResultTo(buf, result, style); - ret = xmlOutputBufferClose(buf); - return(ret); -} - -/** - * xsltSaveResultToFile: - * @file: a FILE * I/O - * @result: the result xmlDocPtr - * @style: the stylesheet - * - * Save the result @result obtained by applying the @style stylesheet - * to an open FILE * I/O. - * This does not close the FILE @file - * - * Returns the number of bytes written or -1 in case of failure. - */ -int -xsltSaveResultToFile(FILE *file, xmlDocPtr result, xsltStylesheetPtr style) { - xmlOutputBufferPtr buf; - const xmlChar *encoding; - int ret; - - if ((file == NULL) || (result == NULL) || (style == NULL)) - return(-1); - if (result->children == NULL) - return(0); - - XSLT_GET_IMPORT_PTR(encoding, style, encoding) - if (encoding != NULL) { - xmlCharEncodingHandlerPtr encoder; - - encoder = xmlFindCharEncodingHandler((char *)encoding); - if ((encoder != NULL) && - (xmlStrEqual((const xmlChar *)encoder->name, - (const xmlChar *) "UTF-8"))) - encoder = NULL; - buf = xmlOutputBufferCreateFile(file, encoder); - } else { - buf = xmlOutputBufferCreateFile(file, NULL); - } - - if (buf == NULL) - return(-1); - xsltSaveResultTo(buf, result, style); - ret = xmlOutputBufferClose(buf); - return(ret); -} - -/** - * xsltSaveResultToFd: - * @fd: a file descriptor - * @result: the result xmlDocPtr - * @style: the stylesheet - * - * Save the result @result obtained by applying the @style stylesheet - * to an open file descriptor - * This does not close the descriptor. - * - * Returns the number of bytes written or -1 in case of failure. - */ -int -xsltSaveResultToFd(int fd, xmlDocPtr result, xsltStylesheetPtr style) { - xmlOutputBufferPtr buf; - const xmlChar *encoding; - int ret; - - if ((fd < 0) || (result == NULL) || (style == NULL)) - return(-1); - if (result->children == NULL) - return(0); - - XSLT_GET_IMPORT_PTR(encoding, style, encoding) - if (encoding != NULL) { - xmlCharEncodingHandlerPtr encoder; - - encoder = xmlFindCharEncodingHandler((char *)encoding); - if ((encoder != NULL) && - (xmlStrEqual((const xmlChar *)encoder->name, - (const xmlChar *) "UTF-8"))) - encoder = NULL; - buf = xmlOutputBufferCreateFd(fd, encoder); - } else { - buf = xmlOutputBufferCreateFd(fd, NULL); - } - if (buf == NULL) - return(-1); - xsltSaveResultTo(buf, result, style); - ret = xmlOutputBufferClose(buf); - return(ret); -} - -/** - * xsltSaveResultToString: - * @doc_txt_ptr: Memory pointer for allocated XML text - * @doc_txt_len: Length of the generated XML text - * @result: the result xmlDocPtr - * @style: the stylesheet - * - * Save the result @result obtained by applying the @style stylesheet - * to a new allocated string. - * - * Returns 0 in case of success and -1 in case of error - */ -int -xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len, - xmlDocPtr result, xsltStylesheetPtr style) { - xmlOutputBufferPtr buf; - const xmlChar *encoding; - - *doc_txt_ptr = NULL; - *doc_txt_len = 0; - if (result->children == NULL) - return(0); - - XSLT_GET_IMPORT_PTR(encoding, style, encoding) - if (encoding != NULL) { - xmlCharEncodingHandlerPtr encoder; - - encoder = xmlFindCharEncodingHandler((char *)encoding); - if ((encoder != NULL) && - (xmlStrEqual((const xmlChar *)encoder->name, - (const xmlChar *) "UTF-8"))) - encoder = NULL; - buf = xmlAllocOutputBuffer(encoder); - } else { - buf = xmlAllocOutputBuffer(NULL); - } - if (buf == NULL) - return(-1); - xsltSaveResultTo(buf, result, style); - if (buf->conv != NULL) { - *doc_txt_len = buf->conv->use; - *doc_txt_ptr = xmlStrndup(buf->conv->content, *doc_txt_len); - } else { - *doc_txt_len = buf->buffer->use; - *doc_txt_ptr = xmlStrndup(buf->buffer->content, *doc_txt_len); - } - (void)xmlOutputBufferClose(buf); - return 0; -} - -/************************************************************************ - * * - * Generating profiling informations * - * * - ************************************************************************/ - -static long calibration = -1; - -/** - * xsltCalibrateTimestamps: - * - * Used for to calibrate the xsltTimestamp() function - * Should work if launched at startup and we don't loose our quantum :-) - * - * Returns the number of milliseconds used by xsltTimestamp() - */ -static long -xsltCalibrateTimestamps(void) { - register int i; - - for (i = 0;i < 999;i++) - xsltTimestamp(); - return(xsltTimestamp() / 1000); -} - -/** - * xsltCalibrateAdjust: - * @delta: a negative dealy value found - * - * Used for to correct the calibration for xsltTimestamp() - */ -void -xsltCalibrateAdjust(long delta) { - calibration += delta; -} - -/** - * xsltTimestamp: - * - * Used for gathering profiling data - * - * Returns the number of tenth of milliseconds since the beginning of the - * profiling - */ -long -xsltTimestamp(void) -{ -#ifdef XSLT_WIN32_PERFORMANCE_COUNTER - BOOL ok; - LARGE_INTEGER performanceCount; - LARGE_INTEGER performanceFrequency; - LONGLONG quadCount; - double seconds; - static LONGLONG startupQuadCount = 0; - static LONGLONG startupQuadFreq = 0; - - ok = QueryPerformanceCounter(&performanceCount); - if (!ok) - return 0; - quadCount = performanceCount.QuadPart; - if (calibration < 0) { - calibration = 0; - ok = QueryPerformanceFrequency(&performanceFrequency); - if (!ok) - return 0; - startupQuadFreq = performanceFrequency.QuadPart; - startupQuadCount = quadCount; - return (0); - } - if (startupQuadFreq == 0) - return 0; - seconds = (quadCount - startupQuadCount) / (double) startupQuadFreq; - return (long) (seconds * XSLT_TIMESTAMP_TICS_PER_SEC); - -#else /* XSLT_WIN32_PERFORMANCE_COUNTER */ -#ifdef HAVE_GETTIMEOFDAY - static struct timeval startup; - struct timeval cur; - long tics; - - if (calibration < 0) { - gettimeofday(&startup, NULL); - calibration = 0; - calibration = xsltCalibrateTimestamps(); - gettimeofday(&startup, NULL); - return (0); - } - - gettimeofday(&cur, NULL); - tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC; - tics += (cur.tv_usec - startup.tv_usec) / - (1000000l / XSLT_TIMESTAMP_TICS_PER_SEC); - - tics -= calibration; - return(tics); -#else - - /* Neither gettimeofday() nor Win32 performance counter available */ - - return (0); - -#endif /* HAVE_GETTIMEOFDAY */ -#endif /* XSLT_WIN32_PERFORMANCE_COUNTER */ -} - -#define MAX_TEMPLATES 10000 - -/** - * xsltSaveProfiling: - * @ctxt: an XSLT context - * @output: a FILE * for saving the informations - * - * Save the profiling informations on @output - */ -void -xsltSaveProfiling(xsltTransformContextPtr ctxt, FILE *output) { - int nb, i,j; - int max; - int total; - long totalt; - xsltTemplatePtr *templates; - xsltStylesheetPtr style; - xsltTemplatePtr template; - - if ((output == NULL) || (ctxt == NULL)) - return; - if (ctxt->profile == 0) - return; - - nb = 0; - max = MAX_TEMPLATES; - templates = xmlMalloc(max * sizeof(xsltTemplatePtr)); - if (templates == NULL) - return; - - style = ctxt->style; - while (style != NULL) { - template = style->templates; - while (template != NULL) { - if (nb >= max) - break; - - if (template->nbCalls > 0) - templates[nb++] = template; - template = template->next; - } - - style = xsltNextImport(style); - } - - for (i = 0;i < nb -1;i++) { - for (j = i + 1; j < nb; j++) { - if ((templates[i]->time <= templates[j]->time) || - ((templates[i]->time == templates[j]->time) && - (templates[i]->nbCalls <= templates[j]->nbCalls))) { - template = templates[j]; - templates[j] = templates[i]; - templates[i] = template; - } - } - } - - fprintf(output, "%6s%20s%20s%10s Calls Tot 100us Avg\n\n", - "number", "match", "name", "mode"); - total = 0; - totalt = 0; - for (i = 0;i < nb;i++) { - fprintf(output, "%5d ", i); - if (templates[i]->match != NULL) { - if (xmlStrlen(templates[i]->match) > 20) - fprintf(output, "%s\n%26s", templates[i]->match, ""); - else - fprintf(output, "%20s", templates[i]->match); - } else { - fprintf(output, "%20s", ""); - } - if (templates[i]->name != NULL) { - if (xmlStrlen(templates[i]->name) > 20) - fprintf(output, "%s\n%46s", templates[i]->name, ""); - else - fprintf(output, "%20s", templates[i]->name); - } else { - fprintf(output, "%20s", ""); - } - if (templates[i]->mode != NULL) { - if (xmlStrlen(templates[i]->mode) > 10) - fprintf(output, "%s\n%56s", templates[i]->mode, ""); - else - fprintf(output, "%10s", templates[i]->mode); - } else { - fprintf(output, "%10s", ""); - } - fprintf(output, " %6d", templates[i]->nbCalls); - fprintf(output, " %6ld %6ld\n", templates[i]->time, - templates[i]->time / templates[i]->nbCalls); - total += templates[i]->nbCalls; - totalt += templates[i]->time; - } - fprintf(output, "\n%30s%26s %6d %6ld\n", "Total", "", total, totalt); - - xmlFree(templates); -} - -/************************************************************************ - * * - * Fetching profiling informations * - * * - ************************************************************************/ - -/** - * xsltGetProfileInformation: - * @ctxt: a transformation context - * - * This function should be called after the transformation completed - * to extract template processing profiling informations if availble. - * The informations are returned as an XML document tree like - * - * - *