1
0
mirror of https://gitlab.gnome.org/GNOME/libxslt synced 2025-08-08 21:42:07 +03:00
Files
libxslt/libxslt/transform.c
Daniel Veillard 3efba7ccbf revamped the mechanism to hook a debuger to use a callback setup function,
* libxslt/attributes.c libxslt/transform.c libxslt/xsltutils.c
  libxslt/xsltutils.h: revamped the mechanism to hook a debuger
  to use a callback setup function, deprecating libxsltbreakpoint
* xsltproc/Makefile.am configure.in breakpoint/Makefile.am: removing
  dependancies on libxsltbreakpoint
Daniel
2001-11-30 12:01:25 +00:00

3717 lines
110 KiB
C

/*
* transform.c: Implemetation of the XSL Transformation 1.0 engine
* transform part, i.e. applying a Stylesheet to a document
*
* References:
* http://www.w3.org/TR/1999/REC-xslt-19991116
*
* Michael Kay "XSLT Programmer's Reference" pp 637-643
* Writing Multiple Output Files
*
* XSLT-1.1 Working Draft
* http://www.w3.org/TR/xslt11#multiple-output
*
* See Copyright for the status of this software.
*
* daniel@veillard.com
*/
#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/encoding.h>
#include <libxml/xmlerror.h>
#include <libxml/xpath.h>
#include <libxml/parserInternals.h>
#include <libxml/xpathInternals.h>
#include <libxml/HTMLtree.h>
#include <libxml/debugXML.h>
#include <libxml/uri.h>
#include "xslt.h"
#include "xsltInternals.h"
#include "xsltutils.h"
#include "pattern.h"
#include "transform.h"
#include "variables.h"
#include "numbersInternals.h"
#include "namespaces.h"
#include "attributes.h"
#include "templates.h"
#include "imports.h"
#include "keys.h"
#include "documents.h"
#include "extensions.h"
#include "extra.h"
#include "preproc.h"
#ifdef WITH_XSLT_DEBUG
#define WITH_XSLT_DEBUG_EXTRA
#define WITH_XSLT_DEBUG_PROCESS
#endif
#define XSLT_GENERATE_HTML_DOCTYPE
#ifdef XSLT_GENERATE_HTML_DOCTYPE
static int xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID,
const xmlChar **systemID);
#endif
int xsltMaxDepth = 5000;
/*
* Useful macros
*/
#ifndef FALSE
# define FALSE (0 == 1)
# define TRUE (!FALSE)
#endif
#define IS_BLANK_NODE(n) \
(((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
/*
* Generic function for accessing stacks in the transform Context
*/
#define PUSH_AND_POP(scope, type, name) \
scope int name##Push(xsltTransformContextPtr ctxt, type value) { \
if (ctxt->name##Max == 0) { \
ctxt->name##Max = 4; \
ctxt->name##Tab = (type *) xmlMalloc(ctxt->name##Max * \
sizeof(ctxt->name##Tab[0])); \
if (ctxt->name##Tab == NULL) { \
xmlGenericError(xmlGenericErrorContext, \
"malloc failed !\n"); \
return(0); \
} \
} \
if (ctxt->name##Nr >= ctxt->name##Max) { \
ctxt->name##Max *= 2; \
ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
if (ctxt->name##Tab == NULL) { \
xmlGenericError(xmlGenericErrorContext, \
"realloc failed !\n"); \
return(0); \
} \
} \
ctxt->name##Tab[ctxt->name##Nr] = value; \
ctxt->name = value; \
return(ctxt->name##Nr++); \
} \
scope type name##Pop(xsltTransformContextPtr ctxt) { \
type ret; \
if (ctxt->name##Nr <= 0) return(0); \
ctxt->name##Nr--; \
if (ctxt->name##Nr > 0) \
ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
else \
ctxt->name = (type) 0; \
ret = ctxt->name##Tab[ctxt->name##Nr]; \
ctxt->name##Tab[ctxt->name##Nr] = 0; \
return(ret); \
} \
/*
* Those macros actually generate the functions
*/
PUSH_AND_POP(static, xsltTemplatePtr, templ)
PUSH_AND_POP(static, xsltStackElemPtr, vars)
PUSH_AND_POP(static, long, prof)
/************************************************************************
* *
* XInclude default settings *
* *
************************************************************************/
static int xsltDoXIncludeDefault = 0;
/**
* xsltSetXIncludeDefault:
* @xinclude: whether to do XInclude processing
*
* Set whether XInclude should be processed on document being loaded by default
*/
void
xsltSetXIncludeDefault(int xinclude) {
xsltDoXIncludeDefault = (xinclude != 0);
}
/**
* xsltGetXIncludeDefault:
*
* return the default state for XInclude processing
*
* Returns 0 if there is no processing 1 otherwise
*/
int
xsltGetXIncludeDefault(void) {
return(xsltDoXIncludeDefault);
}
/************************************************************************
* *
* Handling of Transformation Contexts *
* *
************************************************************************/
/**
* xsltNewTransformContext:
* @style: a parsed XSLT stylesheet
* @doc: the input document
*
* Create a new XSLT TransformContext
*
* Returns the newly allocated xsltTransformContextPtr or NULL in case of error
*/
xsltTransformContextPtr
xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) {
xsltTransformContextPtr cur;
xsltDocumentPtr docu;
cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext));
if (cur == NULL) {
xsltPrintErrorContext(NULL, NULL, (xmlNodePtr)doc);
xsltGenericError(xsltGenericErrorContext,
"xsltNewTransformContext : malloc failed\n");
return(NULL);
}
memset(cur, 0, sizeof(xsltTransformContext));
/*
* initialize the template stack
*/
cur->templTab = (xsltTemplatePtr *)
xmlMalloc(10 * sizeof(xsltTemplatePtr));
if (cur->templTab == NULL) {
xsltPrintErrorContext(NULL, NULL, (xmlNodePtr) doc);
xsltGenericError(xsltGenericErrorContext,
"xsltNewTransformContext: out of memory\n");
xmlFree(cur);
return(NULL);
}
cur->templNr = 0;
cur->templMax = 5;
cur->templ = NULL;
/*
* initialize the variables stack
*/
cur->varsTab = (xsltStackElemPtr *)
xmlMalloc(10 * sizeof(xsltStackElemPtr));
if (cur->varsTab == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xsltNewTransformContext: out of memory\n");
xmlFree(cur->templTab);
xmlFree(cur);
return(NULL);
}
cur->varsNr = 0;
cur->varsMax = 5;
cur->vars = NULL;
cur->varsBase = 0;
/*
* the profiling stcka is not initialized by default
*/
cur->profTab = NULL;
cur->profNr = 0;
cur->profMax = 0;
cur->prof = 0;
cur->style = style;
xmlXPathInit();
cur->xpathCtxt = xmlXPathNewContext(doc);
if (cur->xpathCtxt == NULL) {
xsltPrintErrorContext(NULL, NULL, (xmlNodePtr) doc);
xsltGenericError(xsltGenericErrorContext,
"xsltNewTransformContext : xmlXPathNewContext failed\n");
xmlFree(cur->templTab);
xmlFree(cur->varsTab);
xmlFree(cur);
return(NULL);
}
cur->xpathCtxt->proximityPosition = 0;
cur->xpathCtxt->contextSize = 0;
XSLT_REGISTER_VARIABLE_LOOKUP(cur);
XSLT_REGISTER_FUNCTION_LOOKUP(cur);
cur->xpathCtxt->nsHash = style->nsHash;
docu = xsltNewDocument(cur, doc);
if (docu == NULL) {
xsltPrintErrorContext(cur, NULL, (xmlNodePtr)doc);
xsltGenericError(xsltGenericErrorContext,
"xsltNewTransformContext : xsltNewDocument failed\n");
xmlFree(cur->templTab);
xmlFree(cur->varsTab);
xmlFree(cur);
return(NULL);
}
docu->main = 1;
cur->document = docu;
cur->inst = NULL;
cur->xinclude = xsltDoXIncludeDefault;
cur->outputFile = NULL;
return(cur);
}
/**
* xsltFreeTransformContext:
* @ctxt: an XSLT parser context
*
* Free up the memory allocated by @ctxt
*/
void
xsltFreeTransformContext(xsltTransformContextPtr ctxt) {
if (ctxt == NULL)
return;
if (ctxt->xpathCtxt != NULL) {
ctxt->xpathCtxt->nsHash = NULL;
xmlXPathFreeContext(ctxt->xpathCtxt);
}
if (ctxt->templTab != NULL)
xmlFree(ctxt->templTab);
if (ctxt->varsTab != NULL)
xmlFree(ctxt->varsTab);
if (ctxt->profTab != NULL)
xmlFree(ctxt->profTab);
xsltFreeDocuments(ctxt);
xsltFreeCtxtExts(ctxt);
xsltFreeGlobalVariables(ctxt);
memset(ctxt, -1, sizeof(xsltTransformContext));
xmlFree(ctxt);
}
/************************************************************************
* *
* Copy of Nodes in an XSLT fashion *
* *
************************************************************************/
xmlNodePtr xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr insert);
/**
* xsltCopyTextString:
* @ctxt: a XSLT process context
* @target: the element where the text will be attached
* @string: the text string
*
* Create a text node
*
* Returns: a new xmlNodePtr, or NULL in case of error.
*/
static xmlNodePtr
xsltCopyTextString(xsltTransformContextPtr ctxt, xmlNodePtr target,
const xmlChar *string) {
xmlNodePtr copy;
if (string == NULL)
return(NULL);
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltCopyTextString: copy text %s\n",
string);
#endif
/* TODO: handle coalescing of text nodes here */
if ((ctxt->type == XSLT_OUTPUT_XML) &&
(ctxt->style->cdataSection != NULL) &&
(target != NULL) &&
(xmlHashLookup(ctxt->style->cdataSection,
target->name) != NULL)) {
copy = xmlNewCDataBlock(ctxt->output, string,
xmlStrlen(string));
} else {
if ((target != NULL) && (target->last != NULL) &&
(target->last->type == XML_TEXT_NODE) &&
(target->last->name == xmlStringText)) {
xmlNodeAddContent(target->last, string);
return(target->last);
}
copy = xmlNewText(string);
}
if (copy != NULL) {
if (target != NULL)
xmlAddChild(target, copy);
} else {
xsltPrintErrorContext(ctxt, NULL, target);
xsltGenericError(xsltGenericErrorContext,
"xsltCopyTextString: text copy failed\n");
}
return(copy);
}
/**
* xsltCopyText:
* @ctxt: a XSLT process context
* @target: the element where the text will be attached
* @cur: the text or CDATA node
*
* Do a copy of a text node
*
* Returns: a new xmlNodePtr, or NULL in case of error.
*/
static xmlNodePtr
xsltCopyText(xsltTransformContextPtr ctxt, xmlNodePtr target,
xmlNodePtr cur) {
xmlNodePtr copy;
if ((cur->type != XML_TEXT_NODE) &&
(cur->type != XML_CDATA_SECTION_NODE))
return(NULL);
if (cur->content == NULL)
return(NULL);
#ifdef WITH_XSLT_DEBUG_PROCESS
if (cur->type == XML_CDATA_SECTION_NODE)
xsltGenericDebug(xsltGenericDebugContext,
"xsltCopyText: copy CDATA text %s\n",
cur->content);
else if (cur->name == xmlStringTextNoenc)
xsltGenericDebug(xsltGenericDebugContext,
"xsltCopyText: copy unescaped text %s\n",
cur->content);
else
xsltGenericDebug(xsltGenericDebugContext,
"xsltCopyText: copy text %s\n",
cur->content);
#endif
/* TODO: handle coalescing of text nodes here */
if ((ctxt->type == XSLT_OUTPUT_XML) &&
(ctxt->style->cdataSection != NULL) &&
(target != NULL) &&
(xmlHashLookup(ctxt->style->cdataSection,
target->name) != NULL)) {
copy = xmlNewCDataBlock(ctxt->output, cur->content,
xmlStrlen(cur->content));
} else {
if ((target != NULL) && (target->last != NULL) &&
(target->last->type == XML_TEXT_NODE) &&
(target->last->name == xmlStringText) &&
(cur->name != xmlStringTextNoenc) &&
(cur->type != XML_CDATA_SECTION_NODE)) {
xmlNodeAddContent(target->last, cur->content);
return(target->last);
}
copy = xmlNewText(cur->content);
if ((cur->name == xmlStringTextNoenc) ||
(cur->type == XML_CDATA_SECTION_NODE))
copy->name = xmlStringTextNoenc;
}
if (copy != NULL) {
if (target != NULL)
xmlAddChild(target, copy);
} else {
xsltPrintErrorContext(ctxt, NULL, target);
xsltGenericError(xsltGenericErrorContext,
"xsltCopyText: text copy failed\n");
}
return(copy);
}
/**
* xsltCopyProp:
* @ctxt: a XSLT process context
* @target: the element where the attribute will be grafted
* @attr: the attribute
*
* Do a copy of an attribute
*
* Returns: a new xmlAttrPtr, or NULL in case of error.
*/
static xmlAttrPtr
xsltCopyProp(xsltTransformContextPtr ctxt, xmlNodePtr target,
xmlAttrPtr attr) {
xmlAttrPtr ret = NULL;
xmlNsPtr ns;
xmlChar *val;
if (attr == NULL)
return(NULL);
if (attr->ns != NULL) {
ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target);
} else {
ns = NULL;
}
val = xmlNodeListGetString(attr->doc, attr->children, 1);
ret = xmlSetNsProp(target, ns, attr->name, val);
if (val != NULL)
xmlFree(val);
return(ret);
}
/**
* xsltCopyPropList:
* @ctxt: a XSLT process context
* @target: the element where the properties will be grafted
* @cur: the first property
*
* Do a copy of a properties list.
*
* Returns: a new xmlAttrPtr, or NULL in case of error.
*/
static xmlAttrPtr
xsltCopyPropList(xsltTransformContextPtr ctxt, xmlNodePtr target,
xmlAttrPtr cur) {
xmlAttrPtr ret = NULL;
xmlAttrPtr p = NULL,q;
xmlNsPtr ns;
while (cur != NULL) {
if (cur->ns != NULL) {
ns = xsltGetNamespace(ctxt, cur->parent, cur->ns, target);
} else {
ns = NULL;
}
q = xmlCopyProp(target, cur);
if (q != NULL) {
q->ns = ns;
if (p == NULL) {
ret = p = q;
} else {
p->next = q;
q->prev = p;
p = q;
}
}
cur = cur->next;
}
return(ret);
}
/**
* xsltCopyNode:
* @ctxt: a XSLT process context
* @node: the element node in the source tree.
* @insert: the parent in the result tree.
*
* Make a copy of the element node @node
* and insert it as last child of @insert
*
* Returns a pointer to the new node, or NULL in case of error
*/
static xmlNodePtr
xsltCopyNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr insert) {
xmlNodePtr copy;
if (node->type == XML_DTD_NODE)
return(NULL);
if ((node->type == XML_TEXT_NODE) ||
(node->type == XML_CDATA_SECTION_NODE))
return(xsltCopyText(ctxt, insert, node));
copy = xmlCopyNode(node, 0);
if (copy != NULL) {
copy->doc = ctxt->output;
xmlAddChild(insert, copy);
if (node->type == XML_ELEMENT_NODE) {
/*
* Add namespaces as they are needed
*/
if (node->nsDef != NULL)
xsltCopyNamespaceList(ctxt, copy, node->nsDef);
}
if (((node->type == XML_ELEMENT_NODE) ||
(node->type == XML_ATTRIBUTE_NODE)) &&
(node->ns != NULL)) {
copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy);
}
} else {
xsltPrintErrorContext(ctxt, NULL, node);
xsltGenericError(xsltGenericErrorContext,
"xsltCopyNode: copy %s failed\n", node->name);
}
return(copy);
}
/**
* xsltCopyTreeList:
* @ctxt: a XSLT process context
* @list: the list of element nodes in the source tree.
* @insert: the parent in the result tree.
*
* Make a copy of the full list of tree @list
* and insert it as last children of @insert
*
* Returns a pointer to the new list, or NULL in case of error
*/
static xmlNodePtr
xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr list,
xmlNodePtr insert) {
xmlNodePtr copy, ret = NULL;
while (list != NULL) {
copy = xsltCopyTree(ctxt, list, insert);
if (copy != NULL) {
if (ret == NULL) {
ret = copy;
}
}
list = list->next;
}
return(ret);
}
/**
* xsltCopyTree:
* @ctxt: a XSLT process context
* @node: the element node in the source tree.
* @insert: the parent in the result tree.
*
* Make a copy of the full tree under the element node @node
* and insert it as last child of @insert
*
* Returns a pointer to the new tree, or NULL in case of error
*/
xmlNodePtr
xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr insert) {
xmlNodePtr copy;
if (node == NULL)
return(NULL);
switch (node->type) {
case XML_ELEMENT_NODE:
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
break;
case XML_ATTRIBUTE_NODE:
return((xmlNodePtr)
xsltCopyProp(ctxt, insert, (xmlAttrPtr) node));
case XML_NAMESPACE_DECL:
if (insert->type != XML_ELEMENT_NODE)
return(NULL);
return((xmlNodePtr)
xsltCopyNamespaceList(ctxt, insert, (xmlNsPtr) node));
case XML_DOCUMENT_TYPE_NODE:
case XML_DOCUMENT_FRAG_NODE:
case XML_NOTATION_NODE:
case XML_DTD_NODE:
case XML_ELEMENT_DECL:
case XML_ATTRIBUTE_DECL:
case XML_ENTITY_DECL:
case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
return(NULL);
}
copy = xmlCopyNode(node, 0);
copy->doc = ctxt->output;
if (copy != NULL) {
xmlAddChild(insert, copy);
copy->next = NULL;
/*
* Add namespaces as they are needed
*/
if (node->nsDef != NULL)
xsltCopyNamespaceList(ctxt, copy, node->nsDef);
if (node->ns != NULL) {
copy->ns = xsltGetNamespace(ctxt, node, node->ns, insert);
}
if (node->properties != NULL)
copy->properties = xsltCopyPropList(ctxt, copy,
node->properties);
if (node->children != NULL)
xsltCopyTreeList(ctxt, node->children, copy);
} else {
xsltPrintErrorContext(ctxt, NULL, node);
xsltGenericError(xsltGenericErrorContext,
"xsltCopyTree: copy %s failed\n", node->name);
}
return(copy);
}
/************************************************************************
* *
* Error/fallback processing *
* *
************************************************************************/
/**
* xsltApplyFallbacks:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the node generating the error
*
* Process possible xsl:fallback nodes present under @inst
*
* Returns the number of xsl:fallback element found and processed
*/
static int
xsltApplyFallbacks(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst) {
xmlNodePtr child;
int ret = 0;
if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
return(ret);
child = inst->children;
while (child != NULL) {
if ((IS_XSLT_ELEM(child)) &&
(xmlStrEqual(child->name, BAD_CAST "fallback"))) {
#ifdef WITH_XSLT_DEBUG_PARSING
xsltGenericDebug(xsltGenericDebugContext,
"applying xsl:fallback\n");
#endif
ret++;
xsltApplyOneTemplate(ctxt, node, child->children, NULL, NULL);
}
child = child->next;
}
return(ret);
}
/************************************************************************
* *
* Default processing *
* *
************************************************************************/
void xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
xsltStackElemPtr params);
/**
* xsltDefaultProcessOneNode:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
*
* Process the source node with the default built-in template rule:
* <xsl:template match="*|/">
* <xsl:apply-templates/>
* </xsl:template>
*
* and
*
* <xsl:template match="text()|@*">
* <xsl:value-of select="."/>
* </xsl:template>
*
* Note also that namespace declarations are copied directly:
*
* the built-in template rule is the only template rule that is applied
* for namespace nodes.
*/
static void
xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
xmlNodePtr copy;
xmlAttrPtr attrs;
xmlNodePtr delete = NULL, cur;
int nbchild = 0, oldSize;
int childno = 0, oldPos;
xsltTemplatePtr template;
CHECK_STOPPED;
/*
* Handling of leaves
*/
switch (node->type) {
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
case XML_ELEMENT_NODE:
break;
case XML_CDATA_SECTION_NODE:
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: copy CDATA %s\n",
node->content);
#endif
copy = xmlNewDocText(ctxt->output, node->content);
if (copy != NULL) {
xmlAddChild(ctxt->insert, copy);
} else {
xsltPrintErrorContext(ctxt, NULL, node);
xsltGenericError(xsltGenericErrorContext,
"xsltDefaultProcessOneNode: cdata copy failed\n");
}
return;
case XML_TEXT_NODE:
#ifdef WITH_XSLT_DEBUG_PROCESS
if (node->content == NULL)
xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: copy empty text\n");
else
xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: copy text %s\n",
node->content);
#endif
copy = xmlCopyNode(node, 0);
if (copy != NULL) {
xmlAddChild(ctxt->insert, copy);
} else {
xsltPrintErrorContext(ctxt, NULL, node);
xsltGenericError(xsltGenericErrorContext,
"xsltDefaultProcessOneNode: text copy failed\n");
}
return;
case XML_ATTRIBUTE_NODE:
cur = node->children;
while ((cur != NULL) && (cur->type != XML_TEXT_NODE))
cur = cur->next;
if (cur == NULL) {
xsltPrintErrorContext(ctxt, NULL, node);
xsltGenericError(xsltGenericErrorContext,
"xsltDefaultProcessOneNode: no text for attribute\n");
} else {
#ifdef WITH_XSLT_DEBUG_PROCESS
if (cur->content == NULL)
xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: copy empty text\n");
else
xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: copy text %s\n",
cur->content);
#endif
copy = xmlCopyNode(cur, 0);
if (copy != NULL) {
xmlAddChild(ctxt->insert, copy);
} else {
xsltPrintErrorContext(ctxt, NULL, node);
xsltGenericError(xsltGenericErrorContext,
"xsltDefaultProcessOneNode: text copy failed\n");
}
}
return;
default:
return;
}
/*
* Handling of Elements: first pass, cleanup and counting
*/
cur = node->children;
while (cur != NULL) {
switch (cur->type) {
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
case XML_ELEMENT_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
nbchild++;
break;
default:
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: skipping node type %d\n",
cur->type);
#endif
delete = cur;
}
cur = cur->next;
if (delete != NULL) {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: removing ignorable blank node\n");
#endif
xmlUnlinkNode(delete);
xmlFreeNode(delete);
delete = NULL;
}
}
if (delete != NULL) {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: removing ignorable blank node\n");
#endif
xmlUnlinkNode(delete);
xmlFreeNode(delete);
delete = NULL;
}
/*
* Handling of Elements: second pass, actual processing
*/
attrs = node->properties;
while (attrs != NULL) {
template = xsltGetTemplate(ctxt, (xmlNodePtr) attrs, NULL);
if (template) {
xsltApplyOneTemplate(ctxt, node, template->content, template,
NULL);
}
attrs = attrs->next;
}
oldSize = ctxt->xpathCtxt->contextSize;
oldPos = ctxt->xpathCtxt->proximityPosition;
cur = node->children;
while (cur != NULL) {
childno++;
switch (cur->type) {
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
case XML_ELEMENT_NODE:
ctxt->xpathCtxt->contextSize = nbchild;
ctxt->xpathCtxt->proximityPosition = childno;
xsltProcessOneNode(ctxt, cur, NULL);
break;
case XML_CDATA_SECTION_NODE:
template = xsltGetTemplate(ctxt, cur, NULL);
if (template) {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: applying template for CDATA %s\n",
cur->content);
#endif
xsltApplyOneTemplate(ctxt, cur, template->content,
template, NULL);
} else /* if (ctxt->mode == NULL) */ {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: copy CDATA %s\n",
cur->content);
#endif
copy = xmlNewDocText(ctxt->output, cur->content);
if (copy != NULL) {
xmlAddChild(ctxt->insert, copy);
} else {
xsltPrintErrorContext(ctxt, NULL, cur);
xsltGenericError(xsltGenericErrorContext,
"xsltDefaultProcessOneNode: cdata copy failed\n");
}
}
break;
case XML_TEXT_NODE:
template = xsltGetTemplate(ctxt, cur, NULL);
if (template) {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: applying template for text %s\n",
cur->content);
#endif
ctxt->xpathCtxt->contextSize = nbchild;
ctxt->xpathCtxt->proximityPosition = childno;
xsltApplyOneTemplate(ctxt, cur, template->content,
template, NULL);
} else /* if (ctxt->mode == NULL) */ {
#ifdef WITH_XSLT_DEBUG_PROCESS
if (cur->content == NULL)
xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: copy empty text\n");
else
xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: copy text %s\n",
cur->content);
#endif
copy = xmlCopyNode(cur, 0);
if (copy != NULL) {
xmlAddChild(ctxt->insert, copy);
} else {
xsltPrintErrorContext(ctxt, NULL, cur);
xsltGenericError(xsltGenericErrorContext,
"xsltDefaultProcessOneNode: text copy failed\n");
}
}
break;
case XML_PI_NODE:
case XML_COMMENT_NODE:
template = xsltGetTemplate(ctxt, cur, NULL);
if (template) {
#ifdef WITH_XSLT_DEBUG_PROCESS
if (cur->type == XML_PI_NODE)
xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: template found for PI %s\n",
cur->name);
else if (cur->type == XML_COMMENT_NODE)
xsltGenericDebug(xsltGenericDebugContext,
"xsltDefaultProcessOneNode: template found for comment\n");
#endif
ctxt->xpathCtxt->contextSize = nbchild;
ctxt->xpathCtxt->proximityPosition = childno;
xsltApplyOneTemplate(ctxt, cur, template->content,
template, NULL);
}
break;
default:
break;
}
cur = cur->next;
}
ctxt->xpathCtxt->contextSize = oldSize;
ctxt->xpathCtxt->proximityPosition = oldPos;
}
/**
* xsltProcessOneNode:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
*
* Process the source node.
*/
void
xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
xsltStackElemPtr params) {
xsltTemplatePtr template;
xmlNodePtr oldNode;
template = xsltGetTemplate(ctxt, node, NULL);
/*
* If no template is found, apply the default rule.
*/
if (template == NULL) {
#ifdef WITH_XSLT_DEBUG_PROCESS
if (node->type == XML_DOCUMENT_NODE)
xsltGenericDebug(xsltGenericDebugContext,
"xsltProcessOneNode: no template found for /\n");
else if (node->type == XML_CDATA_SECTION_NODE)
xsltGenericDebug(xsltGenericDebugContext,
"xsltProcessOneNode: no template found for CDATA\n");
else if (node->type == XML_ATTRIBUTE_NODE)
xsltGenericDebug(xsltGenericDebugContext,
"xsltProcessOneNode: no template found for attribute %s\n",
((xmlAttrPtr) node)->name);
else
xsltGenericDebug(xsltGenericDebugContext,
"xsltProcessOneNode: no template found for %s\n", node->name);
#endif
oldNode = ctxt->node;
ctxt->node = node;
xsltDefaultProcessOneNode(ctxt, node);
ctxt->node = oldNode;
return;
}
if (node->type == XML_ATTRIBUTE_NODE) {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltProcessOneNode: applying template '%s' for attribute %s\n",
template->match, node->name);
#endif
xsltApplyOneTemplate(ctxt, node, template->content, template, params);
} else {
#ifdef WITH_XSLT_DEBUG_PROCESS
if (node->type == XML_DOCUMENT_NODE)
xsltGenericDebug(xsltGenericDebugContext,
"xsltProcessOneNode: applying template '%s' for /\n",
template->match);
else
xsltGenericDebug(xsltGenericDebugContext,
"xsltProcessOneNode: applying template '%s' for %s\n",
template->match, node->name);
#endif
xsltApplyOneTemplate(ctxt, node, template->content, template, params);
}
}
/**
* xsltApplyOneTemplate:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @list: the template replacement nodelist
* @templ: if is this a real template processing, the template processed
* @params: a set of parameters for the template or NULL
*
* Process the apply-templates node on the source node, if params are passed
* they are pushed on the variable stack but not popped, it's left to the
* caller to handle them back (they may be reused).
*/
void
xsltApplyOneTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr list, xsltTemplatePtr templ,
xsltStackElemPtr params)
{
xmlNodePtr cur = NULL, insert, copy = NULL;
xmlNodePtr oldInsert;
xmlNodePtr oldCurrent = NULL;
xmlNodePtr oldInst = NULL;
xmlAttrPtr attrs;
int oldBase;
#ifdef WITH_DEBUGGER
int addCallResult = 0;
#endif
long start = 0;
#ifdef WITH_DEBUGGER
if (xslDebugStatus != XSLT_DEBUG_NONE) {
if (templ) {
addCallResult = xslAddCall(templ, templ->elem);
} else {
addCallResult = xslAddCall(NULL, list);
}
switch (xslDebugStatus) {
case XSLT_DEBUG_RUN_RESTART:
case XSLT_DEBUG_QUIT:
if (addCallResult)
xslDropCall();
return;
}
if (templ)
xslHandleDebugger(templ->elem, node, templ, ctxt);
else if (list)
xslHandleDebugger(list, node, templ, ctxt);
else if (ctxt->inst)
xslHandleDebugger(ctxt->inst, node, templ, ctxt);
}
#endif
if ((ctxt == NULL) || (list == NULL))
return;
CHECK_STOPPED;
if (ctxt->templNr >= xsltMaxDepth) {
xsltPrintErrorContext(ctxt, NULL, list);
xsltGenericError(xsltGenericErrorContext,
"xsltApplyOneTemplate: loop found ???\n");
xsltGenericError(xsltGenericErrorContext,
"try increasing xsltMaxDepth (--maxdepth)\n");
xsltDebug(ctxt, node, list, NULL);
return;
}
/*
* stack saves, beware ordering of operations counts
*/
oldInsert = insert = ctxt->insert;
oldInst = ctxt->inst;
oldCurrent = ctxt->node;
varsPush(ctxt, params);
oldBase = ctxt->varsBase; /* only needed if templ != NULL */
if (templ != NULL) {
ctxt->varsBase = ctxt->varsNr - 1;
ctxt->node = node;
if (ctxt->profile) {
templ->nbCalls++;
start = xsltTimestamp();
profPush(ctxt, 0);
}
templPush(ctxt, templ);
#ifdef WITH_XSLT_DEBUG_PROCESS
if (templ->name != NULL)
xsltGenericDebug(xsltGenericDebugContext,
"applying template '%s'\n", templ->name);
#endif
}
/*
* Insert all non-XSLT nodes found in the template
*/
cur = list;
while (cur != NULL) {
ctxt->inst = cur;
#ifdef WITH_DEBUGGER
switch (xslDebugStatus) {
case XSLT_DEBUG_RUN_RESTART:
case XSLT_DEBUG_QUIT:
break;
}
#endif
/*
* test, we must have a valid insertion point
*/
if (insert == NULL) {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltApplyOneTemplate: insert == NULL !\n");
#endif
goto error;
}
#ifdef WITH_DEBUGGER
if (xslDebugStatus != XSLT_DEBUG_NONE)
xslHandleDebugger(cur, node, templ, ctxt);
#endif
if (IS_XSLT_ELEM(cur)) {
/*
* This is an XSLT node
*/
xsltStylePreCompPtr info = (xsltStylePreCompPtr) cur->_private;
if (info == NULL) {
if (IS_XSLT_NAME(cur, "message")) {
xsltMessage(ctxt, node, cur);
} else {
/*
* That's an error try to apply one of the fallback cases
*/
ctxt->insert = insert;
if (!xsltApplyFallbacks(ctxt, node, cur)) {
xsltGenericError(xsltGenericDebugContext,
"xsltApplyOneTemplate: %s was not compiled\n",
cur->name);
}
ctxt->insert = oldInsert;
}
goto skip_children;
}
if (info->func != NULL) {
ctxt->insert = insert;
info->func(ctxt, node, cur, (xsltElemPreCompPtr) info);
ctxt->insert = oldInsert;
goto skip_children;
}
if (IS_XSLT_NAME(cur, "variable")) {
xsltParseStylesheetVariable(ctxt, cur);
} else if (IS_XSLT_NAME(cur, "param")) {
xsltParseStylesheetParam(ctxt, cur);
} else if (IS_XSLT_NAME(cur, "message")) {
xsltMessage(ctxt, node, cur);
} else {
xsltGenericError(xsltGenericDebugContext,
"xsltApplyOneTemplate: problem with xsl:%s\n",
cur->name);
}
CHECK_STOPPED;
goto skip_children;
} else if ((cur->type == XML_TEXT_NODE) ||
(cur->type == XML_CDATA_SECTION_NODE)) {
/*
* This text comes from the stylesheet
* For stylesheets, the set of whitespace-preserving
* element names consists of just xsl:text.
*/
#ifdef WITH_XSLT_DEBUG_PROCESS
if (cur->type == XML_CDATA_SECTION_NODE)
xsltGenericDebug(xsltGenericDebugContext,
"xsltApplyOneTemplate: copy CDATA text %s\n",
cur->content);
else if (cur->name == xmlStringTextNoenc)
xsltGenericDebug(xsltGenericDebugContext,
"xsltApplyOneTemplate: copy unescaped text %s\n",
cur->content);
else
xsltGenericDebug(xsltGenericDebugContext,
"xsltApplyOneTemplate: copy text %s\n",
cur->content);
#endif
xsltCopyText(ctxt, insert, cur);
} else if ((cur->type == XML_ELEMENT_NODE) &&
(cur->ns != NULL) && (cur->_private != NULL)) {
xsltTransformFunction function;
/*
* Flagged as an extension element
*/
if (cur->_private == xsltExtMarker)
function = (xsltTransformFunction)
xsltExtElementLookup(ctxt, cur->name, cur->ns->href);
else
function = ((xsltElemPreCompPtr) cur->_private)->func;
if (function == NULL) {
xmlNodePtr child;
int found = 0;
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltApplyOneTemplate: unknown extension %s\n",
cur->name);
#endif
/*
* Search if there are fallbacks
*/
child = cur->children;
while (child != NULL) {
if ((IS_XSLT_ELEM(child)) &&
(IS_XSLT_NAME(child, "fallback"))) {
found = 1;
xsltApplyOneTemplate(ctxt, node, child->children,
NULL, NULL);
}
child = child->next;
}
if (!found) {
xsltPrintErrorContext(ctxt, NULL, cur);
xsltGenericError(xsltGenericErrorContext,
"xsltApplyOneTemplate: failed to find extension %s\n",
cur->name);
}
} else {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltApplyOneTemplate: extension construct %s\n",
cur->name);
#endif
ctxt->insert = insert;
function(ctxt, node, cur, cur->_private);
ctxt->insert = oldInsert;
}
goto skip_children;
} else if (cur->type == XML_ELEMENT_NODE) {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltApplyOneTemplate: copy node %s\n",
cur->name);
#endif
copy = xsltCopyNode(ctxt, cur, insert);
/*
* all the attributes are directly inherited
*/
if (cur->properties != NULL) {
attrs = xsltAttrListTemplateProcess(ctxt, copy,
cur->properties);
}
/*
* Add extra namespaces inherited from the current template
* if we are in the first level children
*/
if ((oldInsert == insert) && (ctxt->templ != NULL) &&
(ctxt->templ->inheritedNs != NULL)) {
int i;
xmlNsPtr ns, ret;
for (i = 0; i < ctxt->templ->inheritedNsNr; i++) {
ns = ctxt->templ->inheritedNs[i];
if (ctxt->style->nsAliases != NULL) {
const xmlChar *URI;
URI = (const xmlChar *)
xmlHashLookup(ctxt->style->nsAliases,
ns->href);
if (URI == NULL) {
ret = xmlSearchNs(copy->doc, copy, ns->prefix);
if ((ret == NULL) ||
(!xmlStrEqual(ret->href, ns->href)))
xmlNewNs(copy, ns->href, ns->prefix);
} else if (!xmlStrEqual(URI, XSLT_NAMESPACE)) {
ret = xmlSearchNs(copy->doc, copy, ns->prefix);
if ((ret == NULL) ||
(!xmlStrEqual(ret->href, URI)))
xmlNewNs(copy, URI, ns->prefix);
}
} else {
ret = xmlSearchNs(copy->doc, copy, ns->prefix);
if ((ret == NULL) ||
(!xmlStrEqual(ret->href, ns->href)))
xmlNewNs(copy, ns->href, ns->prefix);
}
}
}
}
/*
* Skip to next node, in document order.
*/
if (cur->children != NULL) {
if (cur->children->type != XML_ENTITY_DECL) {
cur = cur->children;
if (copy != NULL)
insert = copy;
continue;
}
}
skip_children:
if (cur->next != NULL) {
cur = cur->next;
continue;
}
do {
cur = cur->parent;
insert = insert->parent;
if (cur == NULL)
break;
if (cur == list->parent) {
cur = NULL;
break;
}
if (cur->next != NULL) {
cur = cur->next;
break;
}
} while (cur != NULL);
}
error:
ctxt->node = oldCurrent;
ctxt->inst = oldInst;
ctxt->insert = oldInsert;
if (params == NULL)
xsltFreeStackElemList(varsPop(ctxt));
else {
xsltStackElemPtr p, tmp = varsPop(ctxt);
if (tmp != params) {
p = tmp;
while ((p != NULL) && (p->next != params))
p = p->next;
if (p == NULL) {
xsltFreeStackElemList(tmp);
} else {
p->next = NULL;
xsltFreeStackElemList(tmp);
}
}
}
if (templ != NULL) {
ctxt->varsBase = oldBase;
templPop(ctxt);
if (ctxt->profile) {
long spent, child, total, end;
end = xsltTimestamp();
child = profPop(ctxt);
total = end - start;
spent = total - child;
if (spent <= 0) {
/*
* Not possible unless the original calibration failed
* we can try to corret it on the fly.
*/
xsltCalibrateAdjust(spent);
spent = 0;
}
templ->time += spent;
if (ctxt->profNr > 0)
ctxt->profTab[ctxt->profNr - 1] += total;
}
}
#ifdef WITH_DEBUGGER
if ((xslDebugStatus != XSLT_DEBUG_NONE) && (addCallResult)) {
xslDropCall();
}
#endif
}
/************************************************************************
* *
* XSLT-1.1 extensions *
* *
************************************************************************/
/**
* xsltDocumentElem:
* @ctxt: an XSLT processing context
* @node: The current node
* @inst: the instruction in the stylesheet
* @comp: precomputed information
*
* Process an XSLT-1.1 document element
*/
void
xsltDocumentElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst, xsltStylePreCompPtr comp)
{
xsltStylesheetPtr style = NULL;
int ret;
xmlChar *filename = NULL, *prop, *elements;
xmlChar *element, *end;
xmlDocPtr res = NULL;
xmlDocPtr oldOutput;
xmlNodePtr oldInsert, root;
const char *oldOutputFile;
xsltOutputType oldType;
xmlChar *URL = NULL;
const xmlChar *method;
const xmlChar *doctypePublic;
const xmlChar *doctypeSystem;
const xmlChar *version;
if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
return;
if (comp->filename == NULL) {
if (xmlStrEqual(inst->name, (const xmlChar *) "output")) {
#ifdef WITH_XSLT_DEBUG_EXTRA
xsltGenericDebug(xsltGenericDebugContext,
"Found saxon:output extension\n");
#endif
URL = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *) "file",
XSLT_SAXON_NAMESPACE);
if (URL == NULL)
URL = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *) "href",
XSLT_SAXON_NAMESPACE);
} else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) {
#ifdef WITH_XSLT_DEBUG_EXTRA
xsltGenericDebug(xsltGenericDebugContext,
"Found xalan:write extension\n");
#endif
URL = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *)
"select",
XSLT_XALAN_NAMESPACE);
if (URL != NULL) {
xmlXPathCompExprPtr cmp;
xmlChar *val;
/*
* Trying to handle bug #59212
*/
cmp = xmlXPathCompile(URL);
val = xsltEvalXPathString(ctxt, cmp);
xmlXPathFreeCompExpr(cmp);
xmlFree(URL);
URL = val;
}
if (URL == NULL)
URL = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *)
"file",
XSLT_XALAN_NAMESPACE);
if (URL == NULL)
URL = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *)
"href",
XSLT_XALAN_NAMESPACE);
} else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) {
URL = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *) "href",
NULL);
}
} else {
URL = xmlStrdup(comp->filename);
}
if (URL == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsltDocumentElem: href/URI-Reference not found\n");
return;
}
filename = xmlBuildURI(URL, (const xmlChar *) ctxt->outputFile);
if (filename == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsltDocumentElem: URL computation failed for %s\n",
URL);
xmlFree(URL);
return;
}
oldOutputFile = ctxt->outputFile;
oldOutput = ctxt->output;
oldInsert = ctxt->insert;
oldType = ctxt->type;
ctxt->outputFile = (const char *) filename;
style = xsltNewStylesheet();
if (style == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsltDocumentElem: out of memory\n");
goto error;
}
/*
* Version described in 1.1 draft allows full parametrization
* of the output.
*/
prop = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *) "version",
NULL);
if (prop != NULL) {
if (style->version != NULL)
xmlFree(style->version);
style->version = prop;
}
prop = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *) "encoding",
NULL);
if (prop != NULL) {
if (style->encoding != NULL)
xmlFree(style->encoding);
style->encoding = prop;
}
prop = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *) "method",
NULL);
if (prop != NULL) {
const xmlChar *URI;
if (style->method != NULL)
xmlFree(style->method);
style->method = NULL;
if (style->methodURI != NULL)
xmlFree(style->methodURI);
style->methodURI = NULL;
URI = xsltGetQNameURI(inst, &prop);
if (prop == NULL) {
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 {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"invalid value for method: %s\n", prop);
style->warnings++;
}
} else {
style->method = prop;
style->methodURI = xmlStrdup(URI);
}
}
prop = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *)
"doctype-system", NULL);
if (prop != NULL) {
if (style->doctypeSystem != NULL)
xmlFree(style->doctypeSystem);
style->doctypeSystem = prop;
}
prop = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *)
"doctype-public", NULL);
if (prop != NULL) {
if (style->doctypePublic != NULL)
xmlFree(style->doctypePublic);
style->doctypePublic = prop;
}
prop = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *) "standalone",
NULL);
if (prop != NULL) {
if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
style->standalone = 1;
} else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
style->standalone = 0;
} else {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"invalid value for standalone: %s\n",
prop);
style->warnings++;
}
xmlFree(prop);
}
prop = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *) "indent",
NULL);
if (prop != NULL) {
if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
style->indent = 1;
} else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
style->indent = 0;
} else {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"invalid value for indent: %s\n", prop);
style->warnings++;
}
xmlFree(prop);
}
prop = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *)
"omit-xml-declaration",
NULL);
if (prop != NULL) {
if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
style->omitXmlDeclaration = 1;
} else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
style->omitXmlDeclaration = 0;
} else {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"invalid value for omit-xml-declaration: %s\n",
prop);
style->warnings++;
}
xmlFree(prop);
}
elements = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *)
"cdata-section-elements",
NULL);
if (elements != NULL) {
if (style->stripSpaces == NULL)
style->stripSpaces = xmlHashCreate(10);
if (style->stripSpaces == NULL)
return;
element = elements;
while (*element != 0) {
while (IS_BLANK(*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
xmlHashAddEntry(style->stripSpaces, element,
(xmlChar *) "cdata");
xmlFree(element);
}
element = end;
}
xmlFree(elements);
}
/*
* Create a new document tree and process the element template
*/
XSLT_GET_IMPORT_PTR(method, style, method)
XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
XSLT_GET_IMPORT_PTR(version, style, version)
if ((method != NULL) &&
(!xmlStrEqual(method, (const xmlChar *) "xml"))) {
if (xmlStrEqual(method, (const xmlChar *) "html")) {
ctxt->type = XSLT_OUTPUT_HTML;
if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
res = htmlNewDoc(doctypeSystem, doctypePublic);
else {
if (version == NULL)
version = (const xmlChar *) "4.0";
#ifdef XSLT_GENERATE_HTML_DOCTYPE
xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
#endif
res = htmlNewDoc(doctypeSystem, doctypePublic);
}
if (res == NULL)
goto error;
} else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsltDocumentElem: unsupported method xhtml, using html\n",
style->method);
ctxt->type = XSLT_OUTPUT_HTML;
res = htmlNewDoc(doctypeSystem, doctypePublic);
if (res == NULL)
goto error;
} else if (xmlStrEqual(method, (const xmlChar *) "text")) {
ctxt->type = XSLT_OUTPUT_TEXT;
res = xmlNewDoc(style->version);
if (res == NULL)
goto error;
} else {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsltDocumentElem: unsupported method %s\n",
style->method);
goto error;
}
} else {
ctxt->type = XSLT_OUTPUT_XML;
res = xmlNewDoc(style->version);
if (res == NULL)
goto error;
}
res->charset = XML_CHAR_ENCODING_UTF8;
if (style->encoding != NULL)
res->encoding = xmlStrdup(style->encoding);
ctxt->output = res;
ctxt->insert = (xmlNodePtr) res;
xsltApplyOneTemplate(ctxt, node, inst->children, NULL, NULL);
/*
* Do some post processing work depending on the generated output
*/
root = xmlDocGetRootElement(res);
if (root != NULL) {
/*
* Apply the default selection of the method
*/
if ((method == NULL) &&
(root->ns == NULL) &&
(!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) {
xmlNodePtr tmp;
tmp = res->children;
while ((tmp != NULL) && (tmp != root)) {
if (tmp->type == XML_ELEMENT_NODE)
break;
if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp)))
break;
tmp = tmp->next;
}
if (tmp == root) {
ctxt->type = XSLT_OUTPUT_HTML;
res->type = XML_HTML_DOCUMENT_NODE;
if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
res->intSubset = xmlCreateIntSubset(res, root->name,
doctypePublic,
doctypeSystem);
if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
res = htmlNewDoc(doctypeSystem, doctypePublic);
#ifdef XSLT_GENERATE_HTML_DOCTYPE
} else if (version != NULL) {
xsltGetHTMLIDs(version, &doctypePublic,
&doctypeSystem);
if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
res->intSubset =
xmlCreateIntSubset(res, root->name,
doctypePublic,
doctypeSystem);
#endif
}
}
}
if (ctxt->type == XSLT_OUTPUT_XML) {
XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
res->intSubset = xmlCreateIntSubset(res, root->name,
doctypePublic,
doctypeSystem);
}
}
/*
* Save the result
*/
ret = xsltSaveResultToFilename((const char *) filename,
res, style, 0);
if (ret < 0) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsltDocumentElem: unable to save to %s\n",
filename);
#ifdef WITH_XSLT_DEBUG_EXTRA
} else {
xsltGenericDebug(xsltGenericDebugContext,
"Wrote %d bytes to %s\n", ret, filename);
#endif
}
error:
ctxt->output = oldOutput;
ctxt->insert = oldInsert;
ctxt->type = oldType;
ctxt->outputFile = oldOutputFile;
if (URL != NULL)
xmlFree(URL);
if (filename != NULL)
xmlFree(filename);
if (style != NULL)
xsltFreeStylesheet(style);
if (res != NULL)
xmlFreeDoc(res);
}
/************************************************************************
* *
* Most of the XSLT-1.0 transformations *
* *
************************************************************************/
void xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
xsltStackElemPtr params);
/**
* xsltSort:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the xslt sort node
* @comp: precomputed information
*
* function attached to xsl:sort nodes, but this should not be
* called directly
*/
void
xsltSort(xsltTransformContextPtr ctxt,
xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst,
xsltStylePreCompPtr comp) {
if (comp == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:sort : compilation failed\n");
return;
}
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:sort : improper use this should not be reached\n");
}
/**
* xsltCopy:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the xslt copy node
* @comp: precomputed information
*
* Process the xslt copy node on the source node
*/
void
xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst, xsltStylePreCompPtr comp) {
xmlNodePtr copy, oldInsert;
oldInsert = ctxt->insert;
if (ctxt->insert != NULL) {
switch (node->type) {
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
/*
* This text comes from the stylesheet
* For stylesheets, the set of whitespace-preserving
* element names consists of just xsl:text.
*/
#ifdef WITH_XSLT_DEBUG_PROCESS
if (node->type == XML_CDATA_SECTION_NODE)
xsltGenericDebug(xsltGenericDebugContext,
"xsltCopy: CDATA text %s\n", node->content);
else
xsltGenericDebug(xsltGenericDebugContext,
"xsltCopy: text %s\n", node->content);
#endif
xsltCopyText(ctxt, ctxt->insert, node);
break;
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
break;
case XML_ELEMENT_NODE:
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltCopy: node %s\n", node->name);
#endif
copy = xsltCopyNode(ctxt, node, ctxt->insert);
ctxt->insert = copy;
if (comp->use != NULL) {
xsltApplyAttributeSet(ctxt, node, inst, comp->use);
}
break;
case XML_ATTRIBUTE_NODE: {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltCopy: attribute %s\n", node->name);
#endif
if (ctxt->insert->type == XML_ELEMENT_NODE) {
xmlAttrPtr attr = (xmlAttrPtr) node, ret = NULL, cur;
if (attr->ns != NULL) {
if ((!xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) &&
(xmlStrncasecmp(attr->ns->prefix,
(xmlChar *)"xml", 3))) {
ret = xmlCopyProp(ctxt->insert, attr);
ret->ns = xsltGetNamespace(ctxt, node, attr->ns,
ctxt->insert);
}
} else
ret = xmlCopyProp(ctxt->insert, attr);
if (ret != NULL) {
cur = ctxt->insert->properties;
if (cur != NULL) {
while (cur->next != NULL)
cur = cur->next;
cur->next = ret;
ret->prev = cur;
} else
ctxt->insert->properties = ret;
}
}
break;
}
case XML_PI_NODE:
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltCopy: PI %s\n", node->name);
#endif
copy = xmlNewPI(node->name, node->content);
xmlAddChild(ctxt->insert, copy);
break;
case XML_COMMENT_NODE:
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltCopy: comment\n");
#endif
copy = xmlNewComment(node->content);
xmlAddChild(ctxt->insert, copy);
break;
default:
break;
}
}
switch (node->type) {
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
case XML_ELEMENT_NODE:
xsltApplyOneTemplate(ctxt, ctxt->node, inst->children,
NULL, NULL);
break;
default:
break;
}
ctxt->insert = oldInsert;
}
/**
* xsltText:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the xslt text node
* @comp: precomputed information
*
* Process the xslt text node on the source node
*/
void
xsltText(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED,
xmlNodePtr inst, xsltStylePreCompPtr comp) {
if ((inst->children != NULL) && (comp != NULL)) {
xmlNodePtr text = inst->children;
xmlNodePtr copy;
while (text != NULL) {
if ((text->type != XML_TEXT_NODE) &&
(text->type != XML_CDATA_SECTION_NODE)) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:text content problem\n");
break;
}
copy = xmlNewDocText(ctxt->output, text->content);
if ((comp->noescape) || (text->type != XML_CDATA_SECTION_NODE)) {
#ifdef WITH_XSLT_DEBUG_PARSING
xsltGenericDebug(xsltGenericDebugContext,
"Disable escaping: %s\n", text->content);
#endif
copy->name = xmlStringTextNoenc;
}
xmlAddChild(ctxt->insert, copy);
text = text->next;
}
}
}
/**
* xsltElement:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the xslt element node
* @comp: precomputed information
*
* Process the xslt element node on the source node
*/
void
xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst, xsltStylePreCompPtr comp) {
xmlChar *prop = NULL, *attributes = NULL;
xmlChar *ncname = NULL, *name, *namespace;
xmlChar *prefix = NULL;
xmlChar *value = NULL;
xmlNsPtr ns = NULL, oldns = NULL;
xmlNodePtr copy;
xmlNodePtr oldInsert;
if (ctxt->insert == NULL)
return;
if (!comp->has_name) {
return;
}
/*
* stack and saves
*/
oldInsert = ctxt->insert;
if (comp->name == NULL) {
prop = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *)"name", XSLT_NAMESPACE);
if (prop == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:element : name is missing\n");
goto error;
}
name = prop;
} else {
name = comp->name;
}
ncname = xmlSplitQName2(name, &prefix);
if (ncname == NULL) {
prefix = NULL;
} else {
name = ncname;
}
if ((comp->ns == NULL) && (comp->has_ns)) {
namespace = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *)"namespace", XSLT_NAMESPACE);
if (namespace != NULL) {
ns = xsltGetSpecialNamespace(ctxt, inst, namespace, prefix,
ctxt->insert);
xmlFree(namespace);
}
} else if (comp->ns != NULL) {
ns = xsltGetSpecialNamespace(ctxt, inst, comp->ns, prefix,
ctxt->insert);
}
if ((ns == NULL) && (prefix != NULL)) {
if (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3)) {
#ifdef WITH_XSLT_DEBUG_PARSING
xsltGenericDebug(xsltGenericDebugContext,
"xsltElement: xml prefix forbidden\n");
#endif
goto error;
}
oldns = xmlSearchNs(inst->doc, inst, prefix);
if (oldns == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:element : no namespace bound to prefix %s\n", prefix);
} else {
ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
}
}
copy = xmlNewDocNode(ctxt->output, ns, name, NULL);
if (copy == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:element : creation of %s failed\n", name);
goto error;
}
if ((ns == NULL) && (oldns != NULL)) {
/* very specific case xsltGetNamespace failed */
ns = xmlNewNs(copy, oldns->href, oldns->prefix);
copy->ns = ns;
}
xmlAddChild(ctxt->insert, copy);
ctxt->insert = copy;
if (comp->has_use) {
if (comp->use != NULL) {
xsltApplyAttributeSet(ctxt, node, inst, comp->use);
} else {
attributes = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *)"use-attribute-sets", XSLT_NAMESPACE);
if (attributes != NULL) {
xsltApplyAttributeSet(ctxt, node, inst, attributes);
xmlFree(attributes);
}
}
}
xsltApplyOneTemplate(ctxt, ctxt->node, inst->children, NULL, NULL);
ctxt->insert = oldInsert;
error:
if (prop != NULL)
xmlFree(prop);
if (ncname != NULL)
xmlFree(ncname);
if (prefix != NULL)
xmlFree(prefix);
if (value != NULL)
xmlFree(value);
}
/**
* xsltComment:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the xslt comment node
* @comp: precomputed information
*
* Process the xslt comment node on the source node
*/
void
xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
xmlChar *value = NULL;
xmlNodePtr commentNode;
value = xsltEvalTemplateString(ctxt, node, inst);
/* TODO: use or generate the compiled form */
/* TODO: check that there is no -- sequence and doesn't end up with - */
#ifdef WITH_XSLT_DEBUG_PROCESS
if (value == NULL)
xsltGenericDebug(xsltGenericDebugContext,
"xsltComment: empty\n");
else
xsltGenericDebug(xsltGenericDebugContext,
"xsltComment: content %s\n", value);
#endif
commentNode = xmlNewComment(value);
xmlAddChild(ctxt->insert, commentNode);
if (value != NULL)
xmlFree(value);
}
/**
* xsltProcessingInstruction:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the xslt processing-instruction node
* @comp: precomputed information
*
* Process the xslt processing-instruction node on the source node
*/
void
xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst, xsltStylePreCompPtr comp) {
xmlChar *ncname = NULL, *name;
xmlChar *value = NULL;
xmlNodePtr pi;
if (ctxt->insert == NULL)
return;
if (comp->has_name == 0)
return;
if (comp->name == NULL) {
ncname = xsltEvalAttrValueTemplate(ctxt, inst,
(const xmlChar *)"name", XSLT_NAMESPACE);
if (ncname == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:processing-instruction : name is missing\n");
goto error;
}
name = ncname;
} else {
name = comp->name;
}
/* TODO: check that it's both an an NCName and a PITarget. */
value = xsltEvalTemplateString(ctxt, node, inst);
/* TODO: check that there is no ?> sequence */
#ifdef WITH_XSLT_DEBUG_PROCESS
if (value == NULL)
xsltGenericDebug(xsltGenericDebugContext,
"xsltProcessingInstruction: %s empty\n", ncname);
else
xsltGenericDebug(xsltGenericDebugContext,
"xsltProcessingInstruction: %s content %s\n", ncname, value);
#endif
pi = xmlNewPI(name, value);
xmlAddChild(ctxt->insert, pi);
error:
if (ncname != NULL)
xmlFree(ncname);
if (value != NULL)
xmlFree(value);
}
/**
* xsltCopyOf:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the xslt copy-of node
* @comp: precomputed information
*
* Process the xslt copy-of node on the source node
*/
void
xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst, xsltStylePreCompPtr comp) {
xmlXPathObjectPtr res = NULL;
xmlNodeSetPtr list = NULL;
int i;
int oldProximityPosition, oldContextSize;
int oldNsNr;
xmlNsPtr *oldNamespaces;
if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
return;
if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:copy-of : compilation failed\n");
return;
}
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltCopyOf: select %s\n", comp->select);
#endif
oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
oldContextSize = ctxt->xpathCtxt->contextSize;
oldNsNr = ctxt->xpathCtxt->nsNr;
oldNamespaces = ctxt->xpathCtxt->namespaces;
ctxt->xpathCtxt->node = node;
ctxt->xpathCtxt->namespaces = comp->nsList;
ctxt->xpathCtxt->nsNr = comp->nsNr;
res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
ctxt->xpathCtxt->contextSize = oldContextSize;
ctxt->xpathCtxt->nsNr = oldNsNr;
ctxt->xpathCtxt->namespaces = oldNamespaces;
if (res != NULL) {
if (res->type == XPATH_NODESET) {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltCopyOf: result is a node set\n");
#endif
list = res->nodesetval;
if (list != NULL) {
/* sort the list in document order */
xsltDocumentSortFunction(list);
/* append everything in this order under ctxt->insert */
for (i = 0;i < list->nodeNr;i++) {
if (list->nodeTab[i] == NULL)
continue;
if ((list->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
(list->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE)) {
xsltCopyTreeList(ctxt, list->nodeTab[i]->children,
ctxt->insert);
} else if (list->nodeTab[i]->type == XML_ATTRIBUTE_NODE) {
xsltCopyProp(ctxt, ctxt->insert,
(xmlAttrPtr) list->nodeTab[i]);
} else {
xsltCopyTree(ctxt, list->nodeTab[i], ctxt->insert);
}
}
}
} else if (res->type == XPATH_XSLT_TREE) {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltCopyOf: result is a result tree fragment\n");
#endif
list = res->nodesetval;
if ((list != NULL) && (list->nodeTab != NULL) &&
(list->nodeTab[0] != NULL) &&
(IS_XSLT_REAL_NODE(list->nodeTab[0]))) {
xsltCopyTreeList(ctxt, list->nodeTab[0]->children,
ctxt->insert);
}
} else {
/* convert to a string */
res = xmlXPathConvertString(res);
if ((res != NULL) && (res->type == XPATH_STRING)) {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltCopyOf: result %s\n", res->stringval);
#endif
/* append content as text node */
xsltCopyTextString(ctxt, ctxt->insert, res->stringval);
}
}
} else {
ctxt->state = XSLT_STATE_STOPPED;
}
if (res != NULL)
xmlXPathFreeObject(res);
}
/**
* xsltValueOf:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the xslt value-of node
* @comp: precomputed information
*
* Process the xslt value-of node on the source node
*/
void
xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst, xsltStylePreCompPtr comp) {
xmlXPathObjectPtr res = NULL;
xmlNodePtr copy = NULL;
int oldProximityPosition, oldContextSize;
int oldNsNr;
xmlNsPtr *oldNamespaces;
if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
return;
if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:value-of : compilation failed\n");
return;
}
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltValueOf: select %s\n", comp->select);
#endif
oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
oldContextSize = ctxt->xpathCtxt->contextSize;
oldNsNr = ctxt->xpathCtxt->nsNr;
oldNamespaces = ctxt->xpathCtxt->namespaces;
ctxt->xpathCtxt->node = node;
ctxt->xpathCtxt->namespaces = comp->nsList;
ctxt->xpathCtxt->nsNr = comp->nsNr;
res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
ctxt->xpathCtxt->contextSize = oldContextSize;
ctxt->xpathCtxt->nsNr = oldNsNr;
ctxt->xpathCtxt->namespaces = oldNamespaces;
if (res != NULL) {
if (res->type != XPATH_STRING)
res = xmlXPathConvertString(res);
if (res->type == XPATH_STRING) {
/* TODO: integrate with xsltCopyTextString */
copy = xmlNewText(res->stringval);
if (copy != NULL) {
if (comp->noescape)
copy->name = xmlStringTextNoenc;
xmlAddChild(ctxt->insert, copy);
}
}
} else {
ctxt->state = XSLT_STATE_STOPPED;
}
if (copy == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsltValueOf: text copy failed\n");
}
#ifdef WITH_XSLT_DEBUG_PROCESS
else
xsltGenericDebug(xsltGenericDebugContext,
"xsltValueOf: result %s\n", res->stringval);
#endif
if (res != NULL)
xmlXPathFreeObject(res);
}
/**
* xsltNumber:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the xslt number node
* @comp: precomputed information
*
* Process the xslt number node on the source node
*/
void
xsltNumber(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst, xsltStylePreCompPtr comp)
{
if (comp == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:number : compilation failed\n");
return;
}
if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
return;
comp->numdata.doc = inst->doc;
comp->numdata.node = inst;
xsltNumberFormat(ctxt, &comp->numdata, node);
}
/**
* xsltApplyImports:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the xslt apply-imports node
* @comp: precomputed information
*
* Process the xslt apply-imports node on the source node
*/
void
xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst,
xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
xsltTemplatePtr template;
if ((ctxt->templ == NULL) || (ctxt->templ->style == NULL)) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:apply-imports : internal error no current template\n");
return;
}
template = xsltGetTemplate(ctxt, node, ctxt->templ->style);
if (template != NULL) {
xsltApplyOneTemplate(ctxt, node, template->content, template, NULL);
}
}
/**
* xsltCallTemplate:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the xslt call-template node
* @comp: precomputed information
*
* Process the xslt call-template node on the source node
*/
void
xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst, xsltStylePreCompPtr comp) {
xmlNodePtr cur = NULL;
xsltStackElemPtr params = NULL, param;
if (ctxt->insert == NULL)
return;
if (comp == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:call-template : compilation failed\n");
return;
}
/*
* The template must have been precomputed
*/
if (comp->templ == NULL) {
comp->templ = xsltFindTemplate(ctxt, comp->name, comp->ns);
if (comp->templ == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:call-template : template %s not found\n", comp->name);
return;
}
}
#ifdef WITH_XSLT_DEBUG_PROCESS
if ((comp != NULL) && (comp->name != NULL))
xsltGenericDebug(xsltGenericDebugContext,
"call-template: name %s\n", comp->name);
#endif
cur = inst->children;
while (cur != NULL) {
#ifdef WITH_DEBUGGER
if (xslDebugStatus != XSLT_DEBUG_NONE)
xslHandleDebugger(cur, node, comp->templ, ctxt);
#endif
if (ctxt->state == XSLT_STATE_STOPPED) break;
if (IS_XSLT_ELEM(cur)) {
if (IS_XSLT_NAME(cur, "with-param")) {
param = xsltParseStylesheetCallerParam(ctxt, cur);
if (param != NULL) {
param->next = params;
params = param;
}
} else {
xsltGenericError(xsltGenericDebugContext,
"xsl:call-template: misplaced xsl:%s\n", cur->name);
}
} else {
xsltGenericError(xsltGenericDebugContext,
"xsl:call-template: misplaced %s element\n", cur->name);
}
cur = cur->next;
}
/*
* Create a new frame using the params first
*/
xsltApplyOneTemplate(ctxt, node, comp->templ->content, comp->templ, params);
if (params != NULL)
xsltFreeStackElemList(params);
#ifdef WITH_XSLT_DEBUG_PROCESS
if ((comp != NULL) && (comp->name != NULL))
xsltGenericDebug(xsltGenericDebugContext,
"call-template returned: name %s\n", comp->name);
#endif
}
/**
* xsltApplyTemplates:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the apply-templates node
* @comp: precomputed information
*
* Process the apply-templates node on the source node
*/
void
xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst, xsltStylePreCompPtr comp) {
xmlNodePtr cur, delete = NULL, oldNode;
xmlXPathObjectPtr res = NULL;
xmlNodeSetPtr list = NULL, oldList;
int i, oldProximityPosition, oldContextSize;
const xmlChar *oldMode, *oldModeURI;
xsltStackElemPtr params = NULL, param;
int nbsorts = 0;
xmlNodePtr sorts[XSLT_MAX_SORT];
xmlDocPtr oldXDocPtr;
xsltDocumentPtr oldCDocPtr;
int oldNsNr;
xmlNsPtr *oldNamespaces;
if (comp == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:apply-templates : compilation failed\n");
return;
}
if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
return;
#ifdef WITH_XSLT_DEBUG_PROCESS
if ((node != NULL) && (node->name != NULL))
xsltGenericDebug(xsltGenericDebugContext,
"xsltApplyTemplates: node: %s\n", node->name);
#endif
/*
* Get mode if any
*/
oldNode = ctxt->node;
oldMode = ctxt->mode;
oldModeURI = ctxt->modeURI;
ctxt->mode = comp->mode;
ctxt->modeURI = comp->modeURI;
/*
* The xpath context size and proximity position, as
* well as the xpath and context documents, may be changed
* so we save their initial state and will restore on exit
*/
oldXDocPtr = ctxt->xpathCtxt->doc;
oldCDocPtr = ctxt->document;
oldContextSize = ctxt->xpathCtxt->contextSize;
oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
oldNsNr = ctxt->xpathCtxt->nsNr;
oldNamespaces = ctxt->xpathCtxt->namespaces;
oldList = ctxt->nodeList;
if (comp->select != NULL) {
if (comp->comp == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:apply-templates : compilation failed\n");
goto error;
}
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltApplyTemplates: select %s\n", comp->select);
#endif
ctxt->xpathCtxt->node = node;
ctxt->xpathCtxt->namespaces = comp->nsList;
ctxt->xpathCtxt->nsNr = comp->nsNr;
res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
ctxt->xpathCtxt->contextSize = oldContextSize;
ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
if (res != NULL) {
if (res->type == XPATH_NODESET) {
list = res->nodesetval;
res->nodesetval = NULL;
} else {
list = NULL;
}
} else {
ctxt->state = XSLT_STATE_STOPPED;
}
if (list == NULL) {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltApplyTemplates: select didn't evaluate to a node list\n");
#endif
goto error;
}
} else {
/*
* Build an XPath nodelist with the children
*/
list = xmlXPathNodeSetCreate(NULL);
cur = node->children;
while (cur != NULL) {
switch (cur->type) {
case XML_TEXT_NODE:
if ((IS_BLANK_NODE(cur)) &&
(cur->parent != NULL) &&
(ctxt->style->stripSpaces != NULL)) {
const xmlChar *val;
val = (const xmlChar *)
xmlHashLookup(ctxt->style->stripSpaces,
cur->parent->name);
if ((val != NULL) &&
(xmlStrEqual(val, (xmlChar *) "strip"))) {
delete = cur;
break;
}
}
/* no break on purpose */
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
case XML_ELEMENT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
xmlXPathNodeSetAdd(list, cur);
break;
default:
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltApplyTemplates: skipping cur type %d\n",
cur->type);
#endif
delete = cur;
}
cur = cur->next;
if (delete != NULL) {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltApplyTemplates: removing ignorable blank cur\n");
#endif
xmlUnlinkNode(delete);
xmlFreeNode(delete);
delete = NULL;
}
}
}
#ifdef WITH_XSLT_DEBUG_PROCESS
if (list != NULL)
xsltGenericDebug(xsltGenericDebugContext,
"xsltApplyTemplates: list of %d nodes\n", list->nodeNr);
#endif
ctxt->nodeList = list;
ctxt->xpathCtxt->contextSize = list->nodeNr;
/*
* handle (or skip) the xsl:sort and xsl:with-param
*/
cur = inst->children;
while (cur!=NULL) {
#ifdef WITH_DEBUGGER
if (xslDebugStatus != XSLT_DEBUG_NONE)
xslHandleDebugger(cur, node, comp->templ, ctxt);
#endif
if (ctxt->state == XSLT_STATE_STOPPED) break;
if (IS_XSLT_ELEM(cur)) {
if (IS_XSLT_NAME(cur, "with-param")) {
param = xsltParseStylesheetCallerParam(ctxt, cur);
if (param != NULL) {
param->next = params;
params = param;
}
} else if (IS_XSLT_NAME(cur, "sort")) {
if (nbsorts >= XSLT_MAX_SORT) {
xsltGenericError(xsltGenericDebugContext,
"xsl:apply-template: %s too many sort\n", node->name);
} else {
sorts[nbsorts++] = cur;
}
} else {
xsltGenericError(xsltGenericDebugContext,
"xsl:apply-template: misplaced xsl:%s\n", cur->name);
}
} else {
xsltGenericError(xsltGenericDebugContext,
"xsl:apply-template: misplaced %s element\n", cur->name);
}
cur = cur->next;
}
if (nbsorts > 0) {
xsltDoSortFunction(ctxt, sorts, nbsorts);
}
for (i = 0;i < list->nodeNr;i++) {
ctxt->node = list->nodeTab[i];
ctxt->xpathCtxt->proximityPosition = i + 1;
/* For a 'select' nodeset, need to check if document has changed */
if ((IS_XSLT_REAL_NODE(list->nodeTab[i])) &&
(list->nodeTab[i]->doc!=NULL) &&
(list->nodeTab[i]->doc->doc!=NULL) &&
(list->nodeTab[i]->doc->doc)!=ctxt->xpathCtxt->doc) {
/* The nodeset is from another document, so must change */
ctxt->xpathCtxt->doc=list->nodeTab[i]->doc->doc;
if ((ctxt->document =
xsltFindDocument(ctxt,list->nodeTab[i]->doc->doc))==NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:apply-templates : can't find doc\n");
goto error;
}
ctxt->xpathCtxt->node = list->nodeTab[i];
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltApplyTemplates: Changing document - context doc %s, xpathdoc %s\n",
ctxt->document->doc->URL, ctxt->xpathCtxt->doc->URL);
#endif
}
xsltProcessOneNode(ctxt, list->nodeTab[i], params);
}
if (params != NULL)
xsltFreeStackElemList(params); /* free the parameter list */
error:
ctxt->nodeList = oldList;
ctxt->xpathCtxt->contextSize = oldContextSize;
ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
ctxt->xpathCtxt->doc = oldXDocPtr;
ctxt->document = oldCDocPtr;
ctxt->xpathCtxt->nsNr = oldNsNr;
ctxt->xpathCtxt->namespaces = oldNamespaces;
ctxt->node = oldNode;
ctxt->mode = oldMode;
ctxt->modeURI = oldModeURI;
if (res != NULL)
xmlXPathFreeObject(res);
if (list != NULL)
xmlXPathFreeNodeSet(list);
}
/**
* xsltChoose:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the xslt choose node
* @comp: precomputed information
*
* Process the xslt choose node on the source node
*/
void
xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
xmlChar *prop = NULL;
xmlXPathObjectPtr res = NULL;
xmlNodePtr replacement, when;
int doit = 1;
int oldProximityPosition, oldContextSize;
int oldNsNr;
xmlNsPtr *oldNamespaces;
if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
return;
/*
* Check the when's
*/
replacement = inst->children;
if (replacement == NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:choose: empty content not allowed\n");
goto error;
}
if ((!IS_XSLT_ELEM(replacement)) ||
(!IS_XSLT_NAME(replacement, "when"))) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:choose: xsl:when expected first\n");
goto error;
}
while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "when"))) {
xsltStylePreCompPtr wcomp = replacement->_private;
if ((wcomp == NULL) || (wcomp->test == NULL) || (wcomp->comp == NULL)) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:choose: compilation failed !\n");
goto error;
}
when = replacement;
#ifdef WITH_DEBUGGER
if (xslDebugStatus != XSLT_DEBUG_NONE)
xslHandleDebugger(when, node, comp->templ, ctxt);
#endif
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltChoose: test %s\n", wcomp->test);
#endif
oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
oldContextSize = ctxt->xpathCtxt->contextSize;
oldNsNr = ctxt->xpathCtxt->nsNr;
oldNamespaces = ctxt->xpathCtxt->namespaces;
ctxt->xpathCtxt->node = node;
ctxt->xpathCtxt->namespaces = comp->nsList;
ctxt->xpathCtxt->nsNr = comp->nsNr;
res = xmlXPathCompiledEval(wcomp->comp, ctxt->xpathCtxt);
ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
ctxt->xpathCtxt->contextSize = oldContextSize;
ctxt->xpathCtxt->nsNr = oldNsNr;
ctxt->xpathCtxt->namespaces = oldNamespaces;
if (res != NULL) {
if (res->type != XPATH_BOOLEAN)
res = xmlXPathConvertBoolean(res);
if (res->type == XPATH_BOOLEAN)
doit = res->boolval;
else {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltChoose: test didn't evaluate to a boolean\n");
#endif
goto error;
}
} else {
ctxt->state = XSLT_STATE_STOPPED;
}
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltChoose: test evaluate to %d\n", doit);
#endif
if (doit) {
xsltApplyOneTemplate(ctxt, ctxt->node, when->children,
NULL, NULL);
goto done;
}
if (prop != NULL)
xmlFree(prop);
prop = NULL;
if (res != NULL)
xmlXPathFreeObject(res);
res = NULL;
replacement = replacement->next;
}
if (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "otherwise"))) {
#ifdef WITH_DEBUGGER
if (xslDebugStatus != XSLT_DEBUG_NONE)
xslHandleDebugger(replacement, node, comp->templ, ctxt);
#endif
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"evaluating xsl:otherwise\n");
#endif
xsltApplyOneTemplate(ctxt, ctxt->node, replacement->children,
NULL, NULL);
replacement = replacement->next;
}
if (replacement != NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:choose: unexpected content %s\n", replacement->name);
goto error;
}
done:
error:
if (prop != NULL)
xmlFree(prop);
if (res != NULL)
xmlXPathFreeObject(res);
}
/**
* xsltIf:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the xslt if node
* @comp: precomputed information
*
* Process the xslt if node on the source node
*/
void
xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst, xsltStylePreCompPtr comp) {
xmlXPathObjectPtr res = NULL;
int doit = 1;
int oldContextSize, oldProximityPosition;
int oldNsNr;
xmlNsPtr *oldNamespaces;
if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
return;
if ((comp == NULL) || (comp->test == NULL) || (comp->comp == NULL)) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:if : compilation failed\n");
return;
}
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltIf: test %s\n", comp->test);
#endif
oldContextSize = ctxt->xpathCtxt->contextSize;
oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
oldNsNr = ctxt->xpathCtxt->nsNr;
oldNamespaces = ctxt->xpathCtxt->namespaces;
ctxt->xpathCtxt->node = node;
ctxt->xpathCtxt->namespaces = comp->nsList;
ctxt->xpathCtxt->nsNr = comp->nsNr;
res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
ctxt->xpathCtxt->contextSize = oldContextSize;
ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
ctxt->xpathCtxt->nsNr = oldNsNr;
ctxt->xpathCtxt->namespaces = oldNamespaces;
if (res != NULL) {
if (res->type != XPATH_BOOLEAN)
res = xmlXPathConvertBoolean(res);
if (res->type == XPATH_BOOLEAN)
doit = res->boolval;
else {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltIf: test didn't evaluate to a boolean\n");
#endif
goto error;
}
} else {
ctxt->state = XSLT_STATE_STOPPED;
}
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltIf: test evaluate to %d\n", doit);
#endif
if (doit) {
xsltApplyOneTemplate(ctxt, node, inst->children, NULL, NULL);
}
error:
if (res != NULL)
xmlXPathFreeObject(res);
}
/**
* xsltForEach:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @inst: the xslt for-each node
* @comp: precomputed information
*
* Process the xslt for-each node on the source node
*/
void
xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node,
xmlNodePtr inst, xsltStylePreCompPtr comp) {
xmlXPathObjectPtr res = NULL;
xmlNodePtr replacement;
xmlNodeSetPtr list = NULL, oldList;
int i, oldProximityPosition, oldContextSize;
xmlNodePtr oldNode = ctxt->node;
int nbsorts = 0;
xmlNodePtr sorts[XSLT_MAX_SORT];
xmlDocPtr oldXDocPtr;
xsltDocumentPtr oldCDocPtr;
int oldNsNr;
xmlNsPtr *oldNamespaces;
if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
return;
if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:for-each : compilation failed\n");
return;
}
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltForEach: select %s\n", comp->select);
#endif
oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
oldContextSize = ctxt->xpathCtxt->contextSize;
oldNsNr = ctxt->xpathCtxt->nsNr;
oldNamespaces = ctxt->xpathCtxt->namespaces;
ctxt->xpathCtxt->node = node;
ctxt->xpathCtxt->namespaces = comp->nsList;
ctxt->xpathCtxt->nsNr = comp->nsNr;
oldCDocPtr = ctxt->document;
oldXDocPtr = ctxt->xpathCtxt->doc;
res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
ctxt->xpathCtxt->contextSize = oldContextSize;
ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
ctxt->xpathCtxt->nsNr = oldNsNr;
ctxt->xpathCtxt->namespaces = oldNamespaces;
if (res != NULL) {
if (res->type == XPATH_NODESET)
list = res->nodesetval;
} else {
ctxt->state = XSLT_STATE_STOPPED;
}
if (list == NULL) {
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltForEach: select didn't evaluate to a node list\n");
#endif
goto error;
}
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltForEach: select evaluates to %d nodes\n", list->nodeNr);
#endif
oldList = ctxt->nodeList;
ctxt->nodeList = list;
oldContextSize = ctxt->xpathCtxt->contextSize;
oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
ctxt->xpathCtxt->contextSize = list->nodeNr;
/*
* handle and skip the xsl:sort
*/
replacement = inst->children;
while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "sort"))) {
if (nbsorts >= XSLT_MAX_SORT) {
xsltGenericError(xsltGenericDebugContext,
"xsl:for-each: too many sorts\n");
} else {
sorts[nbsorts++] = replacement;
}
#ifdef WITH_DEBUGGER
if (xslDebugStatus != XSLT_DEBUG_NONE)
xslHandleDebugger(replacement, node, NULL, ctxt);
#endif
replacement = replacement->next;
}
if (nbsorts > 0) {
xsltDoSortFunction(ctxt, sorts, nbsorts);
}
for (i = 0;i < list->nodeNr;i++) {
ctxt->node = list->nodeTab[i];
ctxt->xpathCtxt->proximityPosition = i + 1;
/* For a 'select' nodeset, need to check if document has changed */
if ((IS_XSLT_REAL_NODE(list->nodeTab[i])) &&
(list->nodeTab[i]->doc!=NULL) &&
(list->nodeTab[i]->doc->doc!=NULL) &&
(list->nodeTab[i]->doc->doc)!=ctxt->xpathCtxt->doc) {
/* The nodeset is from another document, so must change */
ctxt->xpathCtxt->doc=list->nodeTab[i]->doc->doc;
if ((ctxt->document =
xsltFindDocument(ctxt,list->nodeTab[i]->doc->doc))==NULL) {
xsltPrintErrorContext(ctxt, NULL, inst);
xsltGenericError(xsltGenericErrorContext,
"xsl:for-each : can't find doc\n");
goto error;
}
ctxt->xpathCtxt->node = list->nodeTab[i];
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltForEach: Changing document - context doc %s, xpathdoc %s\n",
ctxt->document->doc->URL, ctxt->xpathCtxt->doc->URL);
#endif
}
xsltApplyOneTemplate(ctxt, list->nodeTab[i], replacement, NULL, NULL);
}
ctxt->document = oldCDocPtr;
ctxt->nodeList = oldList;
ctxt->node = oldNode;
ctxt->xpathCtxt->doc = oldXDocPtr;
ctxt->xpathCtxt->contextSize = oldContextSize;
ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
ctxt->xpathCtxt->nsNr = oldNsNr;
ctxt->xpathCtxt->namespaces = oldNamespaces;
error:
if (res != NULL)
xmlXPathFreeObject(res);
}
/************************************************************************
* *
* Generic interface *
* *
************************************************************************/
#ifdef XSLT_GENERATE_HTML_DOCTYPE
typedef struct xsltHTMLVersion {
const char *version;
const char *public;
const char *system;
} xsltHTMLVersion;
static xsltHTMLVersion xsltHTMLVersions[] = {
{ "4.01frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
"http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd"},
{ "4.01strict", "-//W3C//DTD HTML 4.01//EN",
"http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"},
{ "4.01trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
{ "4.01", "-//W3C//DTD HTML 4.01 Transitional//EN",
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
{ "4.0strict", "-//W3C//DTD HTML 4.01//EN",
"http://www.w3.org/TR/html4/strict.dtd"},
{ "4.0trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
"http://www.w3.org/TR/html4/loose.dtd"},
{ "4.0frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
"http://www.w3.org/TR/html4/frameset.dtd"},
{ "4.0", "-//W3C//DTD HTML 4.01 Transitional//EN",
"http://www.w3.org/TR/html4/loose.dtd"},
{ "3.2", "-//W3C//DTD HTML 3.2//EN", NULL }
};
/**
* xsltGetHTMLIDs:
* @version: the version string
* @publicID: used to return the public ID
* @systemID: used to return the system ID
*
* Returns -1 if not found, 0 otherwise and the system and public
* Identifier for this given verion of HTML
*/
static int
xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID,
const xmlChar **systemID) {
unsigned int i;
if (version == NULL)
return(-1);
for (i = 0;i < (sizeof(xsltHTMLVersions)/sizeof(xsltHTMLVersions[1]));
i++) {
if (!xmlStrcasecmp(version,
(const xmlChar *) xsltHTMLVersions[i].version)) {
if (publicID != NULL)
*publicID = (const xmlChar *) xsltHTMLVersions[i].public;
if (systemID != NULL)
*systemID = (const xmlChar *) xsltHTMLVersions[i].system;
return(0);
}
}
return(-1);
}
#endif
/**
* xsltApplyStripSpaces:
* @ctxt: a XSLT process context
* @root: the root of the XML tree
*
* Strip the unwanted ignorable spaces from the input tree
*/
void
xsltApplyStripSpaces(xsltTransformContextPtr ctxt, xmlNodePtr node) {
xmlNodePtr current;
#ifdef WITH_XSLT_DEBUG_PROCESS
int nb = 0;
#endif
current = node;
while (current != NULL) {
/*
* Cleanup children empty nodes if asked for
*/
if ((IS_XSLT_REAL_NODE(current)) &&
(current->children != NULL) &&
(xsltFindElemSpaceHandling(ctxt, current))) {
xmlNodePtr delete = NULL, cur = current->children;
while (cur != NULL) {
if (IS_BLANK_NODE(cur))
delete = cur;
cur = cur->next;
if (delete != NULL) {
xmlUnlinkNode(delete);
xmlFreeNode(delete);
delete = NULL;
#ifdef WITH_XSLT_DEBUG_PROCESS
nb++;
#endif
}
}
}
/*
* Skip to next node in document order.
*/
if (node->type == XML_ENTITY_REF_NODE) {
/* process deep in entities */
xsltApplyStripSpaces(ctxt, node->children);
}
if ((current->children != NULL) &&
(current->type != XML_ENTITY_REF_NODE)) {
current = current->children;
} else if (current->next != NULL) {
current = current->next;
} else {
do {
current = current->parent;
if (current == NULL)
break;
if (current == node)
goto done;
if (current->next != NULL) {
current = current->next;
break;
}
} while (current != NULL);
}
}
done:
#ifdef WITH_XSLT_DEBUG_PROCESS
xsltGenericDebug(xsltGenericDebugContext,
"xsltApplyStripSpaces: removed %d ignorable blank node\n", nb);
#endif
}
/**
* xsltApplyStylesheetInternal:
* @style: a parsed XSLT stylesheet
* @doc: a parsed XML document
* @params: a NULL terminated arry of parameters names/values tuples
* @output: the targetted output
* @profile: profile FILE * output or NULL
* @user: user provided parameter
*
* Apply the stylesheet to the document
* NOTE: This may lead to a non-wellformed output XML wise !
*
* Returns the result document or NULL in case of error
*/
static xmlDocPtr
xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc,
const char **params, const char *output,
FILE * profile, xsltTransformContextPtr userCtxt)
{
xmlDocPtr res = NULL;
xsltTransformContextPtr ctxt = NULL;
xmlNodePtr root;
const xmlChar *method;
const xmlChar *doctypePublic;
const xmlChar *doctypeSystem;
const xmlChar *version;
xsltStackElemPtr variables;
xsltStackElemPtr vptr;
if ((style == NULL) || (doc == NULL))
return (NULL);
if (userCtxt != NULL)
ctxt = userCtxt;
else
ctxt = xsltNewTransformContext(style, doc);
if (ctxt == NULL)
return (NULL);
if (profile != NULL)
ctxt->profile = 1;
if (output != NULL)
ctxt->outputFile = output;
else
ctxt->outputFile = NULL;
XSLT_GET_IMPORT_PTR(method, style, method)
XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
XSLT_GET_IMPORT_PTR(version, style, version)
if ((method != NULL) &&
(!xmlStrEqual(method, (const xmlChar *) "xml"))) {
if (xmlStrEqual(method, (const xmlChar *) "html")) {
ctxt->type = XSLT_OUTPUT_HTML;
if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
res = htmlNewDoc(doctypeSystem, doctypePublic);
} else {
if (version == NULL) {
xmlDtdPtr dtd;
res = htmlNewDoc(NULL, NULL);
/*
* Make sure no DTD node is generated in this case
*/
if (res != NULL) {
dtd = xmlGetIntSubset(res);
if (dtd != NULL) {
xmlUnlinkNode((xmlNodePtr) dtd);
xmlFreeDtd(dtd);
}
res->intSubset = NULL;
res->extSubset = NULL;
}
} else {
#ifdef XSLT_GENERATE_HTML_DOCTYPE
xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
#endif
res = htmlNewDoc(doctypeSystem, doctypePublic);
}
}
if (res == NULL)
goto error;
} else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
xsltPrintErrorContext(ctxt, NULL, (xmlNodePtr) doc);
xsltGenericError(xsltGenericErrorContext,
"xsltApplyStylesheetInternal: unsupported method xhtml, using html\n",
style->method);
ctxt->type = XSLT_OUTPUT_HTML;
res = htmlNewDoc(doctypeSystem, doctypePublic);
if (res == NULL)
goto error;
} else if (xmlStrEqual(method, (const xmlChar *) "text")) {
ctxt->type = XSLT_OUTPUT_TEXT;
res = xmlNewDoc(style->version);
if (res == NULL)
goto error;
} else {
xsltPrintErrorContext(ctxt, NULL, (xmlNodePtr) doc);
xsltGenericError(xsltGenericErrorContext,
"xsltApplyStylesheetInternal: unsupported method %s\n",
style->method);
goto error;
}
} else {
ctxt->type = XSLT_OUTPUT_XML;
res = xmlNewDoc(style->version);
if (res == NULL)
goto error;
}
res->charset = XML_CHAR_ENCODING_UTF8;
if (style->encoding != NULL)
res->encoding = xmlStrdup(style->encoding);
variables = style->variables;
/*
* Start the evaluation, evaluate the params, the stylesheets globals
* and start by processing the top node.
*/
if (xsltNeedElemSpaceHandling(ctxt))
xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc));
ctxt->output = res;
ctxt->insert = (xmlNodePtr) res;
if (ctxt->globalVars == NULL)
ctxt->globalVars = xmlHashCreate(20);
if (params != NULL)
xsltEvalUserParams(ctxt, params);
xsltInitCtxtExts(ctxt);
xsltEvalGlobalVariables(ctxt);
ctxt->node = (xmlNodePtr) doc;
varsPush(ctxt, NULL);
ctxt->varsBase = ctxt->varsNr - 1;
xsltProcessOneNode(ctxt, ctxt->node, NULL);
xsltFreeStackElemList(varsPop(ctxt));
xsltShutdownCtxtExts(ctxt);
xsltCleanupTemplates(style); /* TODO: <- style should be read only */
/*
* Now cleanup our variables so stylesheet can be re-used
*
* TODO: this is not needed anymore global variables are copied
* and not evaluated directly anymore, keep this as a check
*/
if (style->variables != variables) {
vptr = style->variables;
while (vptr->next != variables)
vptr = vptr->next;
vptr->next = NULL;
xsltFreeStackElemList(style->variables);
style->variables = variables;
}
vptr = style->variables;
while (vptr != NULL) {
if (vptr->computed) {
if (vptr->value != NULL) {
xmlXPathFreeObject(vptr->value);
vptr->value = NULL;
vptr->computed = 0;
}
}
vptr = vptr->next;
}
/*
* Do some post processing work depending on the generated output
*/
root = xmlDocGetRootElement(res);
if (root != NULL) {
/*
* Apply the default selection of the method
*/
if ((method == NULL) &&
(root->ns == NULL) &&
(!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) {
xmlNodePtr tmp;
tmp = res->children;
while ((tmp != NULL) && (tmp != root)) {
if (tmp->type == XML_ELEMENT_NODE)
break;
if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp)))
break;
tmp = tmp->next;
}
if (tmp == root) {
ctxt->type = XSLT_OUTPUT_HTML;
res->type = XML_HTML_DOCUMENT_NODE;
if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
res->intSubset = xmlCreateIntSubset(res, root->name,
doctypePublic,
doctypeSystem);
if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
res = htmlNewDoc(doctypeSystem, doctypePublic);
#ifdef XSLT_GENERATE_HTML_DOCTYPE
} else if (version != NULL) {
xsltGetHTMLIDs(version, &doctypePublic,
&doctypeSystem);
if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
res->intSubset =
xmlCreateIntSubset(res, root->name,
doctypePublic,
doctypeSystem);
#endif
}
}
}
if (ctxt->type == XSLT_OUTPUT_XML) {
XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
res->intSubset = xmlCreateIntSubset(res, root->name,
doctypePublic,
doctypeSystem);
}
}
xmlXPathFreeNodeSet(ctxt->nodeList);
if (profile != NULL) {
xsltSaveProfiling(ctxt, profile);
}
if (userCtxt == NULL)
xsltFreeTransformContext(ctxt);
return (res);
error:
if (res != NULL)
xmlFreeDoc(res);
if (ctxt != NULL)
xsltFreeTransformContext(ctxt);
return (NULL);
}
/**
* xsltApplyStylesheet:
* @style: a parsed XSLT stylesheet
* @doc: a parsed XML document
* @params: a NULL terminated arry of parameters names/values tuples
*
* Apply the stylesheet to the document
* NOTE: This may lead to a non-wellformed output XML wise !
*
* Returns the result document or NULL in case of error
*/
xmlDocPtr
xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
const char **params)
{
return (xsltApplyStylesheetInternal(style, doc, params, NULL, NULL, NULL));
}
/**
* xsltProfileStylesheet:
* @style: a parsed XSLT stylesheet
* @doc: a parsed XML document
* @params: a NULL terminated arry of parameters names/values tuples
* @output: a FILE * for the profiling output
*
* Apply the stylesheet to the document and dump the profiling to
* the given output.
*
* Returns the result document or NULL in case of error
*/
xmlDocPtr
xsltProfileStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
const char **params, FILE * output)
{
xmlDocPtr res;
res = xsltApplyStylesheetInternal(style, doc, params, NULL, output, NULL);
return (res);
}
/**
* xsltApplyStylesheetUser:
* @style: a parsed XSLT stylesheet
* @doc: a parsed XML document
* @params: a NULL terminated arry of parameters names/values tuples
* @output: the targetted output
* @profile: profile FILE * output or NULL
* @userCtxt: user provided transform context
*
* Apply the stylesheet to the document and allow the user to provide
* its own transformation context.
*
* Returns the result document or NULL in case of error
*/
xmlDocPtr
xsltApplyStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc,
const char **params, const char *output,
FILE * profile, xsltTransformContextPtr userCtxt)
{
xmlDocPtr res;
res = xsltApplyStylesheetInternal(style, doc, params, output,
profile, userCtxt);
return (res);
}
/**
* xsltRunStylesheet:
* @style: a parsed XSLT stylesheet
* @doc: a parsed XML document
* @params: a NULL terminated arry of parameters names/values tuples
* @output: the URL/filename ot the generated resource if available
* @SAX: a SAX handler for progressive callback output (not implemented yet)
* @IObuf: an output buffer for progressive output (not implemented yet)
*
* Apply the stylesheet to the document and generate the output according
* to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf.
*
* NOTE: This may lead to a non-wellformed output XML wise !
* NOTE: This may also result in multiple files being generated
* NOTE: using IObuf, the result encoding used will be the one used for
* creating the output buffer, use the following macro to read it
* from the stylesheet
* XSLT_GET_IMPORT_PTR(encoding, style, encoding)
* NOTE: using SAX, any encoding specified in the stylesheet will be lost
* since the interface uses only UTF8
*
* Returns the number of by written to the main resource or -1 in case of
* error.
*/
int
xsltRunStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
const char **params, const char *output,
xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf)
{
xmlDocPtr tmp;
int ret;
if ((output == NULL) && (SAX == NULL) && (IObuf == NULL))
return (-1);
if ((SAX != NULL) && (IObuf != NULL))
return (-1);
/* unsupported yet */
if (SAX != NULL) {
XSLT_TODO /* xsltRunStylesheet xmlSAXHandlerPtr SAX */
return (-1);
}
tmp = xsltApplyStylesheetInternal(style, doc, params, output, NULL, NULL);
if (tmp == NULL) {
xsltPrintErrorContext(NULL, NULL, (xmlNodePtr) doc);
xsltGenericError(xsltGenericErrorContext,
"xsltRunStylesheet : run failed\n");
return (-1);
}
if (IObuf != NULL) {
/* TODO: incomplete, IObuf output not progressive */
ret = xsltSaveResultTo(IObuf, tmp, style);
} else {
ret = xsltSaveResultToFilename(output, tmp, style, 0);
}
xmlFreeDoc(tmp);
return (ret);
}
/**
* xsltRegisterAllElement:
* @ctxt: the XPath context
*
* Registers all default XSLT elements in this context
*/
void
xsltRegisterAllElement(xsltTransformContextPtr ctxt)
{
xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-templates",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltApplyTemplates);
xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-imports",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltApplyImports);
xsltRegisterExtElement(ctxt, (const xmlChar *) "call-template",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltCallTemplate);
xsltRegisterExtElement(ctxt, (const xmlChar *) "element",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltElement);
xsltRegisterExtElement(ctxt, (const xmlChar *) "attribute",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltAttribute);
xsltRegisterExtElement(ctxt, (const xmlChar *) "text",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltText);
xsltRegisterExtElement(ctxt, (const xmlChar *) "processing-instruction",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltProcessingInstruction);
xsltRegisterExtElement(ctxt, (const xmlChar *) "comment",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltComment);
xsltRegisterExtElement(ctxt, (const xmlChar *) "copy",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltCopy);
xsltRegisterExtElement(ctxt, (const xmlChar *) "value-of",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltValueOf);
xsltRegisterExtElement(ctxt, (const xmlChar *) "number",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltNumber);
xsltRegisterExtElement(ctxt, (const xmlChar *) "for-each",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltForEach);
xsltRegisterExtElement(ctxt, (const xmlChar *) "if",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltIf);
xsltRegisterExtElement(ctxt, (const xmlChar *) "choose",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltChoose);
xsltRegisterExtElement(ctxt, (const xmlChar *) "sort",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltSort);
xsltRegisterExtElement(ctxt, (const xmlChar *) "copy-of",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltCopyOf);
xsltRegisterExtElement(ctxt, (const xmlChar *) "message",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltMessage);
/*
* Those don't have callable entry points but are registered anyway
*/
xsltRegisterExtElement(ctxt, (const xmlChar *) "variable",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltDebug);
xsltRegisterExtElement(ctxt, (const xmlChar *) "param",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltDebug);
xsltRegisterExtElement(ctxt, (const xmlChar *) "with-param",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltDebug);
xsltRegisterExtElement(ctxt, (const xmlChar *) "decimal-format",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltDebug);
xsltRegisterExtElement(ctxt, (const xmlChar *) "when",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltDebug);
xsltRegisterExtElement(ctxt, (const xmlChar *) "otherwise",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltDebug);
xsltRegisterExtElement(ctxt, (const xmlChar *) "fallback",
XSLT_NAMESPACE,
(xsltTransformFunction) xsltDebug);
}