diff options
Diffstat (limited to 'libxslt/xslt.c')
-rw-r--r-- | libxslt/xslt.c | 6997 |
1 files changed, 6997 insertions, 0 deletions
diff --git a/libxslt/xslt.c b/libxslt/xslt.c new file mode 100644 index 0000000..bca91ee --- /dev/null +++ b/libxslt/xslt.c @@ -0,0 +1,6997 @@ +/* + * 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 <string.h> + +#include <libxml/xmlmemory.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/valid.h> +#include <libxml/hash.h> +#include <libxml/uri.h> +#include <libxml/xmlerror.h> +#include <libxml/parserInternals.h> +#include <libxml/xpathInternals.h> +#include <libxml/xpath.h> +#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; + +#define XSLT_ELEMENT_CATEGORY_XSLT 0 +#define XSLT_ELEMENT_CATEGORY_EXTENSION 1 +#define XSLT_ELEMENT_CATEGORY_LRE 2 + +/* +* 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 + +#ifdef XSLT_LOCALE_WINAPI +extern xmlRMutexPtr xsltLocaleMutex; +#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 -1 if already present or + * in case of error + */ +static int +exclPrefixPush(xsltStylesheetPtr style, xmlChar * value) +{ + int i; + + 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 (-1); + } + } + /* do not push duplicates */ + for (i = 0;i < style->exclPrefixNr;i++) { + if (xmlStrEqual(style->exclPrefixTab[i], value)) + return(-1); + } + 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 (-1); + } + } + 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 * + * * + ************************************************************************/ + +static int initialized = 0; +/** + * xsltInit: + * + * Initializes the processor (e.g. registers built-in extensions, + * etc.) + */ +void +xsltInit (void) { + if (initialized == 0) { + initialized = 1; +#ifdef XSLT_LOCALE_WINAPI + xsltLocaleMutex = xmlNewRMutex(); +#endif + xsltRegisterAllExtras(); + } +} + +/** + * xsltUninit: + * + * Uninitializes the processor. + */ +void +xsltUninit (void) { +#ifdef XSLT_LOCALE_WINAPI + xmlFreeRMutex(xsltLocaleMutex); + xsltLocaleMutex = NULL; +#endif + 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); + + /* free profiling data */ + if (template->templCalledTab) xmlFree(template->templCalledTab); + if (template->templCountTab) xmlFree(template->templCountTab); + + 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->forwards_compatible = 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 embedded stylesheets. + * + * Returns 0 if OK, -1 on API or internal errors. + */ +static int +xsltCleanupStylesheetTree(xmlDocPtr doc ATTRIBUTE_UNUSED, + xmlNodePtr rootElem ATTRIBUTE_UNUSED) +{ +#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) || (cur->type != XML_ELEMENT_NODE)) + 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 + * + * <!-- Category: top-level-element --> + * <xsl:decimal-format + * name = qname, decimal-separator = char, grouping-separator = char, + * infinity = string, minus-sign = char, NaN = string, percent = char + * per-mille = char, zero-digit = char, digit = char, + * pattern-separator = char /> + * + * 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) || (cur->type != XML_ELEMENT_NODE)) + 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) || (cur->type != XML_ELEMENT_NODE)) + 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) || (cur->type != XML_ELEMENT_NODE)) + 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) || (cur->type != XML_ELEMENT_NODE)) + 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) || (cur->type != XML_ELEMENT_NODE)) + 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 { + if (exclPrefixPush(style, (xmlChar *) ns->href) >= 0) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "exclude result prefix %s\n", prefix); +#endif + 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. +*/ +static 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--; + /* + * Pop information of variables. + */ + if ((cctxt->ivar) && (cctxt->ivar->depth > cctxt->depth)) + xsltCompilerVarInfoPop(cctxt); + + 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: : <foo> <!-- bar -->zoo</foo> + * Corrent (strict) result: <foo> zoo</foo> + * Incorrect (old) result : <foo>zoo</foo> + * + * NOTE that we *will* merge adjacent text-nodes if + * they are in xsl:text. + * Example, the following: + * <xsl:text> <!-- bar -->zoo<xsl:text> + * will result in both cases in: + * <xsl:text> zoo<xsl:text> + */ + 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, styleelem; + 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; + + if ((cur != NULL) && (IS_XSLT_ELEM(cur)) && + (IS_XSLT_NAME(cur, "stylesheet"))) { + styleelem = cur; + } else { + styleelem = NULL; + } + + /* + * 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->parent) != 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. In case of a namespaced element children of + * the stylesheet and not in the XSLT namespace and not an extension + * element, ignore its content. + */ + if ((cur->type == XML_ELEMENT_NODE) && (cur->ns != NULL) && + (styleelem != NULL) && (cur->parent == styleelem) && + (!xmlStrEqual(cur->ns->href, XSLT_NAMESPACE)) && + (!xsltCheckExtURI(style, cur->ns->href))) { + goto skip_children; + } else 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: + /* <!-- Content: (xsl:sort | xsl:with-param)* --> */ + goto apply_templates; + case XSLT_FUNC_ATTRIBUTE: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_CALLTEMPLATE: + /* <!-- Content: xsl:with-param* --> */ + goto call_template; + case XSLT_FUNC_CHOOSE: + /* <!-- Content: (xsl:when+, xsl:otherwise?) --> */ + goto choose; + case XSLT_FUNC_COMMENT: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_COPY: + /* <!-- Content: template --> */ + 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: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_FALLBACK: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_FOREACH: + /* <!-- Content: (xsl:sort*, template) --> */ + goto for_each; + case XSLT_FUNC_IF: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_OTHERWISE: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_MESSAGE: + /* <!-- Content: template --> */ + 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); + } + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_PI: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_SORT: + /* EMPTY */ + goto empty_content; + case XSLT_FUNC_TEXT: + /* <!-- Content: #PCDATA --> */ + 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); + } + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_WHEN: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_WITHPARAM: + /* <!-- Content: template --> */ + 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: + /* <!-- Content: (xsl:sort | xsl:with-param)* --> */ + 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: + /* <!-- Content: xsl:with-param* --> */ + 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: + /* <!-- Content: (xsl:when+, xsl:otherwise?) --> */ + /* + * 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: + /* <!-- Content: (xsl:sort*, template) --> */ + /* + * 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) || (node->type != XML_ELEMENT_NODE)) + 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) || (cur->type == XML_NAMESPACE_DECL)) + return; + /* + * This is the content reffered to as a "template". + * E.g. an xsl:element has such content model: + * <xsl:element + * name = { qname } + * namespace = { uri-reference } + * use-attribute-sets = qnames> + * <!-- Content: template --> + * + * 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) || + (templ->type == XML_NAMESPACE_DECL)) + 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; + + if ((style == NULL) || (templ == NULL) || + (templ->type == XML_NAMESPACE_DECL)) return; + + /* + * 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 if (cur->type == XML_ELEMENT_NODE) + { + /* + * 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->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 + * + * <!-- Category: top-level-element --> + * <xsl:key name = qname, match = pattern, use = expression /> + * + * 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) || (key->type != XML_ELEMENT_NODE)) + 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) || + (templNode->type != XML_ELEMENT_NODE)) + 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: + * <xsl:template ...xml:space="preserve"> + * <xsl:param name="foo"/> + * <xsl:param name="bar"/> + * <xsl:param name="zoo"/> + * ... 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 ((style == NULL) || (template == NULL) || + (template->type != XML_ELEMENT_NODE)) + 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; + + /* + * 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; + } + } + + /* + * 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) || (node->type != XML_ELEMENT_NODE)) + 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. + * + * <xsl:stylesheet + * id = id + * extension-element-prefixes = tokens + * exclude-result-prefixes = tokens + * version = number> + * <!-- Content: (xsl:import*, top-level-elements) --> + * </xsl:stylesheet> + * + * 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) || (node->type != XML_ELEMENT_NODE)) + 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) || (top->type != XML_ELEMENT_NODE)) + 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"); + if (style != NULL) { + style->forwards_compatible = 1; + style->warnings++; + } + } + xmlFree(prop); + } + + /* + * process xsl:import elements + */ + cur = top->children; + 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 { + if ((style != NULL) && (style->forwards_compatible == 0)) { + 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 +/** + * xsltRestoreDocumentNamespaces: + * @ns: map of namespaces + * @doc: the document + * + * Restore the namespaces for the document + * + * Returns 0 in case of success, -1 in case of failure + */ +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; + + xsltInitGlobals(); + + 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 */ + +/** + * xsltParseStylesheetProcess: + * @ret: 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 ret, xmlDocPtr doc) { + xmlNodePtr cur; + + xsltInitGlobals(); + + 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 + * @parentStyle: 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; + if (xsltParseStylesheetProcess(retStyle, doc) == NULL) { + retStyle->doc = NULL; + xsltFreeStylesheet(retStyle); + retStyle = NULL; + } + 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. doc + * is kept as a reference within the returned stylesheet, so changes + * to doc after the parsing will be reflected when the stylesheet + * is applied, and the doc is automatically freed when the + * stylesheet is closed. + * + * Returns a new XSLT stylesheet structure. + */ + +xsltStylesheetPtr +xsltParseStylesheetDoc(xmlDocPtr doc) { + xsltStylesheetPtr ret; + + xsltInitGlobals(); + + 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; + + xsltInitGlobals(); + + 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; + + xsltInitGlobals(); + + 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, newtree; + xmlNsPtr ns; + +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "creating new document from %s for embedded stylesheet\n", + doc->URL); +#endif + /* + * move the subtree in a new document passed to + * the stylesheet analyzer + */ + subtree = ID->parent; + fake = xmlNewDoc(NULL); + if (fake != NULL) { + /* + * Should the dictionary still be shared even though + * the nodes are being copied rather than moved? + */ + fake->dict = doc->dict; + xmlDictReference(doc->dict); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing dictionary from %s for embedded stylesheet\n", + doc->URL); +#endif + + newtree = xmlDocCopyNode(subtree, fake, 1); + + fake->URL = xmlNodeGetBase(doc, subtree->parent); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "set base URI for embedded stylesheet as %s\n", + fake->URL); +#endif + + /* + * Add all namespaces in scope of embedded stylesheet to + * root element of newly created stylesheet document + */ + while ((subtree = subtree->parent) != (xmlNodePtr)doc) { + for (ns = subtree->ns; ns; ns = ns->next) { + xmlNewNs(newtree, ns->href, ns->prefix); + } + } + + xmlAddChild((xmlNodePtr)fake, newtree); + 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); +} |