blob: 55f505b4768b20e87e2cc60b46e70c40ff2f6e57 [file] [log] [blame]
/*
* 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) {
initialized = 0;
}
/**
* xsltIsBlank:
* @str: a string
*
* Check if a string is ignorable
*
* Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
*/
int
xsltIsBlank(xmlChar *str) {
if (str == NULL)
return(1);
while (*str != 0) {
if (!(IS_BLANK(*str))) return(0);
str++;
}
return(1);
}
/************************************************************************
* *
* Routines to handle XSLT data structures *
* *
************************************************************************/
static xsltDecimalFormatPtr
xsltNewDecimalFormat(xmlChar *name)
{
xsltDecimalFormatPtr self;
/* UTF-8 for 0x2030 */
static const xmlChar permille[4] = {0xe2, 0x80, 0xb0, 0};
self = xmlMalloc(sizeof(xsltDecimalFormat));
if (self != NULL) {
self->next = NULL;
self->name = name;
/* Default values */
self->digit = xmlStrdup(BAD_CAST("#"));
self->patternSeparator = xmlStrdup(BAD_CAST(";"));
self->decimalPoint = xmlStrdup(BAD_CAST("."));
self->grouping = xmlStrdup(BAD_CAST(","));
self->percent = xmlStrdup(BAD_CAST("%"));
self->permille = xmlStrdup(BAD_CAST(permille));
self->zeroDigit = xmlStrdup(BAD_CAST("0"));
self->minusSign = xmlStrdup(BAD_CAST("-"));
self->infinity = xmlStrdup(BAD_CAST("Infinity"));
self->noNumber = xmlStrdup(BAD_CAST("NaN"));
}
return self;
}
static void
xsltFreeDecimalFormat(xsltDecimalFormatPtr self)
{
if (self != NULL) {
if (self->digit)
xmlFree(self->digit);
if (self->patternSeparator)
xmlFree(self->patternSeparator);
if (self->decimalPoint)
xmlFree(self->decimalPoint);
if (self->grouping)
xmlFree(self->grouping);
if (self->percent)
xmlFree(self->percent);
if (self->permille)
xmlFree(self->permille);
if (self->zeroDigit)
xmlFree(self->zeroDigit);
if (self->minusSign)
xmlFree(self->minusSign);
if (self->infinity)
xmlFree(self->infinity);
if (self->noNumber)
xmlFree(self->noNumber);
if (self->name)
xmlFree(self->name);
xmlFree(self);
}
}
static void
xsltFreeDecimalFormatList(xsltStylesheetPtr self)
{
xsltDecimalFormatPtr iter;
xsltDecimalFormatPtr tmp;
if (self == NULL)
return;
iter = self->decimalFormat;
while (iter != NULL) {
tmp = iter->next;
xsltFreeDecimalFormat(iter);
iter = tmp;
}
}
/**
* xsltDecimalFormatGetByName:
* @style: the XSLT stylesheet
* @name: the decimal-format name to find
*
* Find decimal-format by name
*
* Returns the xsltDecimalFormatPtr
*/
xsltDecimalFormatPtr
xsltDecimalFormatGetByName(xsltStylesheetPtr style, xmlChar *name)
{
xsltDecimalFormatPtr result = NULL;
if (name == NULL)
return style->decimalFormat;
while (style != NULL) {
for (result = style->decimalFormat->next;
result != NULL;
result = result->next) {
if (xmlStrEqual(name, result->name))
return result;
}
style = xsltNextImport(style);
}
return result;
}
/**
* xsltNewTemplate:
*
* Create a new XSLT Template
*
* Returns the newly allocated xsltTemplatePtr or NULL in case of error
*/
static xsltTemplatePtr
xsltNewTemplate(void) {
xsltTemplatePtr cur;
cur = (xsltTemplatePtr) xmlMalloc(sizeof(xsltTemplate));
if (cur == NULL) {
xsltTransformError(NULL, NULL, NULL,
"xsltNewTemplate : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xsltTemplate));
cur->priority = XSLT_PAT_NO_PRIORITY;
return(cur);
}
/**
* xsltFreeTemplate:
* @template: an XSLT template
*
* Free up the memory allocated by @template
*/
static void
xsltFreeTemplate(xsltTemplatePtr template) {
if (template == NULL)
return;
if (template->match) xmlFree(template->match);
/*
* NOTE: @name and @nameURI are put into the string dict now.
* if (template->name) xmlFree(template->name);
* if (template->nameURI) xmlFree(template->nameURI);
*/
/*
if (template->mode) xmlFree(template->mode);
if (template->modeURI) xmlFree(template->modeURI);
*/
if (template->inheritedNs) xmlFree(template->inheritedNs);
memset(template, -1, sizeof(xsltTemplate));
xmlFree(template);
}
/**
* xsltFreeTemplateList:
* @template: an XSLT template list
*
* Free up the memory allocated by all the elements of @template
*/
static void
xsltFreeTemplateList(xsltTemplatePtr template) {
xsltTemplatePtr cur;
while (template != NULL) {
cur = template;
template = template->next;
xsltFreeTemplate(cur);
}
}
#ifdef XSLT_REFACTORED
static void
xsltFreeNsAliasList(xsltNsAliasPtr item)
{
xsltNsAliasPtr tmp;
while (item) {
tmp = item;
item = item->next;
xmlFree(tmp);
}
return;
}
#ifdef XSLT_REFACTORED_XSLT_NSCOMP
static void
xsltFreeNamespaceMap(xsltNsMapPtr item)
{
xsltNsMapPtr tmp;
while (item) {
tmp = item;
item = item->next;
xmlFree(tmp);
}
return;
}
static xsltNsMapPtr
xsltNewNamespaceMapItem(xsltCompilerCtxtPtr cctxt,
xmlDocPtr doc,
xmlNsPtr ns,
xmlNodePtr elem)
{
xsltNsMapPtr ret;
if ((cctxt == NULL) || (doc == NULL) || (ns == NULL))
return(NULL);
ret = (xsltNsMapPtr) xmlMalloc(sizeof(xsltNsMap));
if (ret == NULL) {
xsltTransformError(NULL, cctxt->style, elem,
"Internal error: (xsltNewNamespaceMapItem) "
"memory allocation failed.\n");
return(NULL);
}
memset(ret, 0, sizeof(xsltNsMap));
ret->doc = doc;
ret->ns = ns;
ret->origNsName = ns->href;
/*
* Store the item at current stylesheet-level.
*/
if (cctxt->psData->nsMap != NULL)
ret->next = cctxt->psData->nsMap;
cctxt->psData->nsMap = ret;
return(ret);
}
#endif /* XSLT_REFACTORED_XSLT_NSCOMP */
/**
* xsltCompilerVarInfoFree:
* @cctxt: the compilation context
*
* Frees the list of information for vars/params.
*/
static void
xsltCompilerVarInfoFree(xsltCompilerCtxtPtr cctxt)
{
xsltVarInfoPtr ivar = cctxt->ivars, ivartmp;
while (ivar) {
ivartmp = ivar;
ivar = ivar->next;
xmlFree(ivartmp);
}
}
/**
* xsltCompilerCtxtFree:
*
* Free an XSLT compiler context.
*/
static void
xsltCompilationCtxtFree(xsltCompilerCtxtPtr cctxt)
{
if (cctxt == NULL)
return;
#ifdef WITH_XSLT_DEBUG_PARSING
xsltGenericDebug(xsltGenericDebugContext,
"Freeing compilation context\n");
xsltGenericDebug(xsltGenericDebugContext,
"### Max inodes: %d\n", cctxt->maxNodeInfos);
xsltGenericDebug(xsltGenericDebugContext,
"### Max LREs : %d\n", cctxt->maxLREs);
#endif
/*
* Free node-infos.
*/
if (cctxt->inodeList != NULL) {
xsltCompilerNodeInfoPtr tmp, cur = cctxt->inodeList;
while (cur != NULL) {
tmp = cur;
cur = cur->next;
xmlFree(tmp);
}
}
if (cctxt->tmpList != NULL)
xsltPointerListFree(cctxt->tmpList);
#ifdef XSLT_REFACTORED_XPATHCOMP
if (cctxt->xpathCtxt != NULL)
xmlXPathFreeContext(cctxt->xpathCtxt);
#endif
if (cctxt->nsAliases != NULL)
xsltFreeNsAliasList(cctxt->nsAliases);
if (cctxt->ivars)
xsltCompilerVarInfoFree(cctxt);
xmlFree(cctxt);
}
/**
* xsltCompilerCreate:
*
* Creates an XSLT compiler context.
*
* Returns the pointer to the created xsltCompilerCtxt or
* NULL in case of an internal error.
*/
static xsltCompilerCtxtPtr
xsltCompilationCtxtCreate(xsltStylesheetPtr style) {
xsltCompilerCtxtPtr ret;
ret = (xsltCompilerCtxtPtr) xmlMalloc(sizeof(xsltCompilerCtxt));
if (ret == NULL) {
xsltTransformError(NULL, style, NULL,
"xsltCompilerCreate: allocation of compiler "
"context failed.\n");
return(NULL);
}
memset(ret, 0, sizeof(xsltCompilerCtxt));
ret->errSeverity = XSLT_ERROR_SEVERITY_ERROR;
ret->tmpList = xsltPointerListCreate(20);
if (ret->tmpList == NULL) {
goto internal_err;
}
#ifdef XSLT_REFACTORED_XPATHCOMP
/*
* Create the XPath compilation context in order
* to speed up precompilation of XPath expressions.
*/
ret->xpathCtxt = xmlXPathNewContext(NULL);
if (ret->xpathCtxt == NULL)
goto internal_err;
#endif
return(ret);
internal_err:
xsltCompilationCtxtFree(ret);
return(NULL);
}
static void
xsltLREEffectiveNsNodesFree(xsltEffectiveNsPtr first)
{
xsltEffectiveNsPtr tmp;
while (first != NULL) {
tmp = first;
first = first->nextInStore;
xmlFree(tmp);
}
}
static void
xsltFreePrincipalStylesheetData(xsltPrincipalStylesheetDataPtr data)
{
if (data == NULL)
return;
if (data->inScopeNamespaces != NULL) {
int i;
xsltNsListContainerPtr nsi;
xsltPointerListPtr list =
(xsltPointerListPtr) data->inScopeNamespaces;
for (i = 0; i < list->number; i++) {
/*
* REVISIT TODO: Free info of in-scope namespaces.
*/
nsi = (xsltNsListContainerPtr) list->items[i];
if (nsi->list != NULL)
xmlFree(nsi->list);
xmlFree(nsi);
}
xsltPointerListFree(list);
data->inScopeNamespaces = NULL;
}
if (data->exclResultNamespaces != NULL) {
int i;
xsltPointerListPtr list = (xsltPointerListPtr)
data->exclResultNamespaces;
for (i = 0; i < list->number; i++)
xsltPointerListFree((xsltPointerListPtr) list->items[i]);
xsltPointerListFree(list);
data->exclResultNamespaces = NULL;
}
if (data->extElemNamespaces != NULL) {
xsltPointerListPtr list = (xsltPointerListPtr)
data->extElemNamespaces;
int i;
for (i = 0; i < list->number; i++)
xsltPointerListFree((xsltPointerListPtr) list->items[i]);
xsltPointerListFree(list);
data->extElemNamespaces = NULL;
}
if (data->effectiveNs) {
xsltLREEffectiveNsNodesFree(data->effectiveNs);
data->effectiveNs = NULL;
}
#ifdef XSLT_REFACTORED_XSLT_NSCOMP
xsltFreeNamespaceMap(data->nsMap);
#endif
xmlFree(data);
}
static xsltPrincipalStylesheetDataPtr
xsltNewPrincipalStylesheetData(void)
{
xsltPrincipalStylesheetDataPtr ret;
ret = (xsltPrincipalStylesheetDataPtr)
xmlMalloc(sizeof(xsltPrincipalStylesheetData));
if (ret == NULL) {
xsltTransformError(NULL, NULL, NULL,
"xsltNewPrincipalStylesheetData: memory allocation failed.\n");
return(NULL);
}
memset(ret, 0, sizeof(xsltPrincipalStylesheetData));
/*
* Global list of in-scope namespaces.
*/
ret->inScopeNamespaces = xsltPointerListCreate(-1);
if (ret->inScopeNamespaces == NULL)
goto internal_err;
/*
* Global list of excluded result ns-decls.
*/
ret->exclResultNamespaces = xsltPointerListCreate(-1);
if (ret->exclResultNamespaces == NULL)
goto internal_err;
/*
* Global list of extension instruction namespace names.
*/
ret->extElemNamespaces = xsltPointerListCreate(-1);
if (ret->extElemNamespaces == NULL)
goto internal_err;
return(ret);
internal_err:
return(NULL);
}
#endif
/**
* xsltNewStylesheet:
*
* Create a new XSLT Stylesheet
*
* Returns the newly allocated xsltStylesheetPtr or NULL in case of error
*/
xsltStylesheetPtr
xsltNewStylesheet(void) {
xsltStylesheetPtr ret = NULL;
ret = (xsltStylesheetPtr) xmlMalloc(sizeof(xsltStylesheet));
if (ret == NULL) {
xsltTransformError(NULL, NULL, NULL,
"xsltNewStylesheet : malloc failed\n");
goto internal_err;
}
memset(ret, 0, sizeof(xsltStylesheet));
ret->omitXmlDeclaration = -1;
ret->standalone = -1;
ret->decimalFormat = xsltNewDecimalFormat(NULL);
ret->indent = -1;
ret->errors = 0;
ret->warnings = 0;
ret->exclPrefixNr = 0;
ret->exclPrefixMax = 0;
ret->exclPrefixTab = NULL;
ret->extInfos = NULL;
ret->extrasNr = 0;
ret->internalized = 1;
ret->literal_result = 0;
ret->dict = xmlDictCreate();
#ifdef WITH_XSLT_DEBUG
xsltGenericDebug(xsltGenericDebugContext,
"creating dictionary for stylesheet\n");
#endif
xsltInit();
return(ret);
internal_err:
if (ret != NULL)
xsltFreeStylesheet(ret);
return(NULL);
}
/**
* xsltAllocateExtra:
* @style: an XSLT stylesheet
*
* Allocate an extra runtime information slot statically while compiling
* the stylesheet and return its number
*
* Returns the number of the slot
*/
int
xsltAllocateExtra(xsltStylesheetPtr style)
{
return(style->extrasNr++);
}
/**
* xsltAllocateExtraCtxt:
* @ctxt: an XSLT transformation context
*
* Allocate an extra runtime information slot at run-time
* and return its number
* This make sure there is a slot ready in the transformation context
*
* Returns the number of the slot
*/
int
xsltAllocateExtraCtxt(xsltTransformContextPtr ctxt)
{
if (ctxt->extrasNr >= ctxt->extrasMax) {
int i;
if (ctxt->extrasNr == 0) {
ctxt->extrasMax = 20;
ctxt->extras = (xsltRuntimeExtraPtr)
xmlMalloc(ctxt->extrasMax * sizeof(xsltRuntimeExtra));
if (ctxt->extras == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xsltAllocateExtraCtxt: out of memory\n");
ctxt->state = XSLT_STATE_ERROR;
return(0);
}
for (i = 0;i < ctxt->extrasMax;i++) {
ctxt->extras[i].info = NULL;
ctxt->extras[i].deallocate = NULL;
ctxt->extras[i].val.ptr = NULL;
}
} else {
xsltRuntimeExtraPtr tmp;
ctxt->extrasMax += 100;
tmp = (xsltRuntimeExtraPtr) xmlRealloc(ctxt->extras,
ctxt->extrasMax * sizeof(xsltRuntimeExtra));
if (tmp == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xsltAllocateExtraCtxt: out of memory\n");
ctxt->state = XSLT_STATE_ERROR;
return(0);
}
ctxt->extras = tmp;
for (i = ctxt->extrasNr;i < ctxt->extrasMax;i++) {
ctxt->extras[i].info = NULL;
ctxt->extras[i].deallocate = NULL;
ctxt->extras[i].val.ptr = NULL;
}
}
}
return(ctxt->extrasNr++);
}
/**
* xsltFreeStylesheetList:
* @style: an XSLT stylesheet list
*
* Free up the memory allocated by the list @style
*/
static void
xsltFreeStylesheetList(xsltStylesheetPtr style) {
xsltStylesheetPtr next;
while (style != NULL) {
next = style->next;
xsltFreeStylesheet(style);
style = next;
}
}
/**
* xsltCleanupStylesheetTree:
*
* @doc: the document-node
* @node: the element where the stylesheet is rooted at
*
* Actually @node need not be the document-element, but
* currently Libxslt does not support 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))
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))
return;
format = style->decimalFormat;
prop = xmlGetNsProp(cur, BAD_CAST("name"), NULL);
if (prop != NULL) {
format = xsltDecimalFormatGetByName(style, prop);
if (format != NULL) {
xsltTransformError(NULL, style, cur,
"xsltParseStylestyleDecimalFormat: %s already exists\n", prop);
if (style != NULL) style->warnings++;
return;
}
format = xsltNewDecimalFormat(prop);
if (format == NULL) {
xsltTransformError(NULL, style, cur,
"xsltParseStylestyleDecimalFormat: failed creating new decimal-format\n");
if (style != NULL) style->errors++;
return;
}
/* Append new decimal-format structure */
for (iter = style->decimalFormat; iter->next; iter = iter->next)
;
if (iter)
iter->next = format;
}
prop = xmlGetNsProp(cur, (const xmlChar *)"decimal-separator", NULL);
if (prop != NULL) {
if (format->decimalPoint != NULL) xmlFree(format->decimalPoint);
format->decimalPoint = prop;
}
prop = xmlGetNsProp(cur, (const xmlChar *)"grouping-separator", NULL);
if (prop != NULL) {
if (format->grouping != NULL) xmlFree(format->grouping);
format->grouping = prop;
}
prop = xmlGetNsProp(cur, (const xmlChar *)"infinity", NULL);
if (prop != NULL) {
if (format->infinity != NULL) xmlFree(format->infinity);
format->infinity = prop;
}
prop = xmlGetNsProp(cur, (const xmlChar *)"minus-sign", NULL);
if (prop != NULL) {
if (format->minusSign != NULL) xmlFree(format->minusSign);
format->minusSign = prop;
}
prop = xmlGetNsProp(cur, (const xmlChar *)"NaN", NULL);
if (prop != NULL) {
if (format->noNumber != NULL) xmlFree(format->noNumber);
format->noNumber = prop;
}
prop = xmlGetNsProp(cur, (const xmlChar *)"percent", NULL);
if (prop != NULL) {
if (format->percent != NULL) xmlFree(format->percent);
format->percent = prop;
}
prop = xmlGetNsProp(cur, (const xmlChar *)"per-mille", NULL);
if (prop != NULL) {
if (format->permille != NULL) xmlFree(format->permille);
format->permille = prop;
}
prop = xmlGetNsProp(cur, (const xmlChar *)"zero-digit", NULL);
if (prop != NULL) {
if (format->zeroDigit != NULL) xmlFree(format->zeroDigit);
format->zeroDigit = prop;
}
prop = xmlGetNsProp(cur, (const xmlChar *)"digit", NULL);
if (prop != NULL) {
if (format->digit != NULL) xmlFree(format->digit);
format->digit = prop;
}
prop = xmlGetNsProp(cur, (const xmlChar *)"pattern-separator", NULL);
if (prop != NULL) {
if (format->patternSeparator != NULL) xmlFree(format->patternSeparator);
format->patternSeparator = prop;
}
if (cur->children != NULL) {
xsltParseContentError(style, cur->children);
}
}
/**
* xsltParseStylesheetPreserveSpace:
* @style: the XSLT stylesheet
* @cur: the "preserve-space" element
*
* parse an XSLT stylesheet preserve-space element and record
* elements needing preserving
*/
static void
xsltParseStylesheetPreserveSpace(xsltStylesheetPtr style, xmlNodePtr cur) {
xmlChar *elements;
xmlChar *element, *end;
if ((cur == NULL) || (style == NULL))
return;
elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL);
if (elements == NULL) {
xsltTransformError(NULL, style, cur,
"xsltParseStylesheetPreserveSpace: missing elements attribute\n");
if (style != NULL) style->warnings++;
return;
}
if (style->stripSpaces == NULL)
style->stripSpaces = xmlHashCreate(10);
if (style->stripSpaces == NULL)
return;
element = elements;
while (*element != 0) {
while (IS_BLANK(*element)) element++;
if (*element == 0)
break;
end = element;
while ((*end != 0) && (!IS_BLANK(*end))) end++;
element = xmlStrndup(element, end - element);
if (element) {
#ifdef WITH_XSLT_DEBUG_PARSING
xsltGenericDebug(xsltGenericDebugContext,
"add preserved space element %s\n", element);
#endif
if (xmlStrEqual(element, (const xmlChar *)"*")) {
style->stripAll = -1;
} else {
const xmlChar *URI;
/*
* TODO: Don't use xsltGetQNameURI().
*/
URI = xsltGetQNameURI(cur, &element);
xmlHashAddEntry2(style->stripSpaces, element, URI,
(xmlChar *) "preserve");
}
xmlFree(element);
}
element = end;
}
xmlFree(elements);
if (cur->children != NULL) {
xsltParseContentError(style, cur->children);
}
}
#ifdef XSLT_REFACTORED
#else
/**
* xsltParseStylesheetExtPrefix:
* @style: the XSLT stylesheet
* @template: the "extension-element-prefixes" prefix
*
* parse an XSLT stylesheet's "extension-element-prefix" attribute value
* and register the namespaces of extension instruction.
* SPEC "A namespace is designated as an extension namespace by using
* an extension-element-prefixes attribute on:
* 1) an xsl:stylesheet element
* 2) an xsl:extension-element-prefixes attribute on a
* literal result element
* 3) an extension instruction."
*/
static void
xsltParseStylesheetExtPrefix(xsltStylesheetPtr style, xmlNodePtr cur,
int isXsltElem) {
xmlChar *prefixes;
xmlChar *prefix, *end;
if ((cur == NULL) || (style == NULL))
return;
if (isXsltElem) {
/* For xsl:stylesheet/xsl:transform. */
prefixes = xmlGetNsProp(cur,
(const xmlChar *)"extension-element-prefixes", NULL);
} else {
/* For literal result elements and extension instructions. */
prefixes = xmlGetNsProp(cur,
(const xmlChar *)"extension-element-prefixes", XSLT_NAMESPACE);
}
if (prefixes == NULL) {
return;
}
prefix = prefixes;
while (*prefix != 0) {
while (IS_BLANK(*prefix)) prefix++;
if (*prefix == 0)
break;
end = prefix;
while ((*end != 0) && (!IS_BLANK(*end))) end++;
prefix = xmlStrndup(prefix, end - prefix);
if (prefix) {
xmlNsPtr ns;
if (xmlStrEqual(prefix, (const xmlChar *)"#default"))
ns = xmlSearchNs(style->doc, cur, NULL);
else
ns = xmlSearchNs(style->doc, cur, prefix);
if (ns == NULL) {
xsltTransformError(NULL, style, cur,
"xsl:extension-element-prefix : undefined namespace %s\n",
prefix);
if (style != NULL) style->warnings++;
} else {
#ifdef WITH_XSLT_DEBUG_PARSING
xsltGenericDebug(xsltGenericDebugContext,
"add extension prefix %s\n", prefix);
#endif
xsltRegisterExtPrefix(style, prefix, ns->href);
}
xmlFree(prefix);
}
prefix = end;
}
xmlFree(prefixes);
}
#endif /* else of XSLT_REFACTORED */
/**
* xsltParseStylesheetStripSpace:
* @style: the XSLT stylesheet
* @cur: the "strip-space" element
*
* parse an XSLT stylesheet's strip-space element and record
* the elements needing stripping
*/
static void
xsltParseStylesheetStripSpace(xsltStylesheetPtr style, xmlNodePtr cur) {
xmlChar *elements;
xmlChar *element, *end;
if ((cur == NULL) || (style == NULL))
return;
elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL);
if (elements == NULL) {
xsltTransformError(NULL, style, cur,
"xsltParseStylesheetStripSpace: missing elements attribute\n");
if (style != NULL) style->warnings++;
return;
}
if (style->stripSpaces == NULL)
style->stripSpaces = xmlHashCreate(10);
if (style->stripSpaces == NULL)
return;
element = elements;
while (*element != 0) {
while (IS_BLANK(*element)) element++;
if (*element == 0)
break;
end = element;
while ((*end != 0) && (!IS_BLANK(*end))) end++;
element = xmlStrndup(element, end - element);
if (element) {
#ifdef WITH_XSLT_DEBUG_PARSING
xsltGenericDebug(xsltGenericDebugContext,
"add stripped space element %s\n", element);
#endif
if (xmlStrEqual(element, (const xmlChar *)"*")) {
style->stripAll = 1;
} else {
const xmlChar *URI;
/*
* TODO: Don't use xsltGetQNameURI().
*/
URI = xsltGetQNameURI(cur, &element);
xmlHashAddEntry2(style->stripSpaces, element, URI,
(xmlChar *) "strip");
}
xmlFree(element);
}
element = end;
}
xmlFree(elements);
if (cur->children != NULL) {
xsltParseContentError(style, cur->children);
}
}
#ifdef XSLT_REFACTORED
#else
/**
* xsltParseStylesheetExcludePrefix:
* @style: the XSLT stylesheet
* @cur: the current point in the stylesheet
*
* parse an XSLT stylesheet exclude prefix and record
* namespaces needing stripping
*
* Returns the number of Excluded prefixes added at that level
*/
static int
xsltParseStylesheetExcludePrefix(xsltStylesheetPtr style, xmlNodePtr cur,
int isXsltElem)
{
int nb = 0;
xmlChar *prefixes;
xmlChar *prefix, *end;
if ((cur == NULL) || (style == NULL))
return(0);
if (isXsltElem)
prefixes = xmlGetNsProp(cur,
(const xmlChar *)"exclude-result-prefixes", NULL);
else
prefixes = xmlGetNsProp(cur,
(const xmlChar *)"exclude-result-prefixes", XSLT_NAMESPACE);
if (prefixes == NULL) {
return(0);
}
prefix = prefixes;
while (*prefix != 0) {
while (IS_BLANK(*prefix)) prefix++;
if (*prefix == 0)
break;
end = prefix;
while ((*end != 0) && (!IS_BLANK(*end))) end++;
prefix = xmlStrndup(prefix, end - prefix);
if (prefix) {
xmlNsPtr ns;
if (xmlStrEqual(prefix, (const xmlChar *)"#default"))
ns = xmlSearchNs(style->doc, cur, NULL);
else
ns = xmlSearchNs(style->doc, cur, prefix);
if (ns == NULL) {
xsltTransformError(NULL, style, cur,
"xsl:exclude-result-prefixes : undefined namespace %s\n",
prefix);
if (style != NULL) style->warnings++;
} else {
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;
}
/*
* Pop information of variables.
*/
if ((cctxt->ivar) && (cctxt->ivar->depth > cctxt->depth))
xsltCompilerVarInfoPop(cctxt);
cctxt->depth--;
cctxt->inode = cctxt->inode->prev;
if (cctxt->inode != NULL)
cctxt->inode->curChildType = 0;
return;
mismatch:
{
const xmlChar *nsName = NULL, *name = NULL;
const xmlChar *infnsName = NULL, *infname = NULL;
if (node) {
if (node->type == XML_ELEMENT_NODE) {
name = node->name;
if (node->ns != NULL)
nsName = node->ns->href;
else
nsName = BAD_CAST "";
} else {
name = BAD_CAST "#document";
nsName = BAD_CAST "";
}
} else
name = BAD_CAST "Not given";
if (cctxt->inode->node) {
if (node->type == XML_ELEMENT_NODE) {
infname = cctxt->inode->node->name;
if (cctxt->inode->node->ns != NULL)
infnsName = cctxt->inode->node->ns->href;
else
infnsName = BAD_CAST "";
} else {
infname = BAD_CAST "#document";
infnsName = BAD_CAST "";
}
} else
infname = BAD_CAST "Not given";
xmlGenericError(xmlGenericErrorContext,
"xsltCompilerNodePop: Given : '%s' URI '%s'\n",
name, nsName);
xmlGenericError(xmlGenericErrorContext,
"xsltCompilerNodePop: Expected: '%s' URI '%s'\n",
infname, infnsName);
}
}
/*
* xsltCompilerBuildInScopeNsList:
*
* Create and store the list of in-scope namespaces for the given
* node in the stylesheet. If there are no changes in the in-scope
* namespaces then the last ns-info of the ancestor axis will be returned.
* Compilation-time only.
*
* Returns the ns-info or NULL if there are no namespaces in scope.
*/
static xsltNsListContainerPtr
xsltCompilerBuildInScopeNsList(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
{
xsltNsListContainerPtr nsi = NULL;
xmlNsPtr *list = NULL, ns;
int i, maxns = 5;
/*
* Create a new ns-list for this position in the node-tree.
* xmlGetNsList() will return NULL, if there are no ns-decls in the
* tree. Note that the ns-decl for the XML namespace is not added
* to the resulting list; the XPath module handles the XML namespace
* internally.
*/
while (node != NULL) {
if (node->type == XML_ELEMENT_NODE) {
ns = node->nsDef;
while (ns != NULL) {
if (nsi == NULL) {
nsi = (xsltNsListContainerPtr)
xmlMalloc(sizeof(xsltNsListContainer));
if (nsi == NULL) {
xsltTransformError(NULL, cctxt->style, NULL,
"xsltCompilerBuildInScopeNsList: "
"malloc failed!\n");
goto internal_err;
}
memset(nsi, 0, sizeof(xsltNsListContainer));
nsi->list =
(xmlNsPtr *) xmlMalloc(maxns * sizeof(xmlNsPtr));
if (nsi->list == NULL) {
xsltTransformError(NULL, cctxt->style, NULL,
"xsltCompilerBuildInScopeNsList: "
"malloc failed!\n");
goto internal_err;
}
nsi->list[0] = NULL;
}
/*
* Skip shadowed namespace bindings.
*/
for (i = 0; i < nsi->totalNumber; i++) {
if ((ns->prefix == nsi->list[i]->prefix) ||
(xmlStrEqual(ns->prefix, nsi->list[i]->prefix)))
break;
}
if (i >= nsi->totalNumber) {
if (nsi->totalNumber +1 >= maxns) {
maxns *= 2;
nsi->list =
(xmlNsPtr *) xmlRealloc(nsi->list,
maxns * sizeof(xmlNsPtr));
if (nsi->list == NULL) {
xsltTransformError(NULL, cctxt->style, NULL,
"xsltCompilerBuildInScopeNsList: "
"realloc failed!\n");
goto internal_err;
}
}
nsi->list[nsi->totalNumber++] = ns;
nsi->list[nsi->totalNumber] = NULL;
}
ns = ns->next;
}
}
node = node->parent;
}
if (nsi == NULL)
return(NULL);
/*
* Move the default namespace to last position.
*/
nsi->xpathNumber = nsi->totalNumber;
for (i = 0; i < nsi->totalNumber; i++) {
if (nsi->list[i]->prefix == NULL) {
ns = nsi->list[i];
nsi->list[i] = nsi->list[nsi->totalNumber-1];
nsi->list[nsi->totalNumber-1] = ns;
nsi->xpathNumber--;
break;
}
}
/*
* Store the ns-list in the stylesheet.
*/
if (xsltPointerListAddSize(
(xsltPointerListPtr)cctxt->psData->inScopeNamespaces,
(void *) nsi, 5) == -1)
{
xmlFree(nsi);
nsi = NULL;
xsltTransformError(NULL, cctxt->style, NULL,
"xsltCompilerBuildInScopeNsList: failed to add ns-info.\n");
goto internal_err;
}
/*
* Notify of change in status wrt namespaces.
*/
if (cctxt->inode != NULL)
cctxt->inode->nsChanged = 1;
return(nsi);
internal_err:
if (list != NULL)
xmlFree(list);
cctxt->style->errors++;
return(NULL);
}
static int
xsltParseNsPrefixList(xsltCompilerCtxtPtr cctxt,
xsltPointerListPtr list,
xmlNodePtr node,
const xmlChar *value)
{
xmlChar *cur, *end;
xmlNsPtr ns;
if ((cctxt == NULL) || (value == NULL) || (list == NULL))
return(-1);
list->number = 0;
cur = (xmlChar *) value;
while (*cur != 0) {
while (IS_BLANK(*cur)) cur++;
if (*cur == 0)
break;
end = cur;
while ((*end != 0) && (!IS_BLANK(*end))) end++;
cur = xmlStrndup(cur, end - cur);
if (cur == NULL) {
cur = end;
continue;
}
/*
* TODO: Export and use xmlSearchNsByPrefixStrict()
* in Libxml2, tree.c, since xmlSearchNs() is in most
* cases not efficient and in some cases not correct.
*
* XSLT-2 TODO: XSLT 2.0 allows an additional "#all" value.
*/
if ((cur[0] == '#') &&
xmlStrEqual(cur, (const xmlChar *)"#default"))
ns = xmlSearchNs(cctxt->style->doc, node, NULL);
else
ns = xmlSearchNs(cctxt->style->doc, node, cur);
if (ns == NULL) {
/*
* TODO: Better to report the attr-node, otherwise
* the user won't know which attribute was invalid.
*/
xsltTransformError(NULL, cctxt->style, node,
"No namespace binding in scope for prefix '%s'.\n", cur);
/*
* XSLT-1.0: "It is an error if there is no namespace
* bound to the prefix on the element bearing the
* exclude-result-prefixes or xsl:exclude-result-prefixes
* attribute."
*/
cctxt->style->errors++;
} else {
#ifdef WITH_XSLT_DEBUG_PARSING
xsltGenericDebug(xsltGenericDebugContext,
"resolved prefix '%s'\n", cur);
#endif
/*
* Note that we put the namespace name into the dict.
*/
if (xsltPointerListAddSize(list,
(void *) xmlDictLookup(cctxt->style->dict,
ns->href, -1), 5) == -1)
{
xmlFree(cur);
goto internal_err;
}
}
xmlFree(cur);
cur = end;
}
return(0);
internal_err:
cctxt->style->errors++;
return(-1);
}
/**
* xsltCompilerUtilsCreateMergedList:
* @dest: the destination list (optional)
* @first: the first list
* @second: the second list (optional)
*
* Appends the content of @second to @first into @destination.
* If @destination is NULL a new list will be created.
*
* Returns the merged list of items or NULL if there's nothing to merge.
*/
static xsltPointerListPtr
xsltCompilerUtilsCreateMergedList(xsltPointerListPtr first,
xsltPointerListPtr second)
{
xsltPointerListPtr ret;
size_t num;
if (first)
num = first->number;
else
num = 0;
if (second)
num += second->number;
if (num == 0)
return(NULL);
ret = xsltPointerListCreate(num);
if (ret == NULL)
return(NULL);
/*
* Copy contents.
*/
if ((first != NULL) && (first->number != 0)) {
memcpy(ret->items, first->items,
first->number * sizeof(void *));
if ((second != NULL) && (second->number != 0))
memcpy(ret->items + first->number, second->items,
second->number * sizeof(void *));
} else if ((second != NULL) && (second->number != 0))
memcpy(ret->items, (void *) second->items,
second->number * sizeof(void *));
ret->number = num;
return(ret);
}
/*
* xsltParseExclResultPrefixes:
*
* Create and store the list of in-scope namespaces for the given
* node in the stylesheet. If there are no changes in the in-scope
* namespaces then the last ns-info of the ancestor axis will be returned.
* Compilation-time only.
*
* Returns the ns-info or NULL if there are no namespaces in scope.
*/
static xsltPointerListPtr
xsltParseExclResultPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,
xsltPointerListPtr def,
int instrCategory)
{
xsltPointerListPtr list = NULL;
xmlChar *value;
xmlAttrPtr attr;
if ((cctxt == NULL) || (node == NULL))
return(NULL);
if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT)
attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes", NULL);
else
attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes",
XSLT_NAMESPACE);
if (attr == NULL)
return(def);
if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) {
/*
* Mark the XSLT attr.
*/
attr->psvi = (void *) xsltXSLTAttrMarker;
}
if ((attr->children != NULL) &&
(attr->children->content != NULL))
value = attr->children->content;
else {
xsltTransformError(NULL, cctxt->style, node,
"Attribute 'exclude-result-prefixes': Invalid value.\n");
cctxt->style->errors++;
return(def);
}
if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node,
BAD_CAST value) != 0)
goto exit;
if (cctxt->tmpList->number == 0)
goto exit;
/*
* Merge the list with the inherited list.
*/
list = xsltCompilerUtilsCreateMergedList(def, cctxt->tmpList);
if (list == NULL)
goto exit;
/*
* Store the list in the stylesheet/compiler context.
*/
if (xsltPointerListAddSize(
cctxt->psData->exclResultNamespaces, list, 5) == -1)
{
xsltPointerListFree(list);
list = NULL;
goto exit;
}
/*
* Notify of change in status wrt namespaces.
*/
if (cctxt->inode != NULL)
cctxt->inode->nsChanged = 1;
exit:
if (list != NULL)
return(list);
else
return(def);
}
/*
* xsltParseExtElemPrefixes:
*
* Create and store the list of in-scope namespaces for the given
* node in the stylesheet. If there are no changes in the in-scope
* namespaces then the last ns-info of the ancestor axis will be returned.
* Compilation-time only.
*
* Returns the ns-info or NULL if there are no namespaces in scope.
*/
static xsltPointerListPtr
xsltParseExtElemPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,
xsltPointerListPtr def,
int instrCategory)
{
xsltPointerListPtr list = NULL;
xmlAttrPtr attr;
xmlChar *value;
int i;
if ((cctxt == NULL) || (node == NULL))
return(NULL);
if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT)
attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes", NULL);
else
attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes",
XSLT_NAMESPACE);
if (attr == NULL)
return(def);
if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) {
/*
* Mark the XSLT attr.
*/
attr->psvi = (void *) xsltXSLTAttrMarker;