summaryrefslogtreecommitdiffstats
path: root/libxslt
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2016-11-17 21:21:33 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2016-11-17 21:21:33 (GMT)
commit93eaa8f0a22ef3712b9a4bafdc50ba9a7d80ae8f (patch)
tree514ee2afcf9797dc556de6e4391cc7cce37fdaf4 /libxslt
downloadblt-93eaa8f0a22ef3712b9a4bafdc50ba9a7d80ae8f.zip
blt-93eaa8f0a22ef3712b9a4bafdc50ba9a7d80ae8f.tar.gz
blt-93eaa8f0a22ef3712b9a4bafdc50ba9a7d80ae8f.tar.bz2
Squashed 'libxslt/' content from commit 5c8f971
git-subtree-dir: libxslt git-subtree-split: 5c8f971cfbd16bec5b3901c5d2781ad34b93b652
Diffstat (limited to 'libxslt')
-rw-r--r--libxslt/Makefile.am79
-rw-r--r--libxslt/attributes.c1240
-rw-r--r--libxslt/attributes.h38
-rw-r--r--libxslt/attrvt.c387
-rw-r--r--libxslt/documents.c434
-rw-r--r--libxslt/documents.h93
-rw-r--r--libxslt/extensions.c2365
-rw-r--r--libxslt/extensions.h262
-rw-r--r--libxslt/extra.c332
-rw-r--r--libxslt/extra.h80
-rw-r--r--libxslt/functions.c988
-rw-r--r--libxslt/functions.h78
-rw-r--r--libxslt/imports.c414
-rw-r--r--libxslt/imports.h75
-rw-r--r--libxslt/keys.c935
-rw-r--r--libxslt/keys.h53
-rw-r--r--libxslt/libxslt.331
-rw-r--r--libxslt/libxslt.h36
-rw-r--r--libxslt/libxslt.syms490
-rw-r--r--libxslt/namespaces.c853
-rw-r--r--libxslt/namespaces.h68
-rw-r--r--libxslt/numbers.c1361
-rw-r--r--libxslt/numbersInternals.h73
-rw-r--r--libxslt/pattern.c2593
-rw-r--r--libxslt/pattern.h81
-rw-r--r--libxslt/preproc.c2376
-rw-r--r--libxslt/preproc.h43
-rw-r--r--libxslt/security.c480
-rw-r--r--libxslt/security.h104
-rw-r--r--libxslt/templates.c843
-rw-r--r--libxslt/templates.h77
-rw-r--r--libxslt/transform.c6449
-rw-r--r--libxslt/transform.h207
-rw-r--r--libxslt/trio.h216
-rw-r--r--libxslt/triodef.h220
-rw-r--r--libxslt/variables.c2365
-rw-r--r--libxslt/variables.h100
-rw-r--r--libxslt/win32config.h129
-rw-r--r--libxslt/xslt.c6990
-rw-r--r--libxslt/xslt.h110
-rw-r--r--libxslt/xsltInternals.h1977
-rw-r--r--libxslt/xsltconfig.h.in183
-rw-r--r--libxslt/xsltexports.h142
-rw-r--r--libxslt/xsltlocale.c525
-rw-r--r--libxslt/xsltlocale.h67
-rw-r--r--libxslt/xsltutils.c2483
-rw-r--r--libxslt/xsltutils.h313
-rw-r--r--libxslt/xsltwin32config.h105
-rw-r--r--libxslt/xsltwin32config.h.in105
49 files changed, 40048 insertions, 0 deletions
diff --git a/libxslt/Makefile.am b/libxslt/Makefile.am
new file mode 100644
index 0000000..d9fed68
--- /dev/null
+++ b/libxslt/Makefile.am
@@ -0,0 +1,79 @@
+AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/libxslt
+
+AM_CFLAGS = $(LIBXML_CFLAGS)
+
+lib_LTLIBRARIES = libxslt.la
+
+xsltincdir = $(includedir)/libxslt
+
+xsltinc_HEADERS = \
+ xslt.h \
+ xsltutils.h \
+ pattern.h \
+ templates.h \
+ variables.h \
+ keys.h \
+ numbersInternals.h \
+ extensions.h \
+ extra.h \
+ functions.h \
+ namespaces.h \
+ imports.h \
+ attributes.h \
+ documents.h \
+ preproc.h \
+ transform.h \
+ security.h \
+ xsltInternals.h \
+ xsltexports.h \
+ xsltlocale.h
+nodist_xsltinc_HEADERS = \
+ xsltconfig.h
+
+libxslt_la_SOURCES = \
+ attrvt.c \
+ xslt.c \
+ xsltlocale.c \
+ xsltutils.c \
+ pattern.c \
+ templates.c \
+ variables.c \
+ keys.c \
+ numbers.c \
+ extensions.c \
+ extra.c \
+ functions.c \
+ namespaces.c \
+ imports.c \
+ attributes.c \
+ documents.c \
+ preproc.c \
+ transform.c \
+ security.c \
+ win32config.h \
+ xsltwin32config.h.in \
+ libxslt.h
+nodist_libxslt_la_SOURCES = \
+ xsltwin32config.h
+
+if USE_VERSION_SCRIPT
+LIBXSLT_VERSION_SCRIPT = $(VERSION_SCRIPT_FLAGS)$(srcdir)/libxslt.syms
+else
+LIBXSLT_VERSION_SCRIPT =
+endif
+
+libxslt_la_LIBADD = $(LIBXML_LIBS) $(EXTRA_LIBS)
+libxslt_la_LDFLAGS = \
+ $(WIN32_EXTRA_LDFLAGS) \
+ $(LIBXSLT_VERSION_SCRIPT) \
+ -version-info $(LIBXSLT_VERSION_INFO)
+
+man_MANS = libxslt.3
+
+EXTRA_DIST = $(man_MANS) trio.h triodef.h libxslt.syms
+
+xsltproc: all
+ @(cd ../xsltproc ; $(MAKE))
+
+install-exec-hook:
+ $(MKDIR_P) "$(DESTDIR)$(libdir)/libxslt-plugins"
diff --git a/libxslt/attributes.c b/libxslt/attributes.c
new file mode 100644
index 0000000..9165ab1
--- /dev/null
+++ b/libxslt/attributes.c
@@ -0,0 +1,1240 @@
+/*
+ * attributes.c: Implementation of the XSLT attributes handling
+ *
+ * Reference:
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+#ifdef HAVE_NAN_H
+#include <nan.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/hash.h>
+#include <libxml/xmlerror.h>
+#include <libxml/uri.h>
+#include <libxml/parserInternals.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "xsltutils.h"
+#include "attributes.h"
+#include "namespaces.h"
+#include "templates.h"
+#include "imports.h"
+#include "transform.h"
+#include "preproc.h"
+
+#define WITH_XSLT_DEBUG_ATTRIBUTES
+#ifdef WITH_XSLT_DEBUG
+#define WITH_XSLT_DEBUG_ATTRIBUTES
+#endif
+
+/*
+ * Useful macros
+ */
+#ifdef IS_BLANK
+#undef IS_BLANK
+#endif
+
+#define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
+ ((c) == 0x0D))
+
+#define IS_BLANK_NODE(n) \
+ (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
+
+#define ATTRSET_UNRESOLVED 0
+#define ATTRSET_RESOLVING 1
+#define ATTRSET_RESOLVED 2
+
+
+/*
+ * The in-memory structure corresponding to an XSLT Attribute in
+ * an attribute set
+ */
+
+
+typedef struct _xsltAttrElem xsltAttrElem;
+typedef xsltAttrElem *xsltAttrElemPtr;
+struct _xsltAttrElem {
+ struct _xsltAttrElem *next;/* chained list */
+ xmlNodePtr attr; /* the xsl:attribute definition */
+};
+
+typedef struct _xsltUseAttrSet xsltUseAttrSet;
+typedef xsltUseAttrSet *xsltUseAttrSetPtr;
+struct _xsltUseAttrSet {
+ struct _xsltUseAttrSet *next; /* chained list */
+ const xmlChar *ncname;
+ const xmlChar *ns;
+};
+
+typedef struct _xsltAttrSet xsltAttrSet;
+typedef xsltAttrSet *xsltAttrSetPtr;
+struct _xsltAttrSet {
+ int state;
+ xsltAttrElemPtr attrs; /* list head */
+ xsltUseAttrSetPtr useAttrSets; /* list head */
+};
+
+typedef struct _xsltAttrSetContext xsltAttrSetContext;
+typedef xsltAttrSetContext *xsltAttrSetContextPtr;
+struct _xsltAttrSetContext {
+ xsltStylesheetPtr topStyle;
+ xsltStylesheetPtr style;
+};
+
+static void
+xsltResolveAttrSet(xsltAttrSetPtr set, xsltStylesheetPtr topStyle,
+ xsltStylesheetPtr style, const xmlChar *name,
+ const xmlChar *ns, int depth);
+
+/************************************************************************
+ * *
+ * XSLT Attribute handling *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltNewAttrElem:
+ * @attr: the new xsl:attribute node
+ *
+ * Create a new XSLT AttrElem
+ *
+ * Returns the newly allocated xsltAttrElemPtr or NULL in case of error
+ */
+static xsltAttrElemPtr
+xsltNewAttrElem(xmlNodePtr attr) {
+ xsltAttrElemPtr cur;
+
+ cur = (xsltAttrElemPtr) xmlMalloc(sizeof(xsltAttrElem));
+ if (cur == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltNewAttrElem : malloc failed\n");
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltAttrElem));
+ cur->attr = attr;
+ return(cur);
+}
+
+/**
+ * xsltFreeAttrElem:
+ * @attr: an XSLT AttrElem
+ *
+ * Free up the memory allocated by @attr
+ */
+static void
+xsltFreeAttrElem(xsltAttrElemPtr attr) {
+ xmlFree(attr);
+}
+
+/**
+ * xsltFreeAttrElemList:
+ * @list: an XSLT AttrElem list
+ *
+ * Free up the memory allocated by @list
+ */
+static void
+xsltFreeAttrElemList(xsltAttrElemPtr list) {
+ xsltAttrElemPtr next;
+
+ while (list != NULL) {
+ next = list->next;
+ xsltFreeAttrElem(list);
+ list = next;
+ }
+}
+
+/**
+ * xsltAddAttrElemList:
+ * @list: an XSLT AttrElem list
+ * @attr: the new xsl:attribute node
+ *
+ * Add the new attribute to the list.
+ *
+ * Returns the new list pointer
+ */
+static xsltAttrElemPtr
+xsltAddAttrElemList(xsltAttrElemPtr list, xmlNodePtr attr) {
+ xsltAttrElemPtr next, cur;
+
+ if (attr == NULL)
+ return(list);
+ if (list == NULL)
+ return(xsltNewAttrElem(attr));
+ cur = list;
+ while (cur != NULL) {
+ next = cur->next;
+ if (next == NULL) {
+ cur->next = xsltNewAttrElem(attr);
+ return(list);
+ }
+ cur = next;
+ }
+ return(list);
+}
+
+/**
+ * xsltNewUseAttrSet:
+ * @ncname: local name
+ * @ns: namespace URI
+ *
+ * Create a new XSLT UseAttrSet
+ *
+ * Returns the newly allocated xsltUseAttrSetPtr or NULL in case of error.
+ */
+static xsltUseAttrSetPtr
+xsltNewUseAttrSet(const xmlChar *ncname, const xmlChar *ns) {
+ xsltUseAttrSetPtr cur;
+
+ cur = (xsltUseAttrSetPtr) xmlMalloc(sizeof(xsltUseAttrSet));
+ if (cur == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltNewUseAttrSet : malloc failed\n");
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltUseAttrSet));
+ cur->ncname = ncname;
+ cur->ns = ns;
+ return(cur);
+}
+
+/**
+ * xsltFreeUseAttrSet:
+ * @use: an XSLT UseAttrSet
+ *
+ * Free up the memory allocated by @use
+ */
+static void
+xsltFreeUseAttrSet(xsltUseAttrSetPtr use) {
+ xmlFree(use);
+}
+
+/**
+ * xsltFreeUseAttrSetList:
+ * @list: an XSLT UseAttrSet list
+ *
+ * Free up the memory allocated by @list
+ */
+static void
+xsltFreeUseAttrSetList(xsltUseAttrSetPtr list) {
+ xsltUseAttrSetPtr next;
+
+ while (list != NULL) {
+ next = list->next;
+ xsltFreeUseAttrSet(list);
+ list = next;
+ }
+}
+
+/**
+ * xsltAddUseAttrSetList:
+ * @list: a xsltUseAttrSet list
+ * @ncname: local name
+ * @ns: namespace URI
+ *
+ * Add the use-attribute-set name to the list.
+ *
+ * Returns the new list pointer.
+ */
+static xsltUseAttrSetPtr
+xsltAddUseAttrSetList(xsltUseAttrSetPtr list, const xmlChar *ncname,
+ const xmlChar *ns) {
+ xsltUseAttrSetPtr next, cur;
+
+ if (ncname == NULL)
+ return(list);
+ if (list == NULL)
+ return(xsltNewUseAttrSet(ncname, ns));
+ cur = list;
+ while (cur != NULL) {
+ if ((cur->ncname == ncname) && (cur->ns == ns))
+ return(list);
+ next = cur->next;
+ if (next == NULL) {
+ cur->next = xsltNewUseAttrSet(ncname, ns);
+ return(list);
+ }
+ cur = next;
+ }
+ return(list);
+}
+
+/**
+ * xsltNewAttrSet:
+ *
+ * Create a new attribute set.
+ *
+ * Returns the newly allocated xsltAttrSetPtr or NULL in case of error.
+ */
+static xsltAttrSetPtr
+xsltNewAttrSet() {
+ xsltAttrSetPtr cur;
+
+ cur = (xsltAttrSetPtr) xmlMalloc(sizeof(xsltAttrSet));
+ if (cur == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltNewAttrSet : malloc failed\n");
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltAttrSet));
+ return(cur);
+}
+
+/**
+ * xsltFreeAttrSet:
+ * @set: an attribute set
+ *
+ * Free memory allocated by @set
+ */
+static void
+xsltFreeAttrSet(xsltAttrSetPtr set) {
+ if (set == NULL)
+ return;
+
+ xsltFreeAttrElemList(set->attrs);
+ xsltFreeUseAttrSetList(set->useAttrSets);
+ xmlFree(set);
+}
+
+/**
+ * xsltMergeAttrSets:
+ * @set: an attribute set
+ * @other: another attribute set
+ *
+ * Add all the attributes from @other to @set,
+ * but drop redefinition of existing values.
+ */
+static void
+xsltMergeAttrSets(xsltAttrSetPtr set, xsltAttrSetPtr other) {
+ xsltAttrElemPtr cur;
+ xsltAttrElemPtr old = other->attrs;
+ int add;
+
+ while (old != NULL) {
+ /*
+ * Check that the attribute is not yet in the list
+ */
+ cur = set->attrs;
+ add = 1;
+ while (cur != NULL) {
+ xsltStylePreCompPtr curComp = cur->attr->psvi;
+ xsltStylePreCompPtr oldComp = old->attr->psvi;
+
+ if ((curComp->name == oldComp->name) &&
+ (curComp->ns == oldComp->ns)) {
+ add = 0;
+ break;
+ }
+ if (cur->next == NULL)
+ break;
+ cur = cur->next;
+ }
+
+ if (add == 1) {
+ if (cur == NULL) {
+ set->attrs = xsltNewAttrElem(old->attr);
+ } else if (add) {
+ cur->next = xsltNewAttrElem(old->attr);
+ }
+ }
+
+ old = old->next;
+ }
+}
+
+/************************************************************************
+ * *
+ * Module interfaces *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltParseStylesheetAttributeSet:
+ * @style: the XSLT stylesheet
+ * @cur: the "attribute-set" element
+ *
+ * parse an XSLT stylesheet attribute-set element
+ */
+
+void
+xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) {
+ const xmlChar *ncname;
+ const xmlChar *prefix;
+ const xmlChar *nsUri = NULL;
+ xmlChar *value;
+ xmlNodePtr child;
+ xsltAttrSetPtr set;
+
+ if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
+ return;
+
+ value = xmlGetNsProp(cur, (const xmlChar *)"name", NULL);
+ if ((value == NULL) || (*value == 0)) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsl:attribute-set : name is missing\n");
+ if (value)
+ xmlFree(value);
+ return;
+ }
+
+ if (xmlValidateQName(value, 0)) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:attribute-set : The name '%s' is not a valid QName.\n",
+ value);
+ style->errors++;
+ xmlFree(value);
+ return;
+ }
+
+ ncname = xsltSplitQName(style->dict, value, &prefix);
+ xmlFree(value);
+ value = NULL;
+ if (prefix != NULL) {
+ xmlNsPtr ns = xmlSearchNs(style->doc, cur, prefix);
+ if (ns == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:attribute-set : No namespace found for QName '%s:%s'\n",
+ prefix, ncname);
+ style->errors++;
+ return;
+ }
+ nsUri = ns->href;
+ }
+
+ if (style->attributeSets == NULL) {
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+ xsltGenericDebug(xsltGenericDebugContext,
+ "creating attribute set table\n");
+#endif
+ style->attributeSets = xmlHashCreate(10);
+ }
+ if (style->attributeSets == NULL)
+ return;
+
+ set = xmlHashLookup2(style->attributeSets, ncname, nsUri);
+ if (set == NULL) {
+ set = xsltNewAttrSet();
+ if (set == NULL)
+ return;
+ xmlHashAddEntry2(style->attributeSets, ncname, nsUri, set);
+ }
+
+ /*
+ * Parse the content. Only xsl:attribute elements are allowed.
+ */
+ child = cur->children;
+ while (child != NULL) {
+ /*
+ * Report invalid nodes.
+ */
+ if ((child->type != XML_ELEMENT_NODE) ||
+ (child->ns == NULL) ||
+ (! IS_XSLT_ELEM(child)))
+ {
+ if (child->type == XML_ELEMENT_NODE)
+ xsltTransformError(NULL, style, child,
+ "xsl:attribute-set : unexpected child %s\n",
+ child->name);
+ else
+ xsltTransformError(NULL, style, child,
+ "xsl:attribute-set : child of unexpected type\n");
+ } else if (!IS_XSLT_NAME(child, "attribute")) {
+ xsltTransformError(NULL, style, child,
+ "xsl:attribute-set : unexpected child xsl:%s\n",
+ child->name);
+ } else {
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+ xsltGenericDebug(xsltGenericDebugContext,
+ "add attribute to list %s\n", ncname);
+#endif
+ xsltStylePreCompute(style, child);
+ if (child->children != NULL) {
+#ifdef XSLT_REFACTORED
+ xsltParseSequenceConstructor(XSLT_CCTXT(style),
+ child->children);
+#else
+ xsltParseTemplateContent(style, child);
+#endif
+ }
+ if (child->psvi == NULL) {
+ xsltTransformError(NULL, style, child,
+ "xsl:attribute-set : internal error, attribute %s not "
+ "compiled\n", child->name);
+ }
+ else {
+ set->attrs = xsltAddAttrElemList(set->attrs, child);
+ }
+ }
+
+ child = child->next;
+ }
+
+ /*
+ * Process attribute "use-attribute-sets".
+ */
+ value = xmlGetNsProp(cur, BAD_CAST "use-attribute-sets", NULL);
+ if (value != NULL) {
+ const xmlChar *curval, *endval;
+ curval = value;
+ while (*curval != 0) {
+ while (IS_BLANK(*curval)) curval++;
+ if (*curval == 0)
+ break;
+ endval = curval;
+ while ((*endval != 0) && (!IS_BLANK(*endval))) endval++;
+ curval = xmlDictLookup(style->dict, curval, endval - curval);
+ if (curval) {
+ const xmlChar *ncname2 = NULL;
+ const xmlChar *prefix2 = NULL;
+ const xmlChar *nsUri2 = NULL;
+
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsl:attribute-set : %s adds use %s\n", ncname, curval);
+#endif
+
+ if (xmlValidateQName(curval, 0)) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:attribute-set : The name '%s' in "
+ "use-attribute-sets is not a valid QName.\n", curval);
+ style->errors++;
+ xmlFree(value);
+ return;
+ }
+
+ ncname2 = xsltSplitQName(style->dict, curval, &prefix2);
+ if (prefix2 != NULL) {
+ xmlNsPtr ns2 = xmlSearchNs(style->doc, cur, prefix2);
+ if (ns2 == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:attribute-set : No namespace found for QName "
+ "'%s:%s' in use-attribute-sets\n",
+ prefix2, ncname2);
+ style->errors++;
+ xmlFree(value);
+ return;
+ }
+ nsUri2 = ns2->href;
+ }
+ set->useAttrSets = xsltAddUseAttrSetList(set->useAttrSets,
+ ncname2, nsUri2);
+ }
+ curval = endval;
+ }
+ xmlFree(value);
+ value = NULL;
+ }
+
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+ xsltGenericDebug(xsltGenericDebugContext,
+ "updated attribute list %s\n", ncname);
+#endif
+}
+
+/**
+ * xsltResolveUseAttrSets:
+ * @set: the attribute set
+ * @asctx: the context for attribute set resolution
+ * @depth: recursion depth
+ *
+ * Process "use-attribute-sets".
+ */
+static void
+xsltResolveUseAttrSets(xsltAttrSetPtr set, xsltStylesheetPtr topStyle,
+ int depth) {
+ xsltStylesheetPtr cur;
+ xsltAttrSetPtr other;
+ xsltUseAttrSetPtr use = set->useAttrSets;
+ xsltUseAttrSetPtr next;
+
+ while (use != NULL) {
+ /*
+ * Iterate top stylesheet and all imports.
+ */
+ cur = topStyle;
+ while (cur != NULL) {
+ if (cur->attributeSets) {
+ other = xmlHashLookup2(cur->attributeSets, use->ncname,
+ use->ns);
+ if (other != NULL) {
+ xsltResolveAttrSet(other, topStyle, cur, use->ncname,
+ use->ns, depth + 1);
+ xsltMergeAttrSets(set, other);
+ break;
+ }
+ }
+ cur = xsltNextImport(cur);
+ }
+
+ next = use->next;
+ /* Free useAttrSets early. */
+ xsltFreeUseAttrSet(use);
+ use = next;
+ }
+
+ set->useAttrSets = NULL;
+}
+
+/**
+ * xsltResolveAttrSet:
+ * @set: the attribute set
+ * @asctx: the context for attribute set resolution
+ * @name: the local name of the attirbute set
+ * @ns: the namespace of the attribute set
+ * @depth: recursion depth
+ *
+ * resolve the references in an attribute set.
+ */
+static void
+xsltResolveAttrSet(xsltAttrSetPtr set, xsltStylesheetPtr topStyle,
+ xsltStylesheetPtr style, const xmlChar *name,
+ const xmlChar *ns, int depth) {
+ xsltStylesheetPtr cur;
+ xsltAttrSetPtr other;
+
+ if (set->state == ATTRSET_RESOLVED)
+ return;
+ if (set->state == ATTRSET_RESOLVING) {
+ xsltTransformError(NULL, topStyle, NULL,
+ "xsl:attribute-set : use-attribute-sets recursion detected"
+ " on %s\n", name);
+ topStyle->errors++;
+ set->state = ATTRSET_RESOLVED;
+ return;
+ }
+ if (depth > 100) {
+ xsltTransformError(NULL, topStyle, NULL,
+ "xsl:attribute-set : use-attribute-sets maximum recursion "
+ "depth exceeded on %s\n", name);
+ topStyle->errors++;
+ return;
+ }
+
+ set->state = ATTRSET_RESOLVING;
+
+ xsltResolveUseAttrSets(set, topStyle, depth);
+
+ /* Merge imported sets. */
+ cur = xsltNextImport(style);
+ while (cur != NULL) {
+ if (cur->attributeSets != NULL) {
+ other = xmlHashLookup2(cur->attributeSets, name, ns);
+
+ if (other != NULL) {
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsl:attribute-set : merging import for %s\n", name);
+#endif
+ xsltResolveUseAttrSets(other, topStyle, depth);
+ xsltMergeAttrSets(set, other);
+ xmlHashRemoveEntry2(cur->attributeSets, name, ns, NULL);
+ xsltFreeAttrSet(other);
+ }
+ }
+
+ cur = xsltNextImport(cur);
+ }
+
+ set->state = ATTRSET_RESOLVED;
+}
+
+/**
+ * xsltResolveSASCallback:
+ * @set: the attribute set
+ * @asctx: the context for attribute set resolution
+ * @name: the local name of the attirbute set
+ * @ns: the namespace of the attribute set
+ *
+ * resolve the references in an attribute set.
+ */
+static void
+xsltResolveSASCallback(xsltAttrSetPtr set, xsltAttrSetContextPtr asctx,
+ const xmlChar *name, const xmlChar *ns,
+ ATTRIBUTE_UNUSED const xmlChar *ignored) {
+ xsltStylesheetPtr topStyle = asctx->topStyle;
+ xsltStylesheetPtr style = asctx->style;
+
+ xsltResolveAttrSet(set, topStyle, style, name, ns, 1);
+
+ /* Move attribute sets to top stylesheet. */
+ if (style != topStyle) {
+ /*
+ * This imported stylesheet won't be visited anymore. Don't bother
+ * removing the hash entry.
+ */
+ if (xmlHashAddEntry2(topStyle->attributeSets, name, ns, set) < 0) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsl:attribute-set : internal error, can't move imported "
+ " attribute set %s\n", name);
+ }
+ }
+}
+
+/**
+ * xsltResolveStylesheetAttributeSet:
+ * @style: the XSLT stylesheet
+ *
+ * resolve the references between attribute sets.
+ */
+void
+xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) {
+ xsltStylesheetPtr cur;
+ xsltAttrSetContext asctx;
+
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Resolving attribute sets references\n");
+#endif
+ asctx.topStyle = style;
+ cur = style;
+ while (cur != NULL) {
+ if (cur->attributeSets != NULL) {
+ if (style->attributeSets == NULL) {
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+ xsltGenericDebug(xsltGenericDebugContext,
+ "creating attribute set table\n");
+#endif
+ style->attributeSets = xmlHashCreate(10);
+ }
+ asctx.style = cur;
+ xmlHashScanFull(cur->attributeSets,
+ (xmlHashScannerFull) xsltResolveSASCallback, &asctx);
+
+ if (cur != style) {
+ /*
+ * the attribute lists have either been migrated to style
+ * or freed directly in xsltResolveSASCallback()
+ */
+ xmlHashFree(cur->attributeSets, NULL);
+ cur->attributeSets = NULL;
+ }
+ }
+ cur = xsltNextImport(cur);
+ }
+}
+
+/**
+ * xsltAttribute:
+ * @ctxt: a XSLT process context
+ * @node: the current node in the source tree
+ * @inst: the xsl:attribute element
+ * @comp: precomputed information
+ *
+ * Process the xslt attribute node on the source node
+ */
+void
+xsltAttribute(xsltTransformContextPtr ctxt,
+ xmlNodePtr contextNode,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr castedComp)
+{
+#ifdef XSLT_REFACTORED
+ xsltStyleItemAttributePtr comp =
+ (xsltStyleItemAttributePtr) castedComp;
+#else
+ xsltStylePreCompPtr comp = castedComp;
+#endif
+ xmlNodePtr targetElem;
+ xmlChar *prop = NULL;
+ const xmlChar *name = NULL, *prefix = NULL, *nsName = NULL;
+ xmlChar *value = NULL;
+ xmlNsPtr ns = NULL;
+ xmlAttrPtr attr;
+
+ if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL) ||
+ (inst->type != XML_ELEMENT_NODE) )
+ return;
+
+ /*
+ * A comp->has_name == 0 indicates that we need to skip this instruction,
+ * since it was evaluated to be invalid already during compilation.
+ */
+ if (!comp->has_name)
+ return;
+ /*
+ * BIG NOTE: This previously used xsltGetSpecialNamespace() and
+ * xsltGetNamespace(), but since both are not appropriate, we
+ * will process namespace lookup here to avoid adding yet another
+ * ns-lookup function to namespaces.c.
+ */
+ /*
+ * SPEC XSLT 1.0: Error cases:
+ * - Creating nodes other than text nodes during the instantiation of
+ * the content of the xsl:attribute element; implementations may
+ * either signal the error or ignore the offending nodes."
+ */
+
+ if (comp == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "Internal error in xsltAttribute(): "
+ "The XSLT 'attribute' instruction was not compiled.\n");
+ return;
+ }
+ /*
+ * TODO: Shouldn't ctxt->insert == NULL be treated as an internal error?
+ * So report an internal error?
+ */
+ if (ctxt->insert == NULL)
+ return;
+ /*
+ * SPEC XSLT 1.0:
+ * "Adding an attribute to a node that is not an element;
+ * implementations may either signal the error or ignore the attribute."
+ *
+ * TODO: I think we should signal such errors in the future, and maybe
+ * provide an option to ignore such errors.
+ */
+ targetElem = ctxt->insert;
+ if (targetElem->type != XML_ELEMENT_NODE)
+ return;
+
+ /*
+ * SPEC XSLT 1.0:
+ * "Adding an attribute to an element after children have been added
+ * to it; implementations may either signal the error or ignore the
+ * attribute."
+ *
+ * TODO: We should decide whether not to report such errors or
+ * to ignore them; note that we *ignore* if the parent is not an
+ * element, but here we report an error.
+ */
+ if (targetElem->children != NULL) {
+ /*
+ * NOTE: Ah! This seems to be intended to support streamed
+ * result generation!.
+ */
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:attribute: Cannot add attributes to an "
+ "element if children have been already added "
+ "to the element.\n");
+ return;
+ }
+
+ /*
+ * Process the name
+ * ----------------
+ */
+
+#ifdef WITH_DEBUGGER
+ if (ctxt->debugStatus != XSLT_DEBUG_NONE)
+ xslHandleDebugger(inst, contextNode, NULL, ctxt);
+#endif
+
+ if (comp->name == NULL) {
+ /* TODO: fix attr acquisition wrt to the XSLT namespace */
+ prop = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *) "name", XSLT_NAMESPACE);
+ if (prop == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:attribute: The attribute 'name' is missing.\n");
+ goto error;
+ }
+ if (xmlValidateQName(prop, 0)) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:attribute: The effective name '%s' is not a "
+ "valid QName.\n", prop);
+ /* we fall through to catch any further errors, if possible */
+ }
+
+ /*
+ * Reject a name of "xmlns".
+ */
+ if (xmlStrEqual(prop, BAD_CAST "xmlns")) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:attribute: The effective name 'xmlns' is not allowed.\n");
+ xmlFree(prop);
+ goto error;
+ }
+
+ name = xsltSplitQName(ctxt->dict, prop, &prefix);
+ xmlFree(prop);
+ } else {
+ /*
+ * The "name" value was static.
+ */
+#ifdef XSLT_REFACTORED
+ prefix = comp->nsPrefix;
+ name = comp->name;
+#else
+ name = xsltSplitQName(ctxt->dict, comp->name, &prefix);
+#endif
+ }
+
+ /*
+ * Process namespace semantics
+ * ---------------------------
+ *
+ * Evaluate the namespace name.
+ */
+ if (comp->has_ns) {
+ /*
+ * The "namespace" attribute was existent.
+ */
+ if (comp->ns != NULL) {
+ /*
+ * No AVT; just plain text for the namespace name.
+ */
+ if (comp->ns[0] != 0)
+ nsName = comp->ns;
+ } else {
+ xmlChar *tmpNsName;
+ /*
+ * Eval the AVT.
+ */
+ /* TODO: check attr acquisition wrt to the XSLT namespace */
+ tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *) "namespace", XSLT_NAMESPACE);
+ /*
+ * This fixes bug #302020: The AVT might also evaluate to the
+ * empty string; this means that the empty string also indicates
+ * "no namespace".
+ * SPEC XSLT 1.0:
+ * "If the string is empty, then the expanded-name of the
+ * attribute has a null namespace URI."
+ */
+ if ((tmpNsName != NULL) && (tmpNsName[0] != 0))
+ nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1);
+ xmlFree(tmpNsName);
+ }
+
+ if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ "
+ "forbidden.\n");
+ goto error;
+ }
+ if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) {
+ prefix = BAD_CAST "xml";
+ } else if (xmlStrEqual(prefix, BAD_CAST "xml")) {
+ prefix = NULL;
+ }
+ } else if (prefix != NULL) {
+ /*
+ * SPEC XSLT 1.0:
+ * "If the namespace attribute is not present, then the QName is
+ * expanded into an expanded-name using the namespace declarations
+ * in effect for the xsl:attribute element, *not* including any
+ * default namespace declaration."
+ */
+ ns = xmlSearchNs(inst->doc, inst, prefix);
+ if (ns == NULL) {
+ /*
+ * Note that this is treated as an error now (checked with
+ * Saxon, Xalan-J and MSXML).
+ */
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:attribute: The QName '%s:%s' has no "
+ "namespace binding in scope in the stylesheet; "
+ "this is an error, since the namespace was not "
+ "specified by the instruction itself.\n", prefix, name);
+ } else
+ nsName = ns->href;
+ }
+
+ /*
+ * Find/create a matching ns-decl in the result tree.
+ */
+ ns = NULL;
+
+#if 0
+ if (0) {
+ /*
+ * OPTIMIZE TODO: How do we know if we are adding to a
+ * fragment or to the result tree?
+ *
+ * If we are adding to a result tree fragment (i.e., not to the
+ * actual result tree), we'll don't bother searching for the
+ * ns-decl, but just store it in the dummy-doc of the result
+ * tree fragment.
+ */
+ if (nsName != NULL) {
+ /*
+ * TODO: Get the doc of @targetElem.
+ */
+ ns = xsltTreeAcquireStoredNs(some doc, nsName, prefix);
+ }
+ }
+#endif
+
+ if (nsName != NULL) {
+ /*
+ * Something about ns-prefixes:
+ * SPEC XSLT 1.0:
+ * "XSLT processors may make use of the prefix of the QName specified
+ * in the name attribute when selecting the prefix used for outputting
+ * the created attribute as XML; however, they are not required to do
+ * so and, if the prefix is xmlns, they must not do so"
+ */
+ /*
+ * xsl:attribute can produce a scenario where the prefix is NULL,
+ * so generate a prefix.
+ */
+ if ((prefix == NULL) || xmlStrEqual(prefix, BAD_CAST "xmlns")) {
+ xmlChar *pref = xmlStrdup(BAD_CAST "ns_1");
+
+ ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, targetElem);
+
+ xmlFree(pref);
+ } else {
+ ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix,
+ targetElem);
+ }
+ if (ns == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "Namespace fixup error: Failed to acquire an in-scope "
+ "namespace binding for the generated attribute '{%s}%s'.\n",
+ nsName, name);
+ goto error;
+ }
+ }
+ /*
+ * Construction of the value
+ * -------------------------
+ */
+ if (inst->children == NULL) {
+ /*
+ * No content.
+ * TODO: Do we need to put the empty string in ?
+ */
+ attr = xmlSetNsProp(ctxt->insert, ns, name, (const xmlChar *) "");
+ } else if ((inst->children->next == NULL) &&
+ ((inst->children->type == XML_TEXT_NODE) ||
+ (inst->children->type == XML_CDATA_SECTION_NODE)))
+ {
+ xmlNodePtr copyTxt;
+
+ /*
+ * xmlSetNsProp() will take care of duplicates.
+ */
+ attr = xmlSetNsProp(ctxt->insert, ns, name, NULL);
+ if (attr == NULL) /* TODO: report error ? */
+ goto error;
+ /*
+ * This was taken over from xsltCopyText() (transform.c).
+ */
+ if (ctxt->internalized &&
+ (ctxt->insert->doc != NULL) &&
+ (ctxt->insert->doc->dict == ctxt->dict))
+ {
+ copyTxt = xmlNewText(NULL);
+ if (copyTxt == NULL) /* TODO: report error */
+ goto error;
+ /*
+ * This is a safe scenario where we don't need to lookup
+ * the dict.
+ */
+ copyTxt->content = inst->children->content;
+ /*
+ * Copy "disable-output-escaping" information.
+ * TODO: Does this have any effect for attribute values
+ * anyway?
+ */
+ if (inst->children->name == xmlStringTextNoenc)
+ copyTxt->name = xmlStringTextNoenc;
+ } else {
+ /*
+ * Copy the value.
+ */
+ copyTxt = xmlNewText(inst->children->content);
+ if (copyTxt == NULL) /* TODO: report error */
+ goto error;
+ }
+ attr->children = attr->last = copyTxt;
+ copyTxt->parent = (xmlNodePtr) attr;
+ copyTxt->doc = attr->doc;
+ /*
+ * Copy "disable-output-escaping" information.
+ * TODO: Does this have any effect for attribute values
+ * anyway?
+ */
+ if (inst->children->name == xmlStringTextNoenc)
+ copyTxt->name = xmlStringTextNoenc;
+
+ /*
+ * since we create the attribute without content IDness must be
+ * asserted as a second step
+ */
+ if ((copyTxt->content != NULL) &&
+ (xmlIsID(attr->doc, attr->parent, attr)))
+ xmlAddID(NULL, attr->doc, copyTxt->content, attr);
+ } else {
+ /*
+ * The sequence constructor might be complex, so instantiate it.
+ */
+ value = xsltEvalTemplateString(ctxt, contextNode, inst);
+ if (value != NULL) {
+ attr = xmlSetNsProp(ctxt->insert, ns, name, value);
+ xmlFree(value);
+ } else {
+ /*
+ * TODO: Do we have to add the empty string to the attr?
+ * TODO: Does a value of NULL indicate an
+ * error in xsltEvalTemplateString() ?
+ */
+ attr = xmlSetNsProp(ctxt->insert, ns, name,
+ (const xmlChar *) "");
+ }
+ }
+
+error:
+ return;
+}
+
+/**
+ * xsltApplyAttributeSet:
+ * @ctxt: the XSLT stylesheet
+ * @node: the node in the source tree.
+ * @inst: the attribute node "xsl:use-attribute-sets"
+ * @attrSets: the list of QNames of the attribute-sets to be applied
+ *
+ * Apply the xsl:use-attribute-sets.
+ * If @attrSets is NULL, then @inst will be used to exctract this
+ * value.
+ * If both, @attrSets and @inst, are NULL, then this will do nothing.
+ */
+void
+xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr inst,
+ const xmlChar *attrSets)
+{
+ const xmlChar *ncname = NULL;
+ const xmlChar *prefix = NULL;
+ const xmlChar *curstr, *endstr;
+ xsltAttrSetPtr set;
+ xsltStylesheetPtr style;
+
+ if (attrSets == NULL) {
+ if (inst == NULL)
+ return;
+ else {
+ /*
+ * Extract the value from @inst.
+ */
+ if (inst->type == XML_ATTRIBUTE_NODE) {
+ if ( ((xmlAttrPtr) inst)->children != NULL)
+ attrSets = ((xmlAttrPtr) inst)->children->content;
+
+ }
+ if (attrSets == NULL) {
+ /*
+ * TODO: Return an error?
+ */
+ return;
+ }
+ }
+ }
+ /*
+ * Parse/apply the list of QNames.
+ */
+ curstr = attrSets;
+ while (*curstr != 0) {
+ while (IS_BLANK(*curstr))
+ curstr++;
+ if (*curstr == 0)
+ break;
+ endstr = curstr;
+ while ((*endstr != 0) && (!IS_BLANK(*endstr)))
+ endstr++;
+ curstr = xmlDictLookup(ctxt->dict, curstr, endstr - curstr);
+ if (curstr) {
+ xmlNsPtr ns;
+ const xmlChar *nsUri = NULL;
+
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+ xsltGenericDebug(xsltGenericDebugContext,
+ "apply attribute set %s\n", curstr);
+#endif
+
+ if (xmlValidateQName(curstr, 0)) {
+ xsltTransformError(ctxt, NULL, inst,
+ "The name '%s' in use-attribute-sets is not a valid "
+ "QName.\n", curstr);
+ return;
+ }
+
+ ncname = xsltSplitQName(ctxt->dict, curstr, &prefix);
+ if (prefix != NULL) {
+ ns = xmlSearchNs(inst->doc, inst, prefix);
+ if (ns == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "use-attribute-set : No namespace found for QName "
+ "'%s:%s'\n", prefix, ncname);
+ return;
+ }
+ nsUri = ns->href;
+ }
+
+ style = ctxt->style;
+
+#ifdef WITH_DEBUGGER
+ if ((style != NULL) &&
+ (style->attributeSets != NULL) &&
+ (ctxt->debugStatus != XSLT_DEBUG_NONE))
+ {
+ set = xmlHashLookup2(style->attributeSets, ncname, nsUri);
+ if ((set != NULL) && (set->attrs != NULL) &&
+ (set->attrs->attr != NULL))
+ xslHandleDebugger(set->attrs->attr->parent, node, NULL,
+ ctxt);
+ }
+#endif
+ /*
+ * Lookup the referenced attribute-set. All attribute sets were
+ * moved to the top stylesheet so there's no need to iterate
+ * imported stylesheets
+ */
+ set = xmlHashLookup2(style->attributeSets, ncname, nsUri);
+ if (set != NULL) {
+ xsltAttrElemPtr cur = set->attrs;
+ while (cur != NULL) {
+ if (cur->attr != NULL) {
+ xsltAttribute(ctxt, node, cur->attr,
+ cur->attr->psvi);
+ }
+ cur = cur->next;
+ }
+ }
+ }
+ curstr = endstr;
+ }
+}
+
+/**
+ * xsltFreeAttributeSetsHashes:
+ * @style: an XSLT stylesheet
+ *
+ * Free up the memory used by attribute sets
+ */
+void
+xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) {
+ if (style->attributeSets != NULL)
+ xmlHashFree((xmlHashTablePtr) style->attributeSets,
+ (xmlHashDeallocator) xsltFreeAttrSet);
+ style->attributeSets = NULL;
+}
diff --git a/libxslt/attributes.h b/libxslt/attributes.h
new file mode 100644
index 0000000..05b8a6e
--- /dev/null
+++ b/libxslt/attributes.h
@@ -0,0 +1,38 @@
+/*
+ * Summary: interface for the XSLT attribute handling
+ * Description: this module handles the specificities of attribute
+ * and attribute groups processing.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_ATTRIBUTES_H__
+#define __XML_XSLT_ATTRIBUTES_H__
+
+#include <libxml/tree.h>
+#include "xsltexports.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+XSLTPUBFUN void XSLTCALL
+ xsltParseStylesheetAttributeSet (xsltStylesheetPtr style,
+ xmlNodePtr cur);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeAttributeSetsHashes (xsltStylesheetPtr style);
+XSLTPUBFUN void XSLTCALL
+ xsltApplyAttributeSet (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ const xmlChar *attributes);
+XSLTPUBFUN void XSLTCALL
+ xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_ATTRIBUTES_H__ */
+
diff --git a/libxslt/attrvt.c b/libxslt/attrvt.c
new file mode 100644
index 0000000..125159c
--- /dev/null
+++ b/libxslt/attrvt.c
@@ -0,0 +1,387 @@
+/*
+ * attrvt.c: Implementation of the XSL Transformation 1.0 engine
+ * attribute value template handling part.
+ *
+ * References:
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * Michael Kay "XSLT Programmer's Reference" pp 637-643
+ * Writing Multiple Output Files
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include "xslt.h"
+#include "xsltutils.h"
+#include "xsltInternals.h"
+#include "templates.h"
+
+#ifdef WITH_XSLT_DEBUG
+#define WITH_XSLT_DEBUG_AVT
+#endif
+
+#define MAX_AVT_SEG 10
+
+typedef struct _xsltAttrVT xsltAttrVT;
+typedef xsltAttrVT *xsltAttrVTPtr;
+struct _xsltAttrVT {
+ struct _xsltAttrVT *next; /* next xsltAttrVT */
+ int nb_seg; /* Number of segments */
+ int max_seg; /* max capacity before re-alloc needed */
+ int strstart; /* is the start a string */
+ /*
+ * the namespaces in scope
+ */
+ xmlNsPtr *nsList;
+ int nsNr;
+ /*
+ * the content is an alternate of string and xmlXPathCompExprPtr
+ */
+ void *segments[MAX_AVT_SEG];
+};
+
+/**
+ * xsltNewAttrVT:
+ * @style: a XSLT process context
+ *
+ * Build a new xsltAttrVT structure
+ *
+ * Returns the structure or NULL in case of error
+ */
+static xsltAttrVTPtr
+xsltNewAttrVT(xsltStylesheetPtr style) {
+ xsltAttrVTPtr cur;
+
+ cur = (xsltAttrVTPtr) xmlMalloc(sizeof(xsltAttrVT));
+ if (cur == NULL) {
+ xsltTransformError(NULL, style, NULL,
+ "xsltNewAttrVTPtr : malloc failed\n");
+ if (style != NULL) style->errors++;
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltAttrVT));
+
+ cur->nb_seg = 0;
+ cur->max_seg = MAX_AVT_SEG;
+ cur->strstart = 0;
+ cur->next = style->attVTs;
+ /*
+ * Note: this pointer may be changed by a re-alloc within xsltCompileAttr,
+ * so that code may change the stylesheet pointer also!
+ */
+ style->attVTs = (xsltAttrVTPtr) cur;
+
+ return(cur);
+}
+
+/**
+ * xsltFreeAttrVT:
+ * @avt: pointer to an xsltAttrVT structure
+ *
+ * Free up the memory associated to the attribute value template
+ */
+static void
+xsltFreeAttrVT(xsltAttrVTPtr avt) {
+ int i;
+
+ if (avt == NULL) return;
+
+ if (avt->strstart == 1) {
+ for (i = 0;i < avt->nb_seg; i += 2)
+ if (avt->segments[i] != NULL)
+ xmlFree((xmlChar *) avt->segments[i]);
+ for (i = 1;i < avt->nb_seg; i += 2)
+ xmlXPathFreeCompExpr((xmlXPathCompExprPtr) avt->segments[i]);
+ } else {
+ for (i = 0;i < avt->nb_seg; i += 2)
+ xmlXPathFreeCompExpr((xmlXPathCompExprPtr) avt->segments[i]);
+ for (i = 1;i < avt->nb_seg; i += 2)
+ if (avt->segments[i] != NULL)
+ xmlFree((xmlChar *) avt->segments[i]);
+ }
+ if (avt->nsList != NULL)
+ xmlFree(avt->nsList);
+ xmlFree(avt);
+}
+
+/**
+ * xsltFreeAVTList:
+ * @avt: pointer to an list of AVT structures
+ *
+ * Free up the memory associated to the attribute value templates
+ */
+void
+xsltFreeAVTList(void *avt) {
+ xsltAttrVTPtr cur = (xsltAttrVTPtr) avt, next;
+
+ while (cur != NULL) {
+ next = cur->next;
+ xsltFreeAttrVT(cur);
+ cur = next;
+ }
+}
+/**
+ * xsltSetAttrVTsegment:
+ * @ avt: pointer to an xsltAttrVT structure
+ * @ val: the value to be set to the next available segment
+ *
+ * Within xsltCompileAttr there are several places where a value
+ * needs to be added to the 'segments' array within the xsltAttrVT
+ * structure, and at each place the allocated size may have to be
+ * re-allocated. This routine takes care of that situation.
+ *
+ * Returns the avt pointer, which may have been changed by a re-alloc
+ */
+static xsltAttrVTPtr
+xsltSetAttrVTsegment(xsltAttrVTPtr avt, void *val) {
+ if (avt->nb_seg >= avt->max_seg) {
+ avt = (xsltAttrVTPtr) xmlRealloc(avt, sizeof(xsltAttrVT) +
+ avt->max_seg * sizeof(void *));
+ if (avt == NULL) {
+ return NULL;
+ }
+ memset(&avt->segments[avt->nb_seg], 0, MAX_AVT_SEG*sizeof(void *));
+ avt->max_seg += MAX_AVT_SEG;
+ }
+ avt->segments[avt->nb_seg++] = val;
+ return avt;
+}
+
+/**
+ * xsltCompileAttr:
+ * @style: a XSLT process context
+ * @attr: the attribute coming from the stylesheet.
+ *
+ * Precompile an attribute in a stylesheet, basically it checks if it is
+ * an attrubute value template, and if yes establish some structures needed
+ * to process it at transformation time.
+ */
+void
+xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) {
+ const xmlChar *str;
+ const xmlChar *cur;
+ xmlChar *ret = NULL;
+ xmlChar *expr = NULL;
+ xsltAttrVTPtr avt;
+ int i = 0, lastavt = 0;
+
+ if ((style == NULL) || (attr == NULL) || (attr->children == NULL))
+ return;
+ if ((attr->children->type != XML_TEXT_NODE) ||
+ (attr->children->next != NULL)) {
+ xsltTransformError(NULL, style, attr->parent,
+ "Attribute '%s': The content is expected to be a single text "
+ "node when compiling an AVT.\n", attr->name);
+ style->errors++;
+ return;
+ }
+ str = attr->children->content;
+ if ((xmlStrchr(str, '{') == NULL) &&
+ (xmlStrchr(str, '}') == NULL)) return;
+
+#ifdef WITH_XSLT_DEBUG_AVT
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Found AVT %s: %s\n", attr->name, str);
+#endif
+ if (attr->psvi != NULL) {
+#ifdef WITH_XSLT_DEBUG_AVT
+ xsltGenericDebug(xsltGenericDebugContext,
+ "AVT %s: already compiled\n", attr->name);
+#endif
+ return;
+ }
+ /*
+ * Create a new AVT object.
+ */
+ avt = xsltNewAttrVT(style);
+ if (avt == NULL)
+ return;
+ attr->psvi = avt;
+
+ avt->nsList = xmlGetNsList(attr->doc, attr->parent);
+ if (avt->nsList != NULL) {
+ while (avt->nsList[i] != NULL)
+ i++;
+ }
+ avt->nsNr = i;
+
+ cur = str;
+ while (*cur != 0) {
+ if (*cur == '{') {
+ if (*(cur+1) == '{') { /* escaped '{' */
+ cur++;
+ ret = xmlStrncat(ret, str, cur - str);
+ cur++;
+ str = cur;
+ continue;
+ }
+ if (*(cur+1) == '}') { /* skip empty AVT */
+ ret = xmlStrncat(ret, str, cur - str);
+ cur += 2;
+ str = cur;
+ continue;
+ }
+ if ((ret != NULL) || (cur - str > 0)) {
+ ret = xmlStrncat(ret, str, cur - str);
+ str = cur;
+ if (avt->nb_seg == 0)
+ avt->strstart = 1;
+ if ((avt = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL)
+ goto error;
+ ret = NULL;
+ lastavt = 0;
+ }
+
+ cur++;
+ while ((*cur != 0) && (*cur != '}')) {
+ /* Need to check for literal (bug539741) */
+ if ((*cur == '\'') || (*cur == '"')) {
+ char delim = *(cur++);
+ while ((*cur != 0) && (*cur != delim))
+ cur++;
+ if (*cur != 0)
+ cur++; /* skip the ending delimiter */
+ } else
+ cur++;
+ }
+ if (*cur == 0) {
+ xsltTransformError(NULL, style, attr->parent,
+ "Attribute '%s': The AVT has an unmatched '{'.\n",
+ attr->name);
+ style->errors++;
+ goto error;
+ }
+ str++;
+ expr = xmlStrndup(str, cur - str);
+ if (expr == NULL) {
+ /*
+ * TODO: What needs to be done here?
+ */
+ XSLT_TODO
+ goto error;
+ } else {
+ xmlXPathCompExprPtr comp;
+
+ comp = xsltXPathCompile(style, expr);
+ if (comp == NULL) {
+ xsltTransformError(NULL, style, attr->parent,
+ "Attribute '%s': Failed to compile the expression "
+ "'%s' in the AVT.\n", attr->name, expr);
+ style->errors++;
+ goto error;
+ }
+ if (avt->nb_seg == 0)
+ avt->strstart = 0;
+ if (lastavt == 1) {
+ if ((avt = xsltSetAttrVTsegment(avt, NULL)) == NULL)
+ goto error;
+ }
+ if ((avt = xsltSetAttrVTsegment(avt, (void *) comp)) == NULL)
+ goto error;
+ lastavt = 1;
+ xmlFree(expr);
+ expr = NULL;
+ }
+ cur++;
+ str = cur;
+ } else if (*cur == '}') {
+ cur++;
+ if (*cur == '}') { /* escaped '}' */
+ ret = xmlStrncat(ret, str, cur - str);
+ cur++;
+ str = cur;
+ continue;
+ } else {
+ xsltTransformError(NULL, style, attr->parent,
+ "Attribute '%s': The AVT has an unmatched '}'.\n",
+ attr->name);
+ goto error;
+ }
+ } else
+ cur++;
+ }
+ if ((ret != NULL) || (cur - str > 0)) {
+ ret = xmlStrncat(ret, str, cur - str);
+ str = cur;
+ if (avt->nb_seg == 0)
+ avt->strstart = 1;
+ if ((avt = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL)
+ goto error;
+ ret = NULL;
+ }
+
+error:
+ if (avt == NULL) {
+ xsltTransformError(NULL, style, attr->parent,
+ "xsltCompileAttr: malloc problem\n");
+ } else {
+ if (attr->psvi != avt) { /* may have changed from realloc */
+ attr->psvi = avt;
+ /*
+ * This is a "hack", but I can't see any clean method of
+ * doing it. If a re-alloc has taken place, then the pointer
+ * for this AVT may have changed. style->attVTs was set by
+ * xsltNewAttrVT, so it needs to be re-set to the new value!
+ */
+ style->attVTs = avt;
+ }
+ }
+ if (ret != NULL)
+ xmlFree(ret);
+ if (expr != NULL)
+ xmlFree(expr);
+}
+
+
+/**
+ * xsltEvalAVT:
+ * @ctxt: the XSLT transformation context
+ * @avt: the prevompiled attribute value template info
+ * @node: the node hosting the attribute
+ *
+ * Process the given AVT, and return the new string value.
+ *
+ * Returns the computed string value or NULL, must be deallocated by the
+ * caller.
+ */
+xmlChar *
+xsltEvalAVT(xsltTransformContextPtr ctxt, void *avt, xmlNodePtr node) {
+ xmlChar *ret = NULL, *tmp;
+ xmlXPathCompExprPtr comp;
+ xsltAttrVTPtr cur = (xsltAttrVTPtr) avt;
+ int i;
+ int str;
+
+ if ((ctxt == NULL) || (avt == NULL) || (node == NULL))
+ return(NULL);
+ str = cur->strstart;
+ for (i = 0;i < cur->nb_seg;i++) {
+ if (str) {
+ ret = xmlStrcat(ret, (const xmlChar *) cur->segments[i]);
+ } else {
+ comp = (xmlXPathCompExprPtr) cur->segments[i];
+ tmp = xsltEvalXPathStringNs(ctxt, comp, cur->nsNr, cur->nsList);
+ if (tmp != NULL) {
+ if (ret != NULL) {
+ ret = xmlStrcat(ret, tmp);
+ xmlFree(tmp);
+ } else {
+ ret = tmp;
+ }
+ }
+ }
+ str = !str;
+ }
+ return(ret);
+}
diff --git a/libxslt/documents.c b/libxslt/documents.c
new file mode 100644
index 0000000..3f3a731
--- /dev/null
+++ b/libxslt/documents.c
@@ -0,0 +1,434 @@
+/*
+ * documents.c: Implementation of the documents handling
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/hash.h>
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "xsltutils.h"
+#include "documents.h"
+#include "transform.h"
+#include "imports.h"
+#include "keys.h"
+#include "security.h"
+
+#ifdef LIBXML_XINCLUDE_ENABLED
+#include <libxml/xinclude.h>
+#endif
+
+#define WITH_XSLT_DEBUG_DOCUMENTS
+
+#ifdef WITH_XSLT_DEBUG
+#define WITH_XSLT_DEBUG_DOCUMENTS
+#endif
+
+/************************************************************************
+ * *
+ * Hooks for the document loader *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltDocDefaultLoaderFunc:
+ * @URI: the URI of the document to load
+ * @dict: the dictionary to use when parsing that document
+ * @options: parsing options, a set of xmlParserOption
+ * @ctxt: the context, either a stylesheet or a transformation context
+ * @type: the xsltLoadType indicating the kind of loading required
+ *
+ * Default function to load document not provided by the compilation or
+ * transformation API themselve, for example when an xsl:import,
+ * xsl:include is found at compilation time or when a document()
+ * call is made at runtime.
+ *
+ * Returns the pointer to the document (which will be modified and
+ * freed by the engine later), or NULL in case of error.
+ */
+static xmlDocPtr
+xsltDocDefaultLoaderFunc(const xmlChar * URI, xmlDictPtr dict, int options,
+ void *ctxt ATTRIBUTE_UNUSED,
+ xsltLoadType type ATTRIBUTE_UNUSED)
+{
+ xmlParserCtxtPtr pctxt;
+ xmlParserInputPtr inputStream;
+ xmlDocPtr doc;
+
+ pctxt = xmlNewParserCtxt();
+ if (pctxt == NULL)
+ return(NULL);
+ if ((dict != NULL) && (pctxt->dict != NULL)) {
+ xmlDictFree(pctxt->dict);
+ pctxt->dict = NULL;
+ }
+ if (dict != NULL) {
+ pctxt->dict = dict;
+ xmlDictReference(pctxt->dict);
+#ifdef WITH_XSLT_DEBUG
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Reusing dictionary for document\n");
+#endif
+ }
+ xmlCtxtUseOptions(pctxt, options);
+ inputStream = xmlLoadExternalEntity((const char *) URI, NULL, pctxt);
+ if (inputStream == NULL) {
+ xmlFreeParserCtxt(pctxt);
+ return(NULL);
+ }
+ inputPush(pctxt, inputStream);
+ if (pctxt->directory == NULL)
+ pctxt->directory = xmlParserGetDirectory((const char *) URI);
+
+ xmlParseDocument(pctxt);
+
+ if (pctxt->wellFormed) {
+ doc = pctxt->myDoc;
+ }
+ else {
+ doc = NULL;
+ xmlFreeDoc(pctxt->myDoc);
+ pctxt->myDoc = NULL;
+ }
+ xmlFreeParserCtxt(pctxt);
+
+ return(doc);
+}
+
+
+xsltDocLoaderFunc xsltDocDefaultLoader = xsltDocDefaultLoaderFunc;
+
+/**
+ * xsltSetLoaderFunc:
+ * @f: the new function to handle document loading.
+ *
+ * Set the new function to load document, if NULL it resets it to the
+ * default function.
+ */
+
+void
+xsltSetLoaderFunc(xsltDocLoaderFunc f) {
+ if (f == NULL)
+ xsltDocDefaultLoader = xsltDocDefaultLoaderFunc;
+ else
+ xsltDocDefaultLoader = f;
+}
+
+/************************************************************************
+ * *
+ * Module interfaces *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltNewDocument:
+ * @ctxt: an XSLT transformation context (or NULL)
+ * @doc: a parsed XML document
+ *
+ * Register a new document, apply key computations
+ *
+ * Returns a handler to the document
+ */
+xsltDocumentPtr
+xsltNewDocument(xsltTransformContextPtr ctxt, xmlDocPtr doc) {
+ xsltDocumentPtr cur;
+
+ cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument));
+ if (cur == NULL) {
+ xsltTransformError(ctxt, NULL, (xmlNodePtr) doc,
+ "xsltNewDocument : malloc failed\n");
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltDocument));
+ cur->doc = doc;
+ if (ctxt != NULL) {
+ if (! XSLT_IS_RES_TREE_FRAG(doc)) {
+ cur->next = ctxt->docList;
+ ctxt->docList = cur;
+ }
+ /*
+ * A key with a specific name for a specific document
+ * will only be computed if there's a call to the key()
+ * function using that specific name for that specific
+ * document. I.e. computation of keys will be done in
+ * xsltGetKey() (keys.c) on an on-demand basis.
+ *
+ * xsltInitCtxtKeys(ctxt, cur); not called here anymore
+ */
+ }
+ return(cur);
+}
+
+/**
+ * xsltNewStyleDocument:
+ * @style: an XSLT style sheet
+ * @doc: a parsed XML document
+ *
+ * Register a new document, apply key computations
+ *
+ * Returns a handler to the document
+ */
+xsltDocumentPtr
+xsltNewStyleDocument(xsltStylesheetPtr style, xmlDocPtr doc) {
+ xsltDocumentPtr cur;
+
+ cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument));
+ if (cur == NULL) {
+ xsltTransformError(NULL, style, (xmlNodePtr) doc,
+ "xsltNewStyleDocument : malloc failed\n");
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltDocument));
+ cur->doc = doc;
+ if (style != NULL) {
+ cur->next = style->docList;
+ style->docList = cur;
+ }
+ return(cur);
+}
+
+/**
+ * xsltFreeStyleDocuments:
+ * @style: an XSLT stylesheet (representing a stylesheet-level)
+ *
+ * Frees the node-trees (and xsltDocument structures) of all
+ * stylesheet-modules of the stylesheet-level represented by
+ * the given @style.
+ */
+void
+xsltFreeStyleDocuments(xsltStylesheetPtr style) {
+ xsltDocumentPtr doc, cur;
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+ xsltNsMapPtr nsMap;
+#endif
+
+ if (style == NULL)
+ return;
+
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+ if (XSLT_HAS_INTERNAL_NSMAP(style))
+ nsMap = XSLT_GET_INTERNAL_NSMAP(style);
+ else
+ nsMap = NULL;
+#endif
+
+ cur = style->docList;
+ while (cur != NULL) {
+ doc = cur;
+ cur = cur->next;
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+ /*
+ * Restore all changed namespace URIs of ns-decls.
+ */
+ if (nsMap)
+ xsltRestoreDocumentNamespaces(nsMap, doc->doc);
+#endif
+ xsltFreeDocumentKeys(doc);
+ if (!doc->main)
+ xmlFreeDoc(doc->doc);
+ xmlFree(doc);
+ }
+}
+
+/**
+ * xsltFreeDocuments:
+ * @ctxt: an XSLT transformation context
+ *
+ * Free up all the space used by the loaded documents
+ */
+void
+xsltFreeDocuments(xsltTransformContextPtr ctxt) {
+ xsltDocumentPtr doc, cur;
+
+ cur = ctxt->docList;
+ while (cur != NULL) {
+ doc = cur;
+ cur = cur->next;
+ xsltFreeDocumentKeys(doc);
+ if (!doc->main)
+ xmlFreeDoc(doc->doc);
+ xmlFree(doc);
+ }
+ cur = ctxt->styleList;
+ while (cur != NULL) {
+ doc = cur;
+ cur = cur->next;
+ xsltFreeDocumentKeys(doc);
+ if (!doc->main)
+ xmlFreeDoc(doc->doc);
+ xmlFree(doc);
+ }
+}
+
+/**
+ * xsltLoadDocument:
+ * @ctxt: an XSLT transformation context
+ * @URI: the computed URI of the document
+ *
+ * Try to load a document (not a stylesheet)
+ * within the XSLT transformation context
+ *
+ * Returns the new xsltDocumentPtr or NULL in case of error
+ */
+xsltDocumentPtr
+xsltLoadDocument(xsltTransformContextPtr ctxt, const xmlChar *URI) {
+ xsltDocumentPtr ret;
+ xmlDocPtr doc;
+
+ if ((ctxt == NULL) || (URI == NULL))
+ return(NULL);
+
+ /*
+ * Security framework check
+ */
+ if (ctxt->sec != NULL) {
+ int res;
+
+ res = xsltCheckRead(ctxt->sec, ctxt, URI);
+ if (res == 0) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "xsltLoadDocument: read rights for %s denied\n",
+ URI);
+ return(NULL);
+ }
+ }
+
+ /*
+ * Walk the context list to find the document if preparsed
+ */
+ ret = ctxt->docList;
+ while (ret != NULL) {
+ if ((ret->doc != NULL) && (ret->doc->URL != NULL) &&
+ (xmlStrEqual(ret->doc->URL, URI)))
+ return(ret);
+ ret = ret->next;
+ }
+
+ doc = xsltDocDefaultLoader(URI, ctxt->dict, ctxt->parserOptions,
+ (void *) ctxt, XSLT_LOAD_DOCUMENT);
+
+ if (doc == NULL)
+ return(NULL);
+
+ if (ctxt->xinclude != 0) {
+#ifdef LIBXML_XINCLUDE_ENABLED
+#if LIBXML_VERSION >= 20603
+ xmlXIncludeProcessFlags(doc, ctxt->parserOptions);
+#else
+ xmlXIncludeProcess(doc);
+#endif
+#else
+ xsltTransformError(ctxt, NULL, NULL,
+ "xsltLoadDocument(%s) : XInclude processing not compiled in\n",
+ URI);
+#endif
+ }
+ /*
+ * Apply white-space stripping if asked for
+ */
+ if (xsltNeedElemSpaceHandling(ctxt))
+ xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc));
+ if (ctxt->debugStatus == XSLT_DEBUG_NONE)
+ xmlXPathOrderDocElems(doc);
+
+ ret = xsltNewDocument(ctxt, doc);
+ return(ret);
+}
+
+/**
+ * xsltLoadStyleDocument:
+ * @style: an XSLT style sheet
+ * @URI: the computed URI of the document
+ *
+ * Try to load a stylesheet document within the XSLT transformation context
+ *
+ * Returns the new xsltDocumentPtr or NULL in case of error
+ */
+xsltDocumentPtr
+xsltLoadStyleDocument(xsltStylesheetPtr style, const xmlChar *URI) {
+ xsltDocumentPtr ret;
+ xmlDocPtr doc;
+ xsltSecurityPrefsPtr sec;
+
+ if ((style == NULL) || (URI == NULL))
+ return(NULL);
+
+ /*
+ * Security framework check
+ */
+ sec = xsltGetDefaultSecurityPrefs();
+ if (sec != NULL) {
+ int res;
+
+ res = xsltCheckRead(sec, NULL, URI);
+ if (res == 0) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltLoadStyleDocument: read rights for %s denied\n",
+ URI);
+ return(NULL);
+ }
+ }
+
+ /*
+ * Walk the context list to find the document if preparsed
+ */
+ ret = style->docList;
+ while (ret != NULL) {
+ if ((ret->doc != NULL) && (ret->doc->URL != NULL) &&
+ (xmlStrEqual(ret->doc->URL, URI)))
+ return(ret);
+ ret = ret->next;
+ }
+
+ doc = xsltDocDefaultLoader(URI, style->dict, XSLT_PARSE_OPTIONS,
+ (void *) style, XSLT_LOAD_STYLESHEET);
+ if (doc == NULL)
+ return(NULL);
+
+ ret = xsltNewStyleDocument(style, doc);
+ return(ret);
+}
+
+/**
+ * xsltFindDocument:
+ * @ctxt: an XSLT transformation context
+ * @doc: a parsed XML document
+ *
+ * Try to find a document within the XSLT transformation context.
+ * This will not find document infos for temporary
+ * Result Tree Fragments.
+ *
+ * Returns the desired xsltDocumentPtr or NULL in case of error
+ */
+xsltDocumentPtr
+xsltFindDocument (xsltTransformContextPtr ctxt, xmlDocPtr doc) {
+ xsltDocumentPtr ret;
+
+ if ((ctxt == NULL) || (doc == NULL))
+ return(NULL);
+
+ /*
+ * Walk the context list to find the document
+ */
+ ret = ctxt->docList;
+ while (ret != NULL) {
+ if (ret->doc == doc)
+ return(ret);
+ ret = ret->next;
+ }
+ if (doc == ctxt->style->doc)
+ return(ctxt->document);
+ return(NULL);
+}
+
diff --git a/libxslt/documents.h b/libxslt/documents.h
new file mode 100644
index 0000000..ae7c0ca
--- /dev/null
+++ b/libxslt/documents.h
@@ -0,0 +1,93 @@
+/*
+ * Summary: interface for the document handling
+ * Description: implements document loading and cache (multiple
+ * document() reference for the same resources must
+ * be equal.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_DOCUMENTS_H__
+#define __XML_XSLT_DOCUMENTS_H__
+
+#include <libxml/tree.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+XSLTPUBFUN xsltDocumentPtr XSLTCALL
+ xsltNewDocument (xsltTransformContextPtr ctxt,
+ xmlDocPtr doc);
+XSLTPUBFUN xsltDocumentPtr XSLTCALL
+ xsltLoadDocument (xsltTransformContextPtr ctxt,
+ const xmlChar *URI);
+XSLTPUBFUN xsltDocumentPtr XSLTCALL
+ xsltFindDocument (xsltTransformContextPtr ctxt,
+ xmlDocPtr doc);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeDocuments (xsltTransformContextPtr ctxt);
+
+XSLTPUBFUN xsltDocumentPtr XSLTCALL
+ xsltLoadStyleDocument (xsltStylesheetPtr style,
+ const xmlChar *URI);
+XSLTPUBFUN xsltDocumentPtr XSLTCALL
+ xsltNewStyleDocument (xsltStylesheetPtr style,
+ xmlDocPtr doc);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeStyleDocuments (xsltStylesheetPtr style);
+
+/*
+ * Hooks for document loading
+ */
+
+/**
+ * xsltLoadType:
+ *
+ * Enum defining the kind of loader requirement.
+ */
+typedef enum {
+ XSLT_LOAD_START = 0, /* loading for a top stylesheet */
+ XSLT_LOAD_STYLESHEET = 1, /* loading for a stylesheet include/import */
+ XSLT_LOAD_DOCUMENT = 2 /* loading document at transformation time */
+} xsltLoadType;
+
+/**
+ * xsltDocLoaderFunc:
+ * @URI: the URI of the document to load
+ * @dict: the dictionary to use when parsing that document
+ * @options: parsing options, a set of xmlParserOption
+ * @ctxt: the context, either a stylesheet or a transformation context
+ * @type: the xsltLoadType indicating the kind of loading required
+ *
+ * An xsltDocLoaderFunc is a signature for a function which can be
+ * registered to load document not provided by the compilation or
+ * transformation API themselve, for example when an xsl:import,
+ * xsl:include is found at compilation time or when a document()
+ * call is made at runtime.
+ *
+ * Returns the pointer to the document (which will be modified and
+ * freed by the engine later), or NULL in case of error.
+ */
+typedef xmlDocPtr (*xsltDocLoaderFunc) (const xmlChar *URI,
+ xmlDictPtr dict,
+ int options,
+ void *ctxt,
+ xsltLoadType type);
+
+XSLTPUBFUN void XSLTCALL
+ xsltSetLoaderFunc (xsltDocLoaderFunc f);
+
+/* the loader may be needed by extension libraries so it is exported */
+XSLTPUBVAR xsltDocLoaderFunc xsltDocDefaultLoader;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_DOCUMENTS_H__ */
+
diff --git a/libxslt/extensions.c b/libxslt/extensions.c
new file mode 100644
index 0000000..ae6eef0
--- /dev/null
+++ b/libxslt/extensions.c
@@ -0,0 +1,2365 @@
+/*
+ * extensions.c: Implemetation of the extensions support
+ *
+ * Reference:
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+#include <limits.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/hash.h>
+#include <libxml/xmlerror.h>
+#include <libxml/parserInternals.h>
+#include <libxml/xpathInternals.h>
+#ifdef WITH_MODULES
+#include <libxml/xmlmodule.h>
+#endif
+#include <libxml/list.h>
+#include <libxml/xmlIO.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "xsltutils.h"
+#include "imports.h"
+#include "extensions.h"
+
+#ifdef _WIN32
+#include <stdlib.h> /* for _MAX_PATH */
+#ifndef PATH_MAX
+#define PATH_MAX _MAX_PATH
+#endif
+#endif
+
+#ifdef WITH_XSLT_DEBUG
+#define WITH_XSLT_DEBUG_EXTENSIONS
+#endif
+
+/************************************************************************
+ * *
+ * Private Types and Globals *
+ * *
+ ************************************************************************/
+
+typedef struct _xsltExtDef xsltExtDef;
+typedef xsltExtDef *xsltExtDefPtr;
+struct _xsltExtDef {
+ struct _xsltExtDef *next;
+ xmlChar *prefix;
+ xmlChar *URI;
+ void *data;
+};
+
+typedef struct _xsltExtModule xsltExtModule;
+typedef xsltExtModule *xsltExtModulePtr;
+struct _xsltExtModule {
+ xsltExtInitFunction initFunc;
+ xsltExtShutdownFunction shutdownFunc;
+ xsltStyleExtInitFunction styleInitFunc;
+ xsltStyleExtShutdownFunction styleShutdownFunc;
+};
+
+typedef struct _xsltExtData xsltExtData;
+typedef xsltExtData *xsltExtDataPtr;
+struct _xsltExtData {
+ xsltExtModulePtr extModule;
+ void *extData;
+};
+
+typedef struct _xsltExtElement xsltExtElement;
+typedef xsltExtElement *xsltExtElementPtr;
+struct _xsltExtElement {
+ xsltPreComputeFunction precomp;
+ xsltTransformFunction transform;
+};
+
+static xmlHashTablePtr xsltExtensionsHash = NULL;
+static xmlHashTablePtr xsltFunctionsHash = NULL;
+static xmlHashTablePtr xsltElementsHash = NULL;
+static xmlHashTablePtr xsltTopLevelsHash = NULL;
+static xmlHashTablePtr xsltModuleHash = NULL;
+static xmlMutexPtr xsltExtMutex = NULL;
+
+/************************************************************************
+ * *
+ * Type functions *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltNewExtDef:
+ * @prefix: the extension prefix
+ * @URI: the namespace URI
+ *
+ * Create a new XSLT ExtDef
+ *
+ * Returns the newly allocated xsltExtDefPtr or NULL in case of error
+ */
+static xsltExtDefPtr
+xsltNewExtDef(const xmlChar * prefix, const xmlChar * URI)
+{
+ xsltExtDefPtr cur;
+
+ cur = (xsltExtDefPtr) xmlMalloc(sizeof(xsltExtDef));
+ if (cur == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltNewExtDef : malloc failed\n");
+ return (NULL);
+ }
+ memset(cur, 0, sizeof(xsltExtDef));
+ if (prefix != NULL)
+ cur->prefix = xmlStrdup(prefix);
+ if (URI != NULL)
+ cur->URI = xmlStrdup(URI);
+ return (cur);
+}
+
+/**
+ * xsltFreeExtDef:
+ * @extensiond: an XSLT extension definition
+ *
+ * Free up the memory allocated by @extensiond
+ */
+static void
+xsltFreeExtDef(xsltExtDefPtr extensiond)
+{
+ if (extensiond == NULL)
+ return;
+ if (extensiond->prefix != NULL)
+ xmlFree(extensiond->prefix);
+ if (extensiond->URI != NULL)
+ xmlFree(extensiond->URI);
+ xmlFree(extensiond);
+}
+
+/**
+ * xsltFreeExtDefList:
+ * @extensiond: an XSLT extension definition list
+ *
+ * Free up the memory allocated by all the elements of @extensiond
+ */
+static void
+xsltFreeExtDefList(xsltExtDefPtr extensiond)
+{
+ xsltExtDefPtr cur;
+
+ while (extensiond != NULL) {
+ cur = extensiond;
+ extensiond = extensiond->next;
+ xsltFreeExtDef(cur);
+ }
+}
+
+/**
+ * xsltNewExtModule:
+ * @initFunc: the module initialization function
+ * @shutdownFunc: the module shutdown function
+ * @styleInitFunc: the stylesheet module data allocator function
+ * @styleShutdownFunc: the stylesheet module data free function
+ *
+ * Create a new XSLT extension module
+ *
+ * Returns the newly allocated xsltExtModulePtr or NULL in case of error
+ */
+static xsltExtModulePtr
+xsltNewExtModule(xsltExtInitFunction initFunc,
+ xsltExtShutdownFunction shutdownFunc,
+ xsltStyleExtInitFunction styleInitFunc,
+ xsltStyleExtShutdownFunction styleShutdownFunc)
+{
+ xsltExtModulePtr cur;
+
+ cur = (xsltExtModulePtr) xmlMalloc(sizeof(xsltExtModule));
+ if (cur == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltNewExtModule : malloc failed\n");
+ return (NULL);
+ }
+ cur->initFunc = initFunc;
+ cur->shutdownFunc = shutdownFunc;
+ cur->styleInitFunc = styleInitFunc;
+ cur->styleShutdownFunc = styleShutdownFunc;
+ return (cur);
+}
+
+/**
+ * xsltFreeExtModule:
+ * @ext: an XSLT extension module
+ *
+ * Free up the memory allocated by @ext
+ */
+static void
+xsltFreeExtModule(xsltExtModulePtr ext)
+{
+ if (ext == NULL)
+ return;
+ xmlFree(ext);
+}
+
+/**
+ * xsltNewExtData:
+ * @extModule: the module
+ * @extData: the associated data
+ *
+ * Create a new XSLT extension module data wrapper
+ *
+ * Returns the newly allocated xsltExtDataPtr or NULL in case of error
+ */
+static xsltExtDataPtr
+xsltNewExtData(xsltExtModulePtr extModule, void *extData)
+{
+ xsltExtDataPtr cur;
+
+ if (extModule == NULL)
+ return (NULL);
+ cur = (xsltExtDataPtr) xmlMalloc(sizeof(xsltExtData));
+ if (cur == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltNewExtData : malloc failed\n");
+ return (NULL);
+ }
+ cur->extModule = extModule;
+ cur->extData = extData;
+ return (cur);
+}
+
+/**
+ * xsltFreeExtData:
+ * @ext: an XSLT extension module data wrapper
+ *
+ * Free up the memory allocated by @ext
+ */
+static void
+xsltFreeExtData(xsltExtDataPtr ext)
+{
+ if (ext == NULL)
+ return;
+ xmlFree(ext);
+}
+
+/**
+ * xsltNewExtElement:
+ * @precomp: the pre-computation function
+ * @transform: the transformation function
+ *
+ * Create a new XSLT extension element
+ *
+ * Returns the newly allocated xsltExtElementPtr or NULL in case of
+ * error
+ */
+static xsltExtElementPtr
+xsltNewExtElement(xsltPreComputeFunction precomp,
+ xsltTransformFunction transform)
+{
+ xsltExtElementPtr cur;
+
+ if (transform == NULL)
+ return (NULL);
+
+ cur = (xsltExtElementPtr) xmlMalloc(sizeof(xsltExtElement));
+ if (cur == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltNewExtElement : malloc failed\n");
+ return (NULL);
+ }
+ cur->precomp = precomp;
+ cur->transform = transform;
+ return (cur);
+}
+
+/**
+ * xsltFreeExtElement:
+ * @ext: an XSLT extension element
+ *
+ * Frees up the memory allocated by @ext
+ */
+static void
+xsltFreeExtElement(xsltExtElementPtr ext)
+{
+ if (ext == NULL)
+ return;
+ xmlFree(ext);
+}
+
+
+#ifdef WITH_MODULES
+typedef void (*exsltRegisterFunction) (void);
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+/**
+ * xsltExtModuleRegisterDynamic:
+ * @URI: the function or element namespace URI
+ *
+ * Dynamically loads an extension plugin when available.
+ *
+ * The plugin name is derived from the URI by removing the
+ * initial protocol designation, e.g. "http://", then converting
+ * the characters ".", "-", "/", and "\" into "_", the removing
+ * any trailing "/", then concatenating LIBXML_MODULE_EXTENSION.
+ *
+ * Plugins are loaded from the directory specified by the
+ * environment variable LIBXSLT_PLUGINS_PATH, or if NULL,
+ * by LIBXSLT_DEFAULT_PLUGINS_PATH() which is determined at
+ * compile time.
+ *
+ * Returns 0 if successful, -1 in case of error.
+ */
+
+static int
+xsltExtModuleRegisterDynamic(const xmlChar * URI)
+{
+
+ xmlModulePtr m;
+ exsltRegisterFunction regfunc;
+ xmlChar *ext_name;
+ char module_filename[PATH_MAX];
+ const xmlChar *ext_directory = NULL;
+ const xmlChar *protocol = NULL;
+ xmlChar *i, *regfunc_name;
+ void *vregfunc;
+ int rc;
+
+ /* check for bad inputs */
+ if (URI == NULL)
+ return (-1);
+
+ if (NULL == xsltModuleHash) {
+ xsltModuleHash = xmlHashCreate(5);
+ if (xsltModuleHash == NULL)
+ return (-1);
+ }
+
+ xmlMutexLock(xsltExtMutex);
+
+ /* have we attempted to register this module already? */
+ if (xmlHashLookup(xsltModuleHash, URI) != NULL) {
+ xmlMutexUnlock(xsltExtMutex);
+ return (-1);
+ }
+ xmlMutexUnlock(xsltExtMutex);
+
+ /* transform extension namespace into a module name */
+ protocol = xmlStrstr(URI, BAD_CAST "://");
+ if (protocol == NULL) {
+ ext_name = xmlStrdup(URI);
+ } else {
+ ext_name = xmlStrdup(protocol + 3);
+ }
+ if (ext_name == NULL) {
+ return (-1);
+ }
+
+ i = ext_name;
+ while ('\0' != *i) {
+ if (('/' == *i) || ('\\' == *i) || ('.' == *i) || ('-' == *i))
+ *i = '_';
+ i++;
+ }
+
+ /* Strip underscores from end of string. */
+ while (i > ext_name && *(i - 1) == '_') {
+ i--;
+ *i = '\0';
+ }
+
+ /* determine module directory */
+ ext_directory = (xmlChar *) getenv("LIBXSLT_PLUGINS_PATH");
+
+ if (NULL == ext_directory) {
+ ext_directory = BAD_CAST LIBXSLT_DEFAULT_PLUGINS_PATH();
+ if (NULL == ext_directory)
+ return (-1);
+ }
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ else
+ xsltGenericDebug(xsltGenericDebugContext,
+ "LIBXSLT_PLUGINS_PATH is %s\n", ext_directory);
+#endif
+
+ /* build the module filename, and confirm the module exists */
+ xmlStrPrintf((xmlChar *) module_filename, sizeof(module_filename),
+ BAD_CAST "%s/%s%s",
+ ext_directory, ext_name, LIBXML_MODULE_EXTENSION);
+
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Attempting to load plugin: %s for URI: %s\n",
+ module_filename, URI);
+#endif
+
+ if (1 != xmlCheckFilename(module_filename)) {
+
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xmlCheckFilename failed for plugin: %s\n", module_filename);
+#endif
+
+ xmlFree(ext_name);
+ return (-1);
+ }
+
+ /* attempt to open the module */
+ m = xmlModuleOpen(module_filename, 0);
+ if (NULL == m) {
+
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xmlModuleOpen failed for plugin: %s\n", module_filename);
+#endif
+
+ xmlFree(ext_name);
+ return (-1);
+ }
+
+ /* construct initialization func name */
+ regfunc_name = xmlStrdup(ext_name);
+ regfunc_name = xmlStrcat(regfunc_name, BAD_CAST "_init");
+
+ vregfunc = NULL;
+ rc = xmlModuleSymbol(m, (const char *) regfunc_name, &vregfunc);
+ regfunc = vregfunc;
+ if (0 == rc) {
+ /*
+ * Call the module's init function. Note that this function
+ * calls xsltRegisterExtModuleFull which will add the module
+ * to xsltExtensionsHash (together with it's entry points).
+ */
+ (*regfunc) ();
+
+ /* register this module in our hash */
+ xmlMutexLock(xsltExtMutex);
+ xmlHashAddEntry(xsltModuleHash, URI, (void *) m);
+ xmlMutexUnlock(xsltExtMutex);
+ } else {
+
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xmlModuleSymbol failed for plugin: %s, regfunc: %s\n",
+ module_filename, regfunc_name);
+#endif
+
+ /* if regfunc not found unload the module immediately */
+ xmlModuleClose(m);
+ }
+
+ xmlFree(ext_name);
+ xmlFree(regfunc_name);
+ return (NULL == regfunc) ? -1 : 0;
+}
+#else
+static int
+xsltExtModuleRegisterDynamic(const xmlChar * URI ATTRIBUTE_UNUSED)
+{
+ return -1;
+}
+#endif
+
+/************************************************************************
+ * *
+ * The stylesheet extension prefixes handling *
+ * *
+ ************************************************************************/
+
+
+/**
+ * xsltFreeExts:
+ * @style: an XSLT stylesheet
+ *
+ * Free up the memory used by XSLT extensions in a stylesheet
+ */
+void
+xsltFreeExts(xsltStylesheetPtr style)
+{
+ if (style->nsDefs != NULL)
+ xsltFreeExtDefList((xsltExtDefPtr) style->nsDefs);
+}
+
+/**
+ * xsltRegisterExtPrefix:
+ * @style: an XSLT stylesheet
+ * @prefix: the prefix used (optional)
+ * @URI: the URI associated to the extension
+ *
+ * Registers an extension namespace
+ * This is called from xslt.c during compile-time.
+ * The given prefix is not needed.
+ * Called by:
+ * xsltParseExtElemPrefixes() (new function)
+ * xsltRegisterExtPrefix() (old function)
+ *
+ * Returns 0 in case of success, 1 if the @URI was already
+ * registered as an extension namespace and
+ * -1 in case of failure
+ */
+int
+xsltRegisterExtPrefix(xsltStylesheetPtr style,
+ const xmlChar * prefix, const xmlChar * URI)
+{
+ xsltExtDefPtr def, ret;
+
+ if ((style == NULL) || (URI == NULL))
+ return (-1);
+
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Registering extension namespace '%s'.\n", URI);
+#endif
+ def = (xsltExtDefPtr) style->nsDefs;
+#ifdef XSLT_REFACTORED
+ /*
+ * The extension is associated with a namespace name.
+ */
+ while (def != NULL) {
+ if (xmlStrEqual(URI, def->URI))
+ return (1);
+ def = def->next;
+ }
+#else
+ while (def != NULL) {
+ if (xmlStrEqual(prefix, def->prefix))
+ return (-1);
+ def = def->next;
+ }
+#endif
+ ret = xsltNewExtDef(prefix, URI);
+ if (ret == NULL)
+ return (-1);
+ ret->next = (xsltExtDefPtr) style->nsDefs;
+ style->nsDefs = ret;
+
+ /*
+ * check whether there is an extension module with a stylesheet
+ * initialization function.
+ */
+#ifdef XSLT_REFACTORED
+ /*
+ * Don't initialize modules based on specified namespaces via
+ * the attribute "[xsl:]extension-element-prefixes".
+ */
+#else
+ if (xsltExtensionsHash != NULL) {
+ xsltExtModulePtr module;
+
+ xmlMutexLock(xsltExtMutex);
+ module = xmlHashLookup(xsltExtensionsHash, URI);
+ xmlMutexUnlock(xsltExtMutex);
+ if (NULL == module) {
+ if (!xsltExtModuleRegisterDynamic(URI)) {
+ xmlMutexLock(xsltExtMutex);
+ module = xmlHashLookup(xsltExtensionsHash, URI);
+ xmlMutexUnlock(xsltExtMutex);
+ }
+ }
+ if (module != NULL) {
+ xsltStyleGetExtData(style, URI);
+ }
+ }
+#endif
+ return (0);
+}
+
+/************************************************************************
+ * *
+ * The extensions modules interfaces *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltRegisterExtFunction:
+ * @ctxt: an XSLT transformation context
+ * @name: the name of the element
+ * @URI: the URI associated to the element
+ * @function: the actual implementation which should be called
+ *
+ * Registers an extension function
+ *
+ * Returns 0 in case of success, -1 in case of failure
+ */
+int
+xsltRegisterExtFunction(xsltTransformContextPtr ctxt, const xmlChar * name,
+ const xmlChar * URI, xmlXPathFunction function)
+{
+ int ret;
+
+ if ((ctxt == NULL) || (name == NULL) ||
+ (URI == NULL) || (function == NULL))
+ return (-1);
+ if (ctxt->xpathCtxt != NULL) {
+ xmlXPathRegisterFuncNS(ctxt->xpathCtxt, name, URI, function);
+ }
+ if (ctxt->extFunctions == NULL)
+ ctxt->extFunctions = xmlHashCreate(10);
+ if (ctxt->extFunctions == NULL)
+ return (-1);
+
+ ret = xmlHashAddEntry2(ctxt->extFunctions, name, URI,
+ XML_CAST_FPTR(function));
+
+ return(ret);
+}
+
+/**
+ * xsltRegisterExtElement:
+ * @ctxt: an XSLT transformation context
+ * @name: the name of the element
+ * @URI: the URI associated to the element
+ * @function: the actual implementation which should be called
+ *
+ * Registers an extension element
+ *
+ * Returns 0 in case of success, -1 in case of failure
+ */
+int
+xsltRegisterExtElement(xsltTransformContextPtr ctxt, const xmlChar * name,
+ const xmlChar * URI, xsltTransformFunction function)
+{
+ if ((ctxt == NULL) || (name == NULL) ||
+ (URI == NULL) || (function == NULL))
+ return (-1);
+ if (ctxt->extElements == NULL)
+ ctxt->extElements = xmlHashCreate(10);
+ if (ctxt->extElements == NULL)
+ return (-1);
+ return (xmlHashAddEntry2
+ (ctxt->extElements, name, URI, XML_CAST_FPTR(function)));
+}
+
+/**
+ * xsltFreeCtxtExts:
+ * @ctxt: an XSLT transformation context
+ *
+ * Free the XSLT extension data
+ */
+void
+xsltFreeCtxtExts(xsltTransformContextPtr ctxt)
+{
+ if (ctxt->extElements != NULL)
+ xmlHashFree(ctxt->extElements, NULL);
+ if (ctxt->extFunctions != NULL)
+ xmlHashFree(ctxt->extFunctions, NULL);
+}
+
+/**
+ * xsltStyleGetStylesheetExtData:
+ * @style: an XSLT stylesheet
+ * @URI: the URI associated to the exension module
+ *
+ * Fires the compile-time initialization callback
+ * of an extension module and returns a container
+ * holding the user-data (retrieved via the callback).
+ *
+ * Returns the create module-data container
+ * or NULL if such a module was not registered.
+ */
+static xsltExtDataPtr
+xsltStyleInitializeStylesheetModule(xsltStylesheetPtr style,
+ const xmlChar * URI)
+{
+ xsltExtDataPtr dataContainer;
+ void *userData = NULL;
+ xsltExtModulePtr module;
+
+ if ((style == NULL) || (URI == NULL))
+ return(NULL);
+
+ if (xsltExtensionsHash == NULL) {
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Not registered extension module: %s\n", URI);
+#endif
+ return(NULL);
+ }
+
+ xmlMutexLock(xsltExtMutex);
+
+ module = xmlHashLookup(xsltExtensionsHash, URI);
+
+ xmlMutexUnlock(xsltExtMutex);
+
+ if (module == NULL) {
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Not registered extension module: %s\n", URI);
+#endif
+ return (NULL);
+ }
+ /*
+ * The specified module was registered so initialize it.
+ */
+ if (style->extInfos == NULL) {
+ style->extInfos = xmlHashCreate(10);
+ if (style->extInfos == NULL)
+ return (NULL);
+ }
+ /*
+ * Fire the initialization callback if available.
+ */
+ if (module->styleInitFunc == NULL) {
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Initializing module with *no* callback: %s\n", URI);
+#endif
+ } else {
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Initializing module with callback: %s\n", URI);
+#endif
+ /*
+ * Fire the initialization callback.
+ */
+ userData = module->styleInitFunc(style, URI);
+ }
+ /*
+ * Store the user-data in the context of the given stylesheet.
+ */
+ dataContainer = xsltNewExtData(module, userData);
+ if (dataContainer == NULL)
+ return (NULL);
+
+ if (xmlHashAddEntry(style->extInfos, URI,
+ (void *) dataContainer) < 0)
+ {
+ xsltTransformError(NULL, style, NULL,
+ "Failed to register module '%s'.\n", URI);
+ style->errors++;
+ if (module->styleShutdownFunc)
+ module->styleShutdownFunc(style, URI, userData);
+ xsltFreeExtData(dataContainer);
+ return (NULL);
+ }
+
+ return(dataContainer);
+}
+
+/**
+ * xsltStyleGetExtData:
+ * @style: an XSLT stylesheet
+ * @URI: the URI associated to the exension module
+ *
+ * Retrieve the data associated to the extension module
+ * in this given stylesheet.
+ * Called by:
+ * xsltRegisterExtPrefix(),
+ * ( xsltExtElementPreCompTest(), xsltExtInitTest )
+ *
+ * Returns the pointer or NULL if not present
+ */
+void *
+xsltStyleGetExtData(xsltStylesheetPtr style, const xmlChar * URI)
+{
+ xsltExtDataPtr dataContainer = NULL;
+ xsltStylesheetPtr tmpStyle;
+
+ if ((style == NULL) || (URI == NULL) ||
+ (xsltExtensionsHash == NULL))
+ return (NULL);
+
+
+#ifdef XSLT_REFACTORED
+ /*
+ * This is intended for global storage, so only the main
+ * stylesheet will hold the data.
+ */
+ tmpStyle = style;
+ while (tmpStyle->parent != NULL)
+ tmpStyle = tmpStyle->parent;
+ if (tmpStyle->extInfos != NULL) {
+ dataContainer =
+ (xsltExtDataPtr) xmlHashLookup(tmpStyle->extInfos, URI);
+ if (dataContainer != NULL) {
+ /*
+ * The module was already initialized in the context
+ * of this stylesheet; just return the user-data that
+ * comes with it.
+ */
+ return(dataContainer->extData);
+ }
+ }
+#else
+ /*
+ * Old behaviour.
+ */
+ tmpStyle = style;
+ while (tmpStyle != NULL) {
+ if (tmpStyle->extInfos != NULL) {
+ dataContainer =
+ (xsltExtDataPtr) xmlHashLookup(tmpStyle->extInfos, URI);
+ if (dataContainer != NULL) {
+ return(dataContainer->extData);
+ }
+ }
+ tmpStyle = xsltNextImport(tmpStyle);
+ }
+ tmpStyle = style;
+#endif
+
+ dataContainer =
+ xsltStyleInitializeStylesheetModule(tmpStyle, URI);
+ if (dataContainer != NULL)
+ return (dataContainer->extData);
+ return(NULL);
+}
+
+#ifdef XSLT_REFACTORED
+/**
+ * xsltStyleStylesheetLevelGetExtData:
+ * @style: an XSLT stylesheet
+ * @URI: the URI associated to the exension module
+ *
+ * Retrieve the data associated to the extension module in this given
+ * stylesheet.
+ *
+ * Returns the pointer or NULL if not present
+ */
+void *
+xsltStyleStylesheetLevelGetExtData(xsltStylesheetPtr style,
+ const xmlChar * URI)
+{
+ xsltExtDataPtr dataContainer = NULL;
+
+ if ((style == NULL) || (URI == NULL) ||
+ (xsltExtensionsHash == NULL))
+ return (NULL);
+
+ if (style->extInfos != NULL) {
+ dataContainer = (xsltExtDataPtr) xmlHashLookup(style->extInfos, URI);
+ /*
+ * The module was already initialized in the context
+ * of this stylesheet; just return the user-data that
+ * comes with it.
+ */
+ if (dataContainer)
+ return(dataContainer->extData);
+ }
+
+ dataContainer =
+ xsltStyleInitializeStylesheetModule(style, URI);
+ if (dataContainer != NULL)
+ return (dataContainer->extData);
+ return(NULL);
+}
+#endif
+
+/**
+ * xsltGetExtData:
+ * @ctxt: an XSLT transformation context
+ * @URI: the URI associated to the exension module
+ *
+ * Retrieve the data associated to the extension module in this given
+ * transformation.
+ *
+ * Returns the pointer or NULL if not present
+ */
+void *
+xsltGetExtData(xsltTransformContextPtr ctxt, const xmlChar * URI)
+{
+ xsltExtDataPtr data;
+
+ if ((ctxt == NULL) || (URI == NULL))
+ return (NULL);
+ if (ctxt->extInfos == NULL) {
+ ctxt->extInfos = xmlHashCreate(10);
+ if (ctxt->extInfos == NULL)
+ return (NULL);
+ data = NULL;
+ } else {
+ data = (xsltExtDataPtr) xmlHashLookup(ctxt->extInfos, URI);
+ }
+ if (data == NULL) {
+ void *extData;
+ xsltExtModulePtr module;
+
+ xmlMutexLock(xsltExtMutex);
+
+ module = xmlHashLookup(xsltExtensionsHash, URI);
+
+ xmlMutexUnlock(xsltExtMutex);
+
+ if (module == NULL) {
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Not registered extension module: %s\n", URI);
+#endif
+ return (NULL);
+ } else {
+ if (module->initFunc == NULL)
+ return (NULL);
+
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Initializing module: %s\n", URI);
+#endif
+
+ extData = module->initFunc(ctxt, URI);
+ if (extData == NULL)
+ return (NULL);
+
+ data = xsltNewExtData(module, extData);
+ if (data == NULL)
+ return (NULL);
+ if (xmlHashAddEntry(ctxt->extInfos, URI, (void *) data) < 0) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "Failed to register module data: %s\n",
+ URI);
+ if (module->shutdownFunc)
+ module->shutdownFunc(ctxt, URI, extData);
+ xsltFreeExtData(data);
+ return (NULL);
+ }
+ }
+ }
+ return (data->extData);
+}
+
+typedef struct _xsltInitExtCtxt xsltInitExtCtxt;
+struct _xsltInitExtCtxt {
+ xsltTransformContextPtr ctxt;
+ int ret;
+};
+
+/**
+ * xsltInitCtxtExt:
+ * @styleData: the registered stylesheet data for the module
+ * @ctxt: the XSLT transformation context + the return value
+ * @URI: the extension URI
+ *
+ * Initializes an extension module
+ */
+static void
+xsltInitCtxtExt(xsltExtDataPtr styleData, xsltInitExtCtxt * ctxt,
+ const xmlChar * URI)
+{
+ xsltExtModulePtr module;
+ xsltExtDataPtr ctxtData;
+ void *extData;
+
+ if ((styleData == NULL) || (ctxt == NULL) || (URI == NULL) ||
+ (ctxt->ret == -1)) {
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltInitCtxtExt: NULL param or error\n");
+#endif
+ return;
+ }
+ module = styleData->extModule;
+ if ((module == NULL) || (module->initFunc == NULL)) {
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltInitCtxtExt: no module or no initFunc\n");
+#endif
+ return;
+ }
+
+ ctxtData = (xsltExtDataPtr) xmlHashLookup(ctxt->ctxt->extInfos, URI);
+ if (ctxtData != NULL) {
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltInitCtxtExt: already initialized\n");
+#endif
+ return;
+ }
+
+ extData = module->initFunc(ctxt->ctxt, URI);
+ if (extData == NULL) {
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltInitCtxtExt: no extData\n");
+#endif
+ }
+ ctxtData = xsltNewExtData(module, extData);
+ if (ctxtData == NULL) {
+ ctxt->ret = -1;
+ return;
+ }
+
+ if (ctxt->ctxt->extInfos == NULL)
+ ctxt->ctxt->extInfos = xmlHashCreate(10);
+ if (ctxt->ctxt->extInfos == NULL) {
+ ctxt->ret = -1;
+ return;
+ }
+
+ if (xmlHashAddEntry(ctxt->ctxt->extInfos, URI, ctxtData) < 0) {
+ xsltGenericError(xsltGenericErrorContext,
+ "Failed to register module data: %s\n", URI);
+ if (module->shutdownFunc)
+ module->shutdownFunc(ctxt->ctxt, URI, extData);
+ xsltFreeExtData(ctxtData);
+ ctxt->ret = -1;
+ return;
+ }
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext, "Registered module %s\n",
+ URI);
+#endif
+ ctxt->ret++;
+}
+
+/**
+ * xsltInitCtxtExts:
+ * @ctxt: an XSLT transformation context
+ *
+ * Initialize the set of modules with registered stylesheet data
+ *
+ * Returns the number of modules initialized or -1 in case of error
+ */
+int
+xsltInitCtxtExts(xsltTransformContextPtr ctxt)
+{
+ xsltStylesheetPtr style;
+ xsltInitExtCtxt ctx;
+
+ if (ctxt == NULL)
+ return (-1);
+
+ style = ctxt->style;
+ if (style == NULL)
+ return (-1);
+
+ ctx.ctxt = ctxt;
+ ctx.ret = 0;
+
+ while (style != NULL) {
+ if (style->extInfos != NULL) {
+ xmlHashScan(style->extInfos,
+ (xmlHashScanner) xsltInitCtxtExt, &ctx);
+ if (ctx.ret == -1)
+ return (-1);
+ }
+ style = xsltNextImport(style);
+ }
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext, "Registered %d modules\n",
+ ctx.ret);
+#endif
+ return (ctx.ret);
+}
+
+/**
+ * xsltShutdownCtxtExt:
+ * @data: the registered data for the module
+ * @ctxt: the XSLT transformation context
+ * @URI: the extension URI
+ *
+ * Shutdown an extension module loaded
+ */
+static void
+xsltShutdownCtxtExt(xsltExtDataPtr data, xsltTransformContextPtr ctxt,
+ const xmlChar * URI)
+{
+ xsltExtModulePtr module;
+
+ if ((data == NULL) || (ctxt == NULL) || (URI == NULL))
+ return;
+ module = data->extModule;
+ if ((module == NULL) || (module->shutdownFunc == NULL))
+ return;
+
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Shutting down module : %s\n", URI);
+#endif
+ module->shutdownFunc(ctxt, URI, data->extData);
+}
+
+/**
+ * xsltShutdownCtxtExts:
+ * @ctxt: an XSLT transformation context
+ *
+ * Shutdown the set of modules loaded
+ */
+void
+xsltShutdownCtxtExts(xsltTransformContextPtr ctxt)
+{
+ if (ctxt == NULL)
+ return;
+ if (ctxt->extInfos == NULL)
+ return;
+ xmlHashScan(ctxt->extInfos, (xmlHashScanner) xsltShutdownCtxtExt,
+ ctxt);
+ xmlHashFree(ctxt->extInfos, (xmlHashDeallocator) xsltFreeExtData);
+ ctxt->extInfos = NULL;
+}
+
+/**
+ * xsltShutdownExt:
+ * @data: the registered data for the module
+ * @ctxt: the XSLT stylesheet
+ * @URI: the extension URI
+ *
+ * Shutdown an extension module loaded
+ */
+static void
+xsltShutdownExt(xsltExtDataPtr data, xsltStylesheetPtr style,
+ const xmlChar * URI)
+{
+ xsltExtModulePtr module;
+
+ if ((data == NULL) || (style == NULL) || (URI == NULL))
+ return;
+ module = data->extModule;
+ if ((module == NULL) || (module->styleShutdownFunc == NULL))
+ return;
+
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Shutting down module : %s\n", URI);
+#endif
+ module->styleShutdownFunc(style, URI, data->extData);
+ /*
+ * Don't remove the entry from the hash table here, since
+ * this will produce segfaults - this fixes bug #340624.
+ *
+ * xmlHashRemoveEntry(style->extInfos, URI,
+ * (xmlHashDeallocator) xsltFreeExtData);
+ */
+}
+
+/**
+ * xsltShutdownExts:
+ * @style: an XSLT stylesheet
+ *
+ * Shutdown the set of modules loaded
+ */
+void
+xsltShutdownExts(xsltStylesheetPtr style)
+{
+ if (style == NULL)
+ return;
+ if (style->extInfos == NULL)
+ return;
+ xmlHashScan(style->extInfos, (xmlHashScanner) xsltShutdownExt, style);
+ xmlHashFree(style->extInfos, (xmlHashDeallocator) xsltFreeExtData);
+ style->extInfos = NULL;
+}
+
+/**
+ * xsltCheckExtPrefix:
+ * @style: the stylesheet
+ * @URI: the namespace prefix (possibly NULL)
+ *
+ * Check if the given prefix is one of the declared extensions.
+ * This is intended to be called only at compile-time.
+ * Called by:
+ * xsltGetInheritedNsList() (xslt.c)
+ * xsltParseTemplateContent (xslt.c)
+ *
+ * Returns 1 if this is an extension, 0 otherwise
+ */
+int
+xsltCheckExtPrefix(xsltStylesheetPtr style, const xmlChar * URI)
+{
+#ifdef XSLT_REFACTORED
+ if ((style == NULL) || (style->compCtxt == NULL) ||
+ (XSLT_CCTXT(style)->inode == NULL) ||
+ (XSLT_CCTXT(style)->inode->extElemNs == NULL))
+ return (0);
+ /*
+ * Lookup the extension namespaces registered
+ * at the current node in the stylesheet's tree.
+ */
+ if (XSLT_CCTXT(style)->inode->extElemNs != NULL) {
+ int i;
+ xsltPointerListPtr list = XSLT_CCTXT(style)->inode->extElemNs;
+
+ for (i = 0; i < list->number; i++) {
+ if (xmlStrEqual((const xmlChar *) list->items[i],
+ URI))
+ {
+ return(1);
+ }
+ }
+ }
+#else
+ xsltExtDefPtr cur;
+
+ if ((style == NULL) || (style->nsDefs == NULL))
+ return (0);
+ if (URI == NULL)
+ URI = BAD_CAST "#default";
+ cur = (xsltExtDefPtr) style->nsDefs;
+ while (cur != NULL) {
+ /*
+ * NOTE: This was change to work on namespace names rather
+ * than namespace prefixes. This fixes bug #339583.
+ * TODO: Consider renaming the field "prefix" of xsltExtDef
+ * to "href".
+ */
+ if (xmlStrEqual(URI, cur->prefix))
+ return (1);
+ cur = cur->next;
+ }
+#endif
+ return (0);
+}
+
+/**
+ * xsltCheckExtURI:
+ * @style: the stylesheet
+ * @URI: the namespace URI (possibly NULL)
+ *
+ * Check if the given prefix is one of the declared extensions.
+ * This is intended to be called only at compile-time.
+ * Called by:
+ * xsltPrecomputeStylesheet() (xslt.c)
+ * xsltParseTemplateContent (xslt.c)
+ *
+ * Returns 1 if this is an extension, 0 otherwise
+ */
+int
+xsltCheckExtURI(xsltStylesheetPtr style, const xmlChar * URI)
+{
+ xsltExtDefPtr cur;
+
+ if ((style == NULL) || (style->nsDefs == NULL))
+ return (0);
+ if (URI == NULL)
+ return (0);
+ cur = (xsltExtDefPtr) style->nsDefs;
+ while (cur != NULL) {
+ if (xmlStrEqual(URI, cur->URI))
+ return (1);
+ cur = cur->next;
+ }
+ return (0);
+}
+
+/**
+ * xsltRegisterExtModuleFull:
+ * @URI: URI associated to this module
+ * @initFunc: the module initialization function
+ * @shutdownFunc: the module shutdown function
+ * @styleInitFunc: the module initialization function
+ * @styleShutdownFunc: the module shutdown function
+ *
+ * Register an XSLT extension module to the library.
+ *
+ * Returns 0 if sucessful, -1 in case of error
+ */
+int
+xsltRegisterExtModuleFull(const xmlChar * URI,
+ xsltExtInitFunction initFunc,
+ xsltExtShutdownFunction shutdownFunc,
+ xsltStyleExtInitFunction styleInitFunc,
+ xsltStyleExtShutdownFunction styleShutdownFunc)
+{
+ int ret;
+ xsltExtModulePtr module;
+
+ if ((URI == NULL) || (initFunc == NULL))
+ return (-1);
+ if (xsltExtensionsHash == NULL)
+ xsltExtensionsHash = xmlHashCreate(10);
+
+ if (xsltExtensionsHash == NULL)
+ return (-1);
+
+ xmlMutexLock(xsltExtMutex);
+
+ module = xmlHashLookup(xsltExtensionsHash, URI);
+ if (module != NULL) {
+ if ((module->initFunc == initFunc) &&
+ (module->shutdownFunc == shutdownFunc))
+ ret = 0;
+ else
+ ret = -1;
+ goto done;
+ }
+ module = xsltNewExtModule(initFunc, shutdownFunc,
+ styleInitFunc, styleShutdownFunc);
+ if (module == NULL) {
+ ret = -1;
+ goto done;
+ }
+ ret = xmlHashAddEntry(xsltExtensionsHash, URI, (void *) module);
+
+done:
+ xmlMutexUnlock(xsltExtMutex);
+ return (ret);
+}
+
+/**
+ * xsltRegisterExtModule:
+ * @URI: URI associated to this module
+ * @initFunc: the module initialization function
+ * @shutdownFunc: the module shutdown function
+ *
+ * Register an XSLT extension module to the library.
+ *
+ * Returns 0 if sucessful, -1 in case of error
+ */
+int
+xsltRegisterExtModule(const xmlChar * URI,
+ xsltExtInitFunction initFunc,
+ xsltExtShutdownFunction shutdownFunc)
+{
+ return xsltRegisterExtModuleFull(URI, initFunc, shutdownFunc,
+ NULL, NULL);
+}
+
+/**
+ * xsltUnregisterExtModule:
+ * @URI: URI associated to this module
+ *
+ * Unregister an XSLT extension module from the library.
+ *
+ * Returns 0 if sucessful, -1 in case of error
+ */
+int
+xsltUnregisterExtModule(const xmlChar * URI)
+{
+ int ret;
+
+ if (URI == NULL)
+ return (-1);
+ if (xsltExtensionsHash == NULL)
+ return (-1);
+
+ xmlMutexLock(xsltExtMutex);
+
+ ret = xmlHashRemoveEntry(xsltExtensionsHash, URI,
+ (xmlHashDeallocator) xsltFreeExtModule);
+
+ xmlMutexUnlock(xsltExtMutex);
+
+ return (ret);
+}
+
+/**
+ * xsltUnregisterAllExtModules:
+ *
+ * Unregister all the XSLT extension module from the library.
+ */
+static void
+xsltUnregisterAllExtModules(void)
+{
+ if (xsltExtensionsHash == NULL)
+ return;
+
+ xmlMutexLock(xsltExtMutex);
+
+ xmlHashFree(xsltExtensionsHash,
+ (xmlHashDeallocator) xsltFreeExtModule);
+ xsltExtensionsHash = NULL;
+
+ xmlMutexUnlock(xsltExtMutex);
+}
+
+/**
+ * xsltXPathGetTransformContext:
+ * @ctxt: an XPath transformation context
+ *
+ * Provides the XSLT transformation context from the XPath transformation
+ * context. This is useful when an XPath function in the extension module
+ * is called by the XPath interpreter and that the XSLT context is needed
+ * for example to retrieve the associated data pertaining to this XSLT
+ * transformation.
+ *
+ * Returns the XSLT transformation context or NULL in case of error.
+ */
+xsltTransformContextPtr
+xsltXPathGetTransformContext(xmlXPathParserContextPtr ctxt)
+{
+ if ((ctxt == NULL) || (ctxt->context == NULL))
+ return (NULL);
+ return (ctxt->context->extra);
+}
+
+/**
+ * xsltRegisterExtModuleFunction:
+ * @name: the function name
+ * @URI: the function namespace URI
+ * @function: the function callback
+ *
+ * Registers an extension module function.
+ *
+ * Returns 0 if successful, -1 in case of error.
+ */
+int
+xsltRegisterExtModuleFunction(const xmlChar * name, const xmlChar * URI,
+ xmlXPathFunction function)
+{
+ if ((name == NULL) || (URI == NULL) || (function == NULL))
+ return (-1);
+
+ if (xsltFunctionsHash == NULL)
+ xsltFunctionsHash = xmlHashCreate(10);
+ if (xsltFunctionsHash == NULL)
+ return (-1);
+
+ xmlMutexLock(xsltExtMutex);
+
+ xmlHashUpdateEntry2(xsltFunctionsHash, name, URI,
+ XML_CAST_FPTR(function), NULL);
+
+ xmlMutexUnlock(xsltExtMutex);
+
+ return (0);
+}
+
+/**
+ * xsltExtModuleFunctionLookup:
+ * @name: the function name
+ * @URI: the function namespace URI
+ *
+ * Looks up an extension module function
+ *
+ * Returns the function if found, NULL otherwise.
+ */
+xmlXPathFunction
+xsltExtModuleFunctionLookup(const xmlChar * name, const xmlChar * URI)
+{
+ xmlXPathFunction ret;
+
+ if ((xsltFunctionsHash == NULL) || (name == NULL) || (URI == NULL))
+ return (NULL);
+
+ xmlMutexLock(xsltExtMutex);
+
+ XML_CAST_FPTR(ret) = xmlHashLookup2(xsltFunctionsHash, name, URI);
+
+ xmlMutexUnlock(xsltExtMutex);
+
+ /* if lookup fails, attempt a dynamic load on supported platforms */
+ if (NULL == ret) {
+ if (!xsltExtModuleRegisterDynamic(URI)) {
+ xmlMutexLock(xsltExtMutex);
+
+ XML_CAST_FPTR(ret) =
+ xmlHashLookup2(xsltFunctionsHash, name, URI);
+
+ xmlMutexUnlock(xsltExtMutex);
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * xsltUnregisterExtModuleFunction:
+ * @name: the function name
+ * @URI: the function namespace URI
+ *
+ * Unregisters an extension module function
+ *
+ * Returns 0 if successful, -1 in case of error.
+ */
+int
+xsltUnregisterExtModuleFunction(const xmlChar * name, const xmlChar * URI)
+{
+ int ret;
+
+ if ((xsltFunctionsHash == NULL) || (name == NULL) || (URI == NULL))
+ return (-1);
+
+ xmlMutexLock(xsltExtMutex);
+
+ ret = xmlHashRemoveEntry2(xsltFunctionsHash, name, URI, NULL);
+
+ xmlMutexUnlock(xsltExtMutex);
+
+ return(ret);
+}
+
+/**
+ * xsltUnregisterAllExtModuleFunction:
+ *
+ * Unregisters all extension module function
+ */
+static void
+xsltUnregisterAllExtModuleFunction(void)
+{
+ xmlMutexLock(xsltExtMutex);
+
+ xmlHashFree(xsltFunctionsHash, NULL);
+ xsltFunctionsHash = NULL;
+
+ xmlMutexUnlock(xsltExtMutex);
+}
+
+
+/**
+ * xsltNewElemPreComp:
+ * @style: the XSLT stylesheet
+ * @inst: the element node
+ * @function: the transform function
+ *
+ * Creates and initializes an #xsltElemPreComp
+ *
+ * Returns the new and initialized #xsltElemPreComp
+ */
+xsltElemPreCompPtr
+xsltNewElemPreComp(xsltStylesheetPtr style, xmlNodePtr inst,
+ xsltTransformFunction function)
+{
+ xsltElemPreCompPtr cur;
+
+ cur = (xsltElemPreCompPtr) xmlMalloc(sizeof(xsltElemPreComp));
+ if (cur == NULL) {
+ xsltTransformError(NULL, style, NULL,
+ "xsltNewExtElement : malloc failed\n");
+ return (NULL);
+ }
+ memset(cur, 0, sizeof(xsltElemPreComp));
+
+ xsltInitElemPreComp(cur, style, inst, function,
+ (xsltElemPreCompDeallocator) xmlFree);
+
+ return (cur);
+}
+
+/**
+ * xsltInitElemPreComp:
+ * @comp: an #xsltElemPreComp (or generally a derived structure)
+ * @style: the XSLT stylesheet
+ * @inst: the element node
+ * @function: the transform function
+ * @freeFunc: the @comp deallocator
+ *
+ * Initializes an existing #xsltElemPreComp structure. This is usefull
+ * when extending an #xsltElemPreComp to store precomputed data.
+ * This function MUST be called on any extension element precomputed
+ * data struct.
+ */
+void
+xsltInitElemPreComp(xsltElemPreCompPtr comp, xsltStylesheetPtr style,
+ xmlNodePtr inst, xsltTransformFunction function,
+ xsltElemPreCompDeallocator freeFunc)
+{
+ comp->type = XSLT_FUNC_EXTENSION;
+ comp->func = function;
+ comp->inst = inst;
+ comp->free = freeFunc;
+
+ comp->next = style->preComps;
+ style->preComps = comp;
+}
+
+/**
+ * xsltPreComputeExtModuleElement:
+ * @style: the stylesheet
+ * @inst: the element node
+ *
+ * Precomputes an extension module element
+ *
+ * Returns the precomputed data
+ */
+xsltElemPreCompPtr
+xsltPreComputeExtModuleElement(xsltStylesheetPtr style, xmlNodePtr inst)
+{
+ xsltExtElementPtr ext;
+ xsltElemPreCompPtr comp = NULL;
+
+ if ((style == NULL) || (inst == NULL) ||
+ (inst->type != XML_ELEMENT_NODE) || (inst->ns == NULL))
+ return (NULL);
+
+ xmlMutexLock(xsltExtMutex);
+
+ ext = (xsltExtElementPtr)
+ xmlHashLookup2(xsltElementsHash, inst->name, inst->ns->href);
+
+ xmlMutexUnlock(xsltExtMutex);
+
+ /*
+ * EXT TODO: Now what?
+ */
+ if (ext == NULL)
+ return (NULL);
+
+ if (ext->precomp != NULL) {
+ /*
+ * REVISIT TODO: Check if the text below is correct.
+ * This will return a xsltElemPreComp structure or NULL.
+ * 1) If the the author of the extension needs a
+ * custom structure to hold the specific values of
+ * this extension, he will derive a structure based on
+ * xsltElemPreComp; thus we obviously *cannot* refactor
+ * the xsltElemPreComp structure, since all already derived
+ * user-defined strucures will break.
+ * Example: For the extension xsl:document,
+ * in xsltDocumentComp() (preproc.c), the structure
+ * xsltStyleItemDocument is allocated, filled with
+ * specific values and returned.
+ * 2) If the author needs no values to be stored in
+ * this structure, then he'll return NULL;
+ */
+ comp = ext->precomp(style, inst, ext->transform);
+ }
+ if (comp == NULL) {
+ /*
+ * Default creation of a xsltElemPreComp structure, if
+ * the author of this extension did not create a custom
+ * structure.
+ */
+ comp = xsltNewElemPreComp(style, inst, ext->transform);
+ }
+
+ return (comp);
+}
+
+/**
+ * xsltRegisterExtModuleElement:
+ * @name: the element name
+ * @URI: the element namespace URI
+ * @precomp: the pre-computation callback
+ * @transform: the transformation callback
+ *
+ * Registers an extension module element.
+ *
+ * Returns 0 if successful, -1 in case of error.
+ */
+int
+xsltRegisterExtModuleElement(const xmlChar * name, const xmlChar * URI,
+ xsltPreComputeFunction precomp,
+ xsltTransformFunction transform)
+{
+ int ret = 0;
+
+ xsltExtElementPtr ext;
+
+ if ((name == NULL) || (URI == NULL) || (transform == NULL))
+ return (-1);
+
+ if (xsltElementsHash == NULL)
+ xsltElementsHash = xmlHashCreate(10);
+ if (xsltElementsHash == NULL)
+ return (-1);
+
+ xmlMutexLock(xsltExtMutex);
+
+ ext = xsltNewExtElement(precomp, transform);
+ if (ext == NULL) {
+ ret = -1;
+ goto done;
+ }
+
+ xmlHashUpdateEntry2(xsltElementsHash, name, URI, (void *) ext,
+ (xmlHashDeallocator) xsltFreeExtElement);
+
+done:
+ xmlMutexUnlock(xsltExtMutex);
+
+ return (ret);
+}
+
+/**
+ * xsltExtElementLookup:
+ * @ctxt: an XSLT process context
+ * @name: the element name
+ * @URI: the element namespace URI
+ *
+ * Looks up an extension element. @ctxt can be NULL to search only in
+ * module elements.
+ *
+ * Returns the element callback or NULL if not found
+ */
+xsltTransformFunction
+xsltExtElementLookup(xsltTransformContextPtr ctxt,
+ const xmlChar * name, const xmlChar * URI)
+{
+ xsltTransformFunction ret;
+
+ if ((name == NULL) || (URI == NULL))
+ return (NULL);
+
+ if ((ctxt != NULL) && (ctxt->extElements != NULL)) {
+ XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->extElements, name, URI);
+ if (ret != NULL) {
+ return(ret);
+ }
+ }
+
+ ret = xsltExtModuleElementLookup(name, URI);
+
+ return (ret);
+}
+
+/**
+ * xsltExtModuleElementLookup:
+ * @name: the element name
+ * @URI: the element namespace URI
+ *
+ * Looks up an extension module element
+ *
+ * Returns the callback function if found, NULL otherwise.
+ */
+xsltTransformFunction
+xsltExtModuleElementLookup(const xmlChar * name, const xmlChar * URI)
+{
+ xsltExtElementPtr ext;
+
+ if ((xsltElementsHash == NULL) || (name == NULL) || (URI == NULL))
+ return (NULL);
+
+ xmlMutexLock(xsltExtMutex);
+
+ ext = (xsltExtElementPtr) xmlHashLookup2(xsltElementsHash, name, URI);
+
+ xmlMutexUnlock(xsltExtMutex);
+
+ /*
+ * if function lookup fails, attempt a dynamic load on
+ * supported platforms
+ */
+ if (NULL == ext) {
+ if (!xsltExtModuleRegisterDynamic(URI)) {
+ xmlMutexLock(xsltExtMutex);
+
+ ext = (xsltExtElementPtr)
+ xmlHashLookup2(xsltElementsHash, name, URI);
+
+ xmlMutexUnlock(xsltExtMutex);
+ }
+ }
+
+ if (ext == NULL)
+ return (NULL);
+ return (ext->transform);
+}
+
+/**
+ * xsltExtModuleElementPreComputeLookup:
+ * @name: the element name
+ * @URI: the element namespace URI
+ *
+ * Looks up an extension module element pre-computation function
+ *
+ * Returns the callback function if found, NULL otherwise.
+ */
+xsltPreComputeFunction
+xsltExtModuleElementPreComputeLookup(const xmlChar * name,
+ const xmlChar * URI)
+{
+ xsltExtElementPtr ext;
+
+ if ((xsltElementsHash == NULL) || (name == NULL) || (URI == NULL))
+ return (NULL);
+
+ xmlMutexLock(xsltExtMutex);
+
+ ext = (xsltExtElementPtr) xmlHashLookup2(xsltElementsHash, name, URI);
+
+ xmlMutexUnlock(xsltExtMutex);
+
+ if (ext == NULL) {
+ if (!xsltExtModuleRegisterDynamic(URI)) {
+ xmlMutexLock(xsltExtMutex);
+
+ ext = (xsltExtElementPtr)
+ xmlHashLookup2(xsltElementsHash, name, URI);
+
+ xmlMutexUnlock(xsltExtMutex);
+ }
+ }
+
+ if (ext == NULL)
+ return (NULL);
+ return (ext->precomp);
+}
+
+/**
+ * xsltUnregisterExtModuleElement:
+ * @name: the element name
+ * @URI: the element namespace URI
+ *
+ * Unregisters an extension module element
+ *
+ * Returns 0 if successful, -1 in case of error.
+ */
+int
+xsltUnregisterExtModuleElement(const xmlChar * name, const xmlChar * URI)
+{
+ int ret;
+
+ if ((xsltElementsHash == NULL) || (name == NULL) || (URI == NULL))
+ return (-1);
+
+ xmlMutexLock(xsltExtMutex);
+
+ ret = xmlHashRemoveEntry2(xsltElementsHash, name, URI,
+ (xmlHashDeallocator) xsltFreeExtElement);
+
+ xmlMutexUnlock(xsltExtMutex);
+
+ return(ret);
+}
+
+/**
+ * xsltUnregisterAllExtModuleElement:
+ *
+ * Unregisters all extension module element
+ */
+static void
+xsltUnregisterAllExtModuleElement(void)
+{
+ xmlMutexLock(xsltExtMutex);
+
+ xmlHashFree(xsltElementsHash, (xmlHashDeallocator) xsltFreeExtElement);
+ xsltElementsHash = NULL;
+
+ xmlMutexUnlock(xsltExtMutex);
+}
+
+/**
+ * xsltRegisterExtModuleTopLevel:
+ * @name: the top-level element name
+ * @URI: the top-level element namespace URI
+ * @function: the top-level element callback
+ *
+ * Registers an extension module top-level element.
+ *
+ * Returns 0 if successful, -1 in case of error.
+ */
+int
+xsltRegisterExtModuleTopLevel(const xmlChar * name, const xmlChar * URI,
+ xsltTopLevelFunction function)
+{
+ if ((name == NULL) || (URI == NULL) || (function == NULL))
+ return (-1);
+
+ if (xsltTopLevelsHash == NULL)
+ xsltTopLevelsHash = xmlHashCreate(10);
+ if (xsltTopLevelsHash == NULL)
+ return (-1);
+
+ xmlMutexLock(xsltExtMutex);
+
+ xmlHashUpdateEntry2(xsltTopLevelsHash, name, URI,
+ XML_CAST_FPTR(function), NULL);
+
+ xmlMutexUnlock(xsltExtMutex);
+
+ return (0);
+}
+
+/**
+ * xsltExtModuleTopLevelLookup:
+ * @name: the top-level element name
+ * @URI: the top-level element namespace URI
+ *
+ * Looks up an extension module top-level element
+ *
+ * Returns the callback function if found, NULL otherwise.
+ */
+xsltTopLevelFunction
+xsltExtModuleTopLevelLookup(const xmlChar * name, const xmlChar * URI)
+{
+ xsltTopLevelFunction ret;
+
+ if ((xsltTopLevelsHash == NULL) || (name == NULL) || (URI == NULL))
+ return (NULL);
+
+ xmlMutexLock(xsltExtMutex);
+
+ XML_CAST_FPTR(ret) = xmlHashLookup2(xsltTopLevelsHash, name, URI);
+
+ xmlMutexUnlock(xsltExtMutex);
+
+ /* if lookup fails, attempt a dynamic load on supported platforms */
+ if (NULL == ret) {
+ if (!xsltExtModuleRegisterDynamic(URI)) {
+ xmlMutexLock(xsltExtMutex);
+
+ XML_CAST_FPTR(ret) = xmlHashLookup2(xsltTopLevelsHash, name, URI);
+
+ xmlMutexUnlock(xsltExtMutex);
+ }
+ }
+
+ return (ret);
+}
+
+/**
+ * xsltUnregisterExtModuleTopLevel:
+ * @name: the top-level element name
+ * @URI: the top-level element namespace URI
+ *
+ * Unregisters an extension module top-level element
+ *
+ * Returns 0 if successful, -1 in case of error.
+ */
+int
+xsltUnregisterExtModuleTopLevel(const xmlChar * name, const xmlChar * URI)
+{
+ int ret;
+
+ if ((xsltTopLevelsHash == NULL) || (name == NULL) || (URI == NULL))
+ return (-1);
+
+ xmlMutexLock(xsltExtMutex);
+
+ ret = xmlHashRemoveEntry2(xsltTopLevelsHash, name, URI, NULL);
+
+ xmlMutexUnlock(xsltExtMutex);
+
+ return(ret);
+}
+
+/**
+ * xsltUnregisterAllExtModuleTopLevel:
+ *
+ * Unregisters all extension module function
+ */
+static void
+xsltUnregisterAllExtModuleTopLevel(void)
+{
+ xmlMutexLock(xsltExtMutex);
+
+ xmlHashFree(xsltTopLevelsHash, NULL);
+ xsltTopLevelsHash = NULL;
+
+ xmlMutexUnlock(xsltExtMutex);
+}
+
+/**
+ * xsltGetExtInfo:
+ * @style: pointer to a stylesheet
+ * @URI: the namespace URI desired
+ *
+ * looks up URI in extInfos of the stylesheet
+ *
+ * returns a pointer to the hash table if found, else NULL
+ */
+xmlHashTablePtr
+xsltGetExtInfo(xsltStylesheetPtr style, const xmlChar * URI)
+{
+ xsltExtDataPtr data;
+
+ /*
+ * TODO: Why do we have a return type of xmlHashTablePtr?
+ * Is the user-allocated data for extension modules expected
+ * to be a xmlHashTablePtr only? Or is this intended for
+ * the EXSLT module only?
+ */
+
+ if (style != NULL && style->extInfos != NULL) {
+ data = xmlHashLookup(style->extInfos, URI);
+ if (data != NULL && data->extData != NULL)
+ return data->extData;
+ }
+ return NULL;
+}
+
+/************************************************************************
+ * *
+ * Test module http://xmlsoft.org/XSLT/ *
+ * *
+ ************************************************************************/
+
+/************************************************************************
+ * *
+ * Test of the extension module API *
+ * *
+ ************************************************************************/
+
+static xmlChar *testData = NULL;
+static xmlChar *testStyleData = NULL;
+
+/**
+ * xsltExtFunctionTest:
+ * @ctxt: the XPath Parser context
+ * @nargs: the number of arguments
+ *
+ * function libxslt:test() for testing the extensions support.
+ */
+static void
+xsltExtFunctionTest(xmlXPathParserContextPtr ctxt,
+ int nargs ATTRIBUTE_UNUSED)
+{
+ xsltTransformContextPtr tctxt;
+ void *data = NULL;
+
+ tctxt = xsltXPathGetTransformContext(ctxt);
+
+ if (testData == NULL) {
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltExtFunctionTest: not initialized,"
+ " calling xsltGetExtData\n");
+ data = xsltGetExtData(tctxt, (const xmlChar *) XSLT_DEFAULT_URL);
+ if (data == NULL) {
+ xsltTransformError(tctxt, NULL, NULL,
+ "xsltExtElementTest: not initialized\n");
+ return;
+ }
+ }
+ if (tctxt == NULL) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "xsltExtFunctionTest: failed to get the transformation context\n");
+ return;
+ }
+ if (data == NULL)
+ data = xsltGetExtData(tctxt, (const xmlChar *) XSLT_DEFAULT_URL);
+ if (data == NULL) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "xsltExtFunctionTest: failed to get module data\n");
+ return;
+ }
+ if (data != testData) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "xsltExtFunctionTest: got wrong module data\n");
+ return;
+ }
+#ifdef WITH_XSLT_DEBUG_FUNCTION
+ xsltGenericDebug(xsltGenericDebugContext,
+ "libxslt:test() called with %d args\n", nargs);
+#endif
+}
+
+/**
+ * xsltExtElementPreCompTest:
+ * @style: the stylesheet
+ * @inst: the instruction in the stylesheet
+ *
+ * Process a libxslt:test node
+ */
+static xsltElemPreCompPtr
+xsltExtElementPreCompTest(xsltStylesheetPtr style, xmlNodePtr inst,
+ xsltTransformFunction function)
+{
+ xsltElemPreCompPtr ret;
+
+ if (style == NULL) {
+ xsltTransformError(NULL, NULL, inst,
+ "xsltExtElementTest: no transformation context\n");
+ return (NULL);
+ }
+ if (testStyleData == NULL) {
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltExtElementPreCompTest: not initialized,"
+ " calling xsltStyleGetExtData\n");
+ xsltStyleGetExtData(style, (const xmlChar *) XSLT_DEFAULT_URL);
+ if (testStyleData == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsltExtElementPreCompTest: not initialized\n");
+ if (style != NULL)
+ style->errors++;
+ return (NULL);
+ }
+ }
+ if (inst == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsltExtElementPreCompTest: no instruction\n");
+ if (style != NULL)
+ style->errors++;
+ return (NULL);
+ }
+ ret = xsltNewElemPreComp(style, inst, function);
+ return (ret);
+}
+
+/**
+ * xsltExtElementTest:
+ * @ctxt: an XSLT processing context
+ * @node: The current node
+ * @inst: the instruction in the stylesheet
+ * @comp: precomputed informations
+ *
+ * Process a libxslt:test node
+ */
+static void
+xsltExtElementTest(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltElemPreCompPtr comp ATTRIBUTE_UNUSED)
+{
+ xmlNodePtr commentNode;
+
+ if (testData == NULL) {
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltExtElementTest: not initialized,"
+ " calling xsltGetExtData\n");
+ xsltGetExtData(ctxt, (const xmlChar *) XSLT_DEFAULT_URL);
+ if (testData == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsltExtElementTest: not initialized\n");
+ return;
+ }
+ }
+ if (ctxt == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsltExtElementTest: no transformation context\n");
+ return;
+ }
+ if (node == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsltExtElementTest: no current node\n");
+ return;
+ }
+ if (inst == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsltExtElementTest: no instruction\n");
+ return;
+ }
+ if (ctxt->insert == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsltExtElementTest: no insertion point\n");
+ return;
+ }
+ commentNode = xmlNewComment((const xmlChar *)
+ "libxslt:test element test worked");
+ xmlAddChild(ctxt->insert, commentNode);
+}
+
+/**
+ * xsltExtInitTest:
+ * @ctxt: an XSLT transformation context
+ * @URI: the namespace URI for the extension
+ *
+ * A function called at initialization time of an XSLT extension module
+ *
+ * Returns a pointer to the module specific data for this transformation
+ */
+static void *
+xsltExtInitTest(xsltTransformContextPtr ctxt, const xmlChar * URI)
+{
+ if (testStyleData == NULL) {
+ xsltGenericDebug(xsltGenericErrorContext,
+ "xsltExtInitTest: not initialized,"
+ " calling xsltStyleGetExtData\n");
+ testStyleData = xsltStyleGetExtData(ctxt->style, URI);
+ if (testStyleData == NULL) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "xsltExtInitTest: not initialized\n");
+ return (NULL);
+ }
+ }
+ if (testData != NULL) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "xsltExtInitTest: already initialized\n");
+ return (NULL);
+ }
+ testData = (void *) "test data";
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Registered test module : %s\n", URI);
+ return (testData);
+}
+
+
+/**
+ * xsltExtShutdownTest:
+ * @ctxt: an XSLT transformation context
+ * @URI: the namespace URI for the extension
+ * @data: the data associated to this module
+ *
+ * A function called at shutdown time of an XSLT extension module
+ */
+static void
+xsltExtShutdownTest(xsltTransformContextPtr ctxt,
+ const xmlChar * URI, void *data)
+{
+ if (testData == NULL) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "xsltExtShutdownTest: not initialized\n");
+ return;
+ }
+ if (data != testData) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "xsltExtShutdownTest: wrong data\n");
+ }
+ testData = NULL;
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Unregistered test module : %s\n", URI);
+}
+
+/**
+ * xsltExtStyleInitTest:
+ * @style: an XSLT stylesheet
+ * @URI: the namespace URI for the extension
+ *
+ * A function called at initialization time of an XSLT extension module
+ *
+ * Returns a pointer to the module specific data for this transformation
+ */
+static void *
+xsltExtStyleInitTest(xsltStylesheetPtr style ATTRIBUTE_UNUSED,
+ const xmlChar * URI)
+{
+ if (testStyleData != NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltExtInitTest: already initialized\n");
+ return (NULL);
+ }
+ testStyleData = (void *) "test data";
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Registered test module : %s\n", URI);
+ return (testStyleData);
+}
+
+
+/**
+ * xsltExtStyleShutdownTest:
+ * @style: an XSLT stylesheet
+ * @URI: the namespace URI for the extension
+ * @data: the data associated to this module
+ *
+ * A function called at shutdown time of an XSLT extension module
+ */
+static void
+xsltExtStyleShutdownTest(xsltStylesheetPtr style ATTRIBUTE_UNUSED,
+ const xmlChar * URI, void *data)
+{
+ if (testStyleData == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltExtShutdownTest: not initialized\n");
+ return;
+ }
+ if (data != testStyleData) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltExtShutdownTest: wrong data\n");
+ }
+ testStyleData = NULL;
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Unregistered test module : %s\n", URI);
+}
+
+/**
+ * xsltRegisterTestModule:
+ *
+ * Registers the test module
+ */
+void
+xsltRegisterTestModule(void)
+{
+ xsltInitGlobals();
+ xsltRegisterExtModuleFull((const xmlChar *) XSLT_DEFAULT_URL,
+ xsltExtInitTest, xsltExtShutdownTest,
+ xsltExtStyleInitTest,
+ xsltExtStyleShutdownTest);
+ xsltRegisterExtModuleFunction((const xmlChar *) "test",
+ (const xmlChar *) XSLT_DEFAULT_URL,
+ xsltExtFunctionTest);
+ xsltRegisterExtModuleElement((const xmlChar *) "test",
+ (const xmlChar *) XSLT_DEFAULT_URL,
+ xsltExtElementPreCompTest,
+ xsltExtElementTest);
+}
+
+static void
+xsltHashScannerModuleFree(void *payload ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED,
+ xmlChar * name ATTRIBUTE_UNUSED)
+{
+#ifdef WITH_MODULES
+ xmlModuleClose(payload);
+#endif
+}
+
+/**
+ * xsltInitGlobals:
+ *
+ * Initialize the global variables for extensions
+ */
+void
+xsltInitGlobals(void)
+{
+ if (xsltExtMutex == NULL) {
+ xsltExtMutex = xmlNewMutex();
+ }
+}
+
+/**
+ * xsltCleanupGlobals:
+ *
+ * Unregister all global variables set up by the XSLT library
+ */
+void
+xsltCleanupGlobals(void)
+{
+ xsltUnregisterAllExtModules();
+ xsltUnregisterAllExtModuleFunction();
+ xsltUnregisterAllExtModuleElement();
+ xsltUnregisterAllExtModuleTopLevel();
+
+ xmlMutexLock(xsltExtMutex);
+ /* cleanup dynamic module hash */
+ if (NULL != xsltModuleHash) {
+ xmlHashScan(xsltModuleHash, xsltHashScannerModuleFree, 0);
+ xmlHashFree(xsltModuleHash, NULL);
+ xsltModuleHash = NULL;
+ }
+ xmlMutexUnlock(xsltExtMutex);
+
+ xmlFreeMutex(xsltExtMutex);
+ xsltExtMutex = NULL;
+ xsltFreeLocales();
+ xsltUninit();
+}
+
+static void
+xsltDebugDumpExtensionsCallback(void *function ATTRIBUTE_UNUSED,
+ FILE * output, const xmlChar * name,
+ const xmlChar * URI,
+ const xmlChar * not_used ATTRIBUTE_UNUSED)
+{
+ if (!name || !URI)
+ return;
+ fprintf(output, "{%s}%s\n", URI, name);
+}
+
+static void
+xsltDebugDumpExtModulesCallback(void *function ATTRIBUTE_UNUSED,
+ FILE * output, const xmlChar * URI,
+ const xmlChar * not_used ATTRIBUTE_UNUSED,
+ const xmlChar * not_used2 ATTRIBUTE_UNUSED)
+{
+ if (!URI)
+ return;
+ fprintf(output, "%s\n", URI);
+}
+
+/**
+ * xsltDebugDumpExtensions:
+ * @output: the FILE * for the output, if NULL stdout is used
+ *
+ * Dumps a list of the registered XSLT extension functions and elements
+ */
+void
+xsltDebugDumpExtensions(FILE * output)
+{
+ if (output == NULL)
+ output = stdout;
+ fprintf(output,
+ "Registered XSLT Extensions\n--------------------------\n");
+ if (!xsltFunctionsHash)
+ fprintf(output, "No registered extension functions\n");
+ else {
+ fprintf(output, "Registered Extension Functions:\n");
+ xmlMutexLock(xsltExtMutex);
+ xmlHashScanFull(xsltFunctionsHash,
+ (xmlHashScannerFull)
+ xsltDebugDumpExtensionsCallback, output);
+ xmlMutexUnlock(xsltExtMutex);
+ }
+ if (!xsltElementsHash)
+ fprintf(output, "\nNo registered extension elements\n");
+ else {
+ fprintf(output, "\nRegistered Extension Elements:\n");
+ xmlMutexLock(xsltExtMutex);
+ xmlHashScanFull(xsltElementsHash,
+ (xmlHashScannerFull)
+ xsltDebugDumpExtensionsCallback, output);
+ xmlMutexUnlock(xsltExtMutex);
+ }
+ if (!xsltExtensionsHash)
+ fprintf(output, "\nNo registered extension modules\n");
+ else {
+ fprintf(output, "\nRegistered Extension Modules:\n");
+ xmlMutexLock(xsltExtMutex);
+ xmlHashScanFull(xsltExtensionsHash,
+ (xmlHashScannerFull)
+ xsltDebugDumpExtModulesCallback, output);
+ xmlMutexUnlock(xsltExtMutex);
+ }
+
+}
diff --git a/libxslt/extensions.h b/libxslt/extensions.h
new file mode 100644
index 0000000..900779c
--- /dev/null
+++ b/libxslt/extensions.h
@@ -0,0 +1,262 @@
+/*
+ * Summary: interface for the extension support
+ * Description: This provide the API needed for simple and module
+ * extension support.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_EXTENSION_H__
+#define __XML_XSLT_EXTENSION_H__
+
+#include <libxml/xpath.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Extension Modules API.
+ */
+
+/**
+ * xsltInitGlobals:
+ *
+ * Initialize the global variables for extensions
+ *
+ */
+
+XSLTPUBFUN void XSLTCALL
+ xsltInitGlobals (void);
+
+/**
+ * xsltStyleExtInitFunction:
+ * @ctxt: an XSLT stylesheet
+ * @URI: the namespace URI for the extension
+ *
+ * A function called at initialization time of an XSLT extension module.
+ *
+ * Returns a pointer to the module specific data for this transformation.
+ */
+typedef void * (*xsltStyleExtInitFunction) (xsltStylesheetPtr style,
+ const xmlChar *URI);
+
+/**
+ * xsltStyleExtShutdownFunction:
+ * @ctxt: an XSLT stylesheet
+ * @URI: the namespace URI for the extension
+ * @data: the data associated to this module
+ *
+ * A function called at shutdown time of an XSLT extension module.
+ */
+typedef void (*xsltStyleExtShutdownFunction) (xsltStylesheetPtr style,
+ const xmlChar *URI,
+ void *data);
+
+/**
+ * xsltExtInitFunction:
+ * @ctxt: an XSLT transformation context
+ * @URI: the namespace URI for the extension
+ *
+ * A function called at initialization time of an XSLT extension module.
+ *
+ * Returns a pointer to the module specific data for this transformation.
+ */
+typedef void * (*xsltExtInitFunction) (xsltTransformContextPtr ctxt,
+ const xmlChar *URI);
+
+/**
+ * xsltExtShutdownFunction:
+ * @ctxt: an XSLT transformation context
+ * @URI: the namespace URI for the extension
+ * @data: the data associated to this module
+ *
+ * A function called at shutdown time of an XSLT extension module.
+ */
+typedef void (*xsltExtShutdownFunction) (xsltTransformContextPtr ctxt,
+ const xmlChar *URI,
+ void *data);
+
+XSLTPUBFUN int XSLTCALL
+ xsltRegisterExtModule (const xmlChar *URI,
+ xsltExtInitFunction initFunc,
+ xsltExtShutdownFunction shutdownFunc);
+XSLTPUBFUN int XSLTCALL
+ xsltRegisterExtModuleFull
+ (const xmlChar * URI,
+ xsltExtInitFunction initFunc,
+ xsltExtShutdownFunction shutdownFunc,
+ xsltStyleExtInitFunction styleInitFunc,
+ xsltStyleExtShutdownFunction styleShutdownFunc);
+
+XSLTPUBFUN int XSLTCALL
+ xsltUnregisterExtModule (const xmlChar * URI);
+
+XSLTPUBFUN void * XSLTCALL
+ xsltGetExtData (xsltTransformContextPtr ctxt,
+ const xmlChar *URI);
+
+XSLTPUBFUN void * XSLTCALL
+ xsltStyleGetExtData (xsltStylesheetPtr style,
+ const xmlChar *URI);
+#ifdef XSLT_REFACTORED
+XSLTPUBFUN void * XSLTCALL
+ xsltStyleStylesheetLevelGetExtData(
+ xsltStylesheetPtr style,
+ const xmlChar * URI);
+#endif
+XSLTPUBFUN void XSLTCALL
+ xsltShutdownCtxtExts (xsltTransformContextPtr ctxt);
+
+XSLTPUBFUN void XSLTCALL
+ xsltShutdownExts (xsltStylesheetPtr style);
+
+XSLTPUBFUN xsltTransformContextPtr XSLTCALL
+ xsltXPathGetTransformContext
+ (xmlXPathParserContextPtr ctxt);
+
+/*
+ * extension functions
+*/
+XSLTPUBFUN int XSLTCALL
+ xsltRegisterExtModuleFunction
+ (const xmlChar *name,
+ const xmlChar *URI,
+ xmlXPathFunction function);
+XSLTPUBFUN xmlXPathFunction XSLTCALL
+ xsltExtModuleFunctionLookup (const xmlChar *name,
+ const xmlChar *URI);
+XSLTPUBFUN int XSLTCALL
+ xsltUnregisterExtModuleFunction
+ (const xmlChar *name,
+ const xmlChar *URI);
+
+/*
+ * extension elements
+ */
+typedef xsltElemPreCompPtr (*xsltPreComputeFunction)
+ (xsltStylesheetPtr style,
+ xmlNodePtr inst,
+ xsltTransformFunction function);
+
+XSLTPUBFUN xsltElemPreCompPtr XSLTCALL
+ xsltNewElemPreComp (xsltStylesheetPtr style,
+ xmlNodePtr inst,
+ xsltTransformFunction function);
+XSLTPUBFUN void XSLTCALL
+ xsltInitElemPreComp (xsltElemPreCompPtr comp,
+ xsltStylesheetPtr style,
+ xmlNodePtr inst,
+ xsltTransformFunction function,
+ xsltElemPreCompDeallocator freeFunc);
+
+XSLTPUBFUN int XSLTCALL
+ xsltRegisterExtModuleElement
+ (const xmlChar *name,
+ const xmlChar *URI,
+ xsltPreComputeFunction precomp,
+ xsltTransformFunction transform);
+XSLTPUBFUN xsltTransformFunction XSLTCALL
+ xsltExtElementLookup (xsltTransformContextPtr ctxt,
+ const xmlChar *name,
+ const xmlChar *URI);
+XSLTPUBFUN xsltTransformFunction XSLTCALL
+ xsltExtModuleElementLookup
+ (const xmlChar *name,
+ const xmlChar *URI);
+XSLTPUBFUN xsltPreComputeFunction XSLTCALL
+ xsltExtModuleElementPreComputeLookup
+ (const xmlChar *name,
+ const xmlChar *URI);
+XSLTPUBFUN int XSLTCALL
+ xsltUnregisterExtModuleElement
+ (const xmlChar *name,
+ const xmlChar *URI);
+
+/*
+ * top-level elements
+ */
+typedef void (*xsltTopLevelFunction) (xsltStylesheetPtr style,
+ xmlNodePtr inst);
+
+XSLTPUBFUN int XSLTCALL
+ xsltRegisterExtModuleTopLevel
+ (const xmlChar *name,
+ const xmlChar *URI,
+ xsltTopLevelFunction function);
+XSLTPUBFUN xsltTopLevelFunction XSLTCALL
+ xsltExtModuleTopLevelLookup
+ (const xmlChar *name,
+ const xmlChar *URI);
+XSLTPUBFUN int XSLTCALL
+ xsltUnregisterExtModuleTopLevel
+ (const xmlChar *name,
+ const xmlChar *URI);
+
+
+/* These 2 functions are deprecated for use within modules. */
+XSLTPUBFUN int XSLTCALL
+ xsltRegisterExtFunction (xsltTransformContextPtr ctxt,
+ const xmlChar *name,
+ const xmlChar *URI,
+ xmlXPathFunction function);
+XSLTPUBFUN int XSLTCALL
+ xsltRegisterExtElement (xsltTransformContextPtr ctxt,
+ const xmlChar *name,
+ const xmlChar *URI,
+ xsltTransformFunction function);
+
+/*
+ * Extension Prefix handling API.
+ * Those are used by the XSLT (pre)processor.
+ */
+
+XSLTPUBFUN int XSLTCALL
+ xsltRegisterExtPrefix (xsltStylesheetPtr style,
+ const xmlChar *prefix,
+ const xmlChar *URI);
+XSLTPUBFUN int XSLTCALL
+ xsltCheckExtPrefix (xsltStylesheetPtr style,
+ const xmlChar *URI);
+XSLTPUBFUN int XSLTCALL
+ xsltCheckExtURI (xsltStylesheetPtr style,
+ const xmlChar *URI);
+XSLTPUBFUN int XSLTCALL
+ xsltInitCtxtExts (xsltTransformContextPtr ctxt);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeCtxtExts (xsltTransformContextPtr ctxt);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeExts (xsltStylesheetPtr style);
+
+XSLTPUBFUN xsltElemPreCompPtr XSLTCALL
+ xsltPreComputeExtModuleElement
+ (xsltStylesheetPtr style,
+ xmlNodePtr inst);
+/*
+ * Extension Infos access.
+ * Used by exslt initialisation
+ */
+
+XSLTPUBFUN xmlHashTablePtr XSLTCALL
+ xsltGetExtInfo (xsltStylesheetPtr style,
+ const xmlChar *URI);
+
+/**
+ * Test module http://xmlsoft.org/XSLT/
+ */
+XSLTPUBFUN void XSLTCALL
+ xsltRegisterTestModule (void);
+XSLTPUBFUN void XSLTCALL
+ xsltDebugDumpExtensions (FILE * output);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_EXTENSION_H__ */
+
diff --git a/libxslt/extra.c b/libxslt/extra.c
new file mode 100644
index 0000000..17df4ba
--- /dev/null
+++ b/libxslt/extra.c
@@ -0,0 +1,332 @@
+/*
+ * extra.c: Implementation of non-standard features
+ *
+ * Reference:
+ * Michael Kay "XSLT Programmer's Reference" pp 637-643
+ * The node-set() extension function
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/hash.h>
+#include <libxml/xmlerror.h>
+#include <libxml/parserInternals.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "xsltutils.h"
+#include "extensions.h"
+#include "variables.h"
+#include "transform.h"
+#include "extra.h"
+#include "preproc.h"
+
+#ifdef WITH_XSLT_DEBUG
+#define WITH_XSLT_DEBUG_EXTRA
+#endif
+
+/************************************************************************
+ * *
+ * Handling of XSLT debugging *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltDebug:
+ * @ctxt: an XSLT processing context
+ * @node: The current node
+ * @inst: the instruction in the stylesheet
+ * @comp: precomputed informations
+ *
+ * Process an debug node
+ */
+void
+xsltDebug(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED,
+ xmlNodePtr inst ATTRIBUTE_UNUSED,
+ xsltStylePreCompPtr comp ATTRIBUTE_UNUSED)
+{
+ int i, j;
+
+ xsltGenericError(xsltGenericErrorContext, "Templates:\n");
+ for (i = 0, j = ctxt->templNr - 1; ((i < 15) && (j >= 0)); i++, j--) {
+ xsltGenericError(xsltGenericErrorContext, "#%d ", i);
+ if (ctxt->templTab[j]->name != NULL)
+ xsltGenericError(xsltGenericErrorContext, "name %s ",
+ ctxt->templTab[j]->name);
+ if (ctxt->templTab[j]->match != NULL)
+ xsltGenericError(xsltGenericErrorContext, "name %s ",
+ ctxt->templTab[j]->match);
+ if (ctxt->templTab[j]->mode != NULL)
+ xsltGenericError(xsltGenericErrorContext, "name %s ",
+ ctxt->templTab[j]->mode);
+ xsltGenericError(xsltGenericErrorContext, "\n");
+ }
+ xsltGenericError(xsltGenericErrorContext, "Variables:\n");
+ for (i = 0, j = ctxt->varsNr - 1; ((i < 15) && (j >= 0)); i++, j--) {
+ xsltStackElemPtr cur;
+
+ if (ctxt->varsTab[j] == NULL)
+ continue;
+ xsltGenericError(xsltGenericErrorContext, "#%d\n", i);
+ cur = ctxt->varsTab[j];
+ while (cur != NULL) {
+ if (cur->comp == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "corrupted !!!\n");
+ } else if (cur->comp->type == XSLT_FUNC_PARAM) {
+ xsltGenericError(xsltGenericErrorContext, "param ");
+ } else if (cur->comp->type == XSLT_FUNC_VARIABLE) {
+ xsltGenericError(xsltGenericErrorContext, "var ");
+ }
+ if (cur->name != NULL)
+ xsltGenericError(xsltGenericErrorContext, "%s ",
+ cur->name);
+ else
+ xsltGenericError(xsltGenericErrorContext, "noname !!!!");
+#ifdef LIBXML_DEBUG_ENABLED
+ if (cur->value != NULL) {
+ xmlXPathDebugDumpObject(stdout, cur->value, 1);
+ } else {
+ xsltGenericError(xsltGenericErrorContext, "NULL !!!!");
+ }
+#endif
+ xsltGenericError(xsltGenericErrorContext, "\n");
+ cur = cur->next;
+ }
+
+ }
+}
+
+/************************************************************************
+ * *
+ * Classic extensions as described by M. Kay *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltFunctionNodeSet:
+ * @ctxt: the XPath Parser context
+ * @nargs: the number of arguments
+ *
+ * Implement the node-set() XSLT function
+ * node-set node-set(result-tree)
+ *
+ * This function is available in libxslt, saxon or xt namespace.
+ */
+void
+xsltFunctionNodeSet(xmlXPathParserContextPtr ctxt, int nargs){
+ if (nargs != 1) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "node-set() : expects one result-tree arg\n");
+ ctxt->error = XPATH_INVALID_ARITY;
+ return;
+ }
+ if ((ctxt->value == NULL) ||
+ ((ctxt->value->type != XPATH_XSLT_TREE) &&
+ (ctxt->value->type != XPATH_NODESET))) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "node-set() invalid arg expecting a result tree\n");
+ ctxt->error = XPATH_INVALID_TYPE;
+ return;
+ }
+ if (ctxt->value->type == XPATH_XSLT_TREE) {
+ ctxt->value->type = XPATH_NODESET;
+ }
+}
+
+
+/*
+ * Okay the following really seems unportable and since it's not
+ * part of any standard I'm not too ashamed to do this
+ */
+#if defined(linux) || defined(__sun)
+#if defined(HAVE_MKTIME) && defined(HAVE_LOCALTIME) && defined(HAVE_ASCTIME)
+#define WITH_LOCALTIME
+
+/**
+ * xsltFunctionLocalTime:
+ * @ctxt: the XPath Parser context
+ * @nargs: the number of arguments
+ *
+ * Implement the localTime XSLT function used by NORM
+ * string localTime(???)
+ *
+ * This function is available in Norm's extension namespace
+ * Code (and comments) contributed by Norm
+ */
+static void
+xsltFunctionLocalTime(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr obj;
+ char *str;
+ char digits[5];
+ char result[29];
+ long int field;
+ time_t gmt, lmt;
+ struct tm gmt_tm;
+ struct tm *local_tm;
+
+ if (nargs != 1) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "localTime() : invalid number of args %d\n", nargs);
+ ctxt->error = XPATH_INVALID_ARITY;
+ return;
+ }
+
+ obj = valuePop(ctxt);
+
+ if (obj->type != XPATH_STRING) {
+ obj = xmlXPathConvertString(obj);
+ }
+ if (obj == NULL) {
+ valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
+ return;
+ }
+
+ str = (char *) obj->stringval;
+
+ /* str = "$Date$" */
+ memset(digits, 0, sizeof(digits));
+ strncpy(digits, str+7, 4);
+ field = strtol(digits, NULL, 10);
+ gmt_tm.tm_year = field - 1900;
+
+ memset(digits, 0, sizeof(digits));
+ strncpy(digits, str+12, 2);
+ field = strtol(digits, NULL, 10);
+ gmt_tm.tm_mon = field - 1;
+
+ memset(digits, 0, sizeof(digits));
+ strncpy(digits, str+15, 2);
+ field = strtol(digits, NULL, 10);
+ gmt_tm.tm_mday = field;
+
+ memset(digits, 0, sizeof(digits));
+ strncpy(digits, str+18, 2);
+ field = strtol(digits, NULL, 10);
+ gmt_tm.tm_hour = field;
+
+ memset(digits, 0, sizeof(digits));
+ strncpy(digits, str+21, 2);
+ field = strtol(digits, NULL, 10);
+ gmt_tm.tm_min = field;
+
+ memset(digits, 0, sizeof(digits));
+ strncpy(digits, str+24, 2);
+ field = strtol(digits, NULL, 10);
+ gmt_tm.tm_sec = field;
+
+ /* Now turn gmt_tm into a time. */
+ gmt = mktime(&gmt_tm);
+
+
+ /*
+ * FIXME: it's been too long since I did manual memory management.
+ * (I swore never to do it again.) Does this introduce a memory leak?
+ */
+ local_tm = localtime(&gmt);
+
+ /*
+ * Calling localtime() has the side-effect of setting timezone.
+ * After we know the timezone, we can adjust for it
+ */
+#if !defined(__FreeBSD__)
+ lmt = gmt - timezone;
+#else /* FreeBSD DOESN'T have such side-ffect */
+ lmt = gmt - local_tm->tm_gmtoff;
+#endif
+ /*
+ * FIXME: it's been too long since I did manual memory management.
+ * (I swore never to do it again.) Does this introduce a memory leak?
+ */
+ local_tm = localtime(&lmt);
+
+ /*
+ * Now convert local_tm back into a string. This doesn't introduce
+ * a memory leak, so says asctime(3).
+ */
+
+ str = asctime(local_tm); /* "Tue Jun 26 05:02:16 2001" */
+ /* 0123456789 123456789 123 */
+
+ memset(result, 0, sizeof(result)); /* "Thu, 26 Jun 2001" */
+ /* 0123456789 12345 */
+
+ strncpy(result, str, 20);
+ strcpy(result+20, "???"); /* tzname doesn't work, fake it */
+ strncpy(result+23, str+19, 5);
+
+ /* Ok, now result contains the string I want to send back. */
+ valuePush(ctxt, xmlXPathNewString((xmlChar *)result));
+}
+#endif
+#endif /* linux or sun */
+
+
+/**
+ * xsltRegisterExtras:
+ * @ctxt: a XSLT process context
+ *
+ * Registers the built-in extensions. This function is deprecated, use
+ * xsltRegisterAllExtras instead.
+ */
+void
+xsltRegisterExtras(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED) {
+ xsltRegisterAllExtras();
+}
+
+/**
+ * xsltRegisterAllExtras:
+ *
+ * Registers the built-in extensions
+ */
+void
+xsltRegisterAllExtras (void) {
+ xsltRegisterExtModuleFunction((const xmlChar *) "node-set",
+ XSLT_LIBXSLT_NAMESPACE,
+ xsltFunctionNodeSet);
+ xsltRegisterExtModuleFunction((const xmlChar *) "node-set",
+ XSLT_SAXON_NAMESPACE,
+ xsltFunctionNodeSet);
+ xsltRegisterExtModuleFunction((const xmlChar *) "node-set",
+ XSLT_XT_NAMESPACE,
+ xsltFunctionNodeSet);
+#ifdef WITH_LOCALTIME
+ xsltRegisterExtModuleFunction((const xmlChar *) "localTime",
+ XSLT_NORM_SAXON_NAMESPACE,
+ xsltFunctionLocalTime);
+#endif
+ xsltRegisterExtModuleElement((const xmlChar *) "debug",
+ XSLT_LIBXSLT_NAMESPACE,
+ NULL,
+ (xsltTransformFunction) xsltDebug);
+ xsltRegisterExtModuleElement((const xmlChar *) "output",
+ XSLT_SAXON_NAMESPACE,
+ xsltDocumentComp,
+ (xsltTransformFunction) xsltDocumentElem);
+ xsltRegisterExtModuleElement((const xmlChar *) "write",
+ XSLT_XALAN_NAMESPACE,
+ xsltDocumentComp,
+ (xsltTransformFunction) xsltDocumentElem);
+ xsltRegisterExtModuleElement((const xmlChar *) "document",
+ XSLT_XT_NAMESPACE,
+ xsltDocumentComp,
+ (xsltTransformFunction) xsltDocumentElem);
+ xsltRegisterExtModuleElement((const xmlChar *) "document",
+ XSLT_NAMESPACE,
+ xsltDocumentComp,
+ (xsltTransformFunction) xsltDocumentElem);
+}
diff --git a/libxslt/extra.h b/libxslt/extra.h
new file mode 100644
index 0000000..6929e3c
--- /dev/null
+++ b/libxslt/extra.h
@@ -0,0 +1,80 @@
+/*
+ * Summary: interface for the non-standard features
+ * Description: implement some extension outside the XSLT namespace
+ * but not EXSLT with is in a different library.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_EXTRA_H__
+#define __XML_XSLT_EXTRA_H__
+
+#include <libxml/xpath.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * XSLT_LIBXSLT_NAMESPACE:
+ *
+ * This is the libxslt namespace for specific extensions.
+ */
+#define XSLT_LIBXSLT_NAMESPACE ((xmlChar *) "http://xmlsoft.org/XSLT/namespace")
+
+/**
+ * XSLT_SAXON_NAMESPACE:
+ *
+ * This is Michael Kay's Saxon processor namespace for extensions.
+ */
+#define XSLT_SAXON_NAMESPACE ((xmlChar *) "http://icl.com/saxon")
+
+/**
+ * XSLT_XT_NAMESPACE:
+ *
+ * This is James Clark's XT processor namespace for extensions.
+ */
+#define XSLT_XT_NAMESPACE ((xmlChar *) "http://www.jclark.com/xt")
+
+/**
+ * XSLT_XALAN_NAMESPACE:
+ *
+ * This is the Apache project XALAN processor namespace for extensions.
+ */
+#define XSLT_XALAN_NAMESPACE ((xmlChar *) \
+ "org.apache.xalan.xslt.extensions.Redirect")
+
+/**
+ * XSLT_NORM_SAXON_NAMESPACE:
+ *
+ * This is Norm's namespace for SAXON extensions.
+ */
+#define XSLT_NORM_SAXON_NAMESPACE ((xmlChar *) \
+ "http://nwalsh.com/xslt/ext/com.nwalsh.saxon.CVS")
+
+
+XSLTPUBFUN void XSLTCALL
+ xsltFunctionNodeSet (xmlXPathParserContextPtr ctxt,
+ int nargs);
+XSLTPUBFUN void XSLTCALL
+ xsltDebug (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+
+
+XSLTPUBFUN void XSLTCALL
+ xsltRegisterExtras (xsltTransformContextPtr ctxt);
+XSLTPUBFUN void XSLTCALL
+ xsltRegisterAllExtras (void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_EXTRA_H__ */
+
diff --git a/libxslt/functions.c b/libxslt/functions.c
new file mode 100644
index 0000000..a5e7021
--- /dev/null
+++ b/libxslt/functions.c
@@ -0,0 +1,988 @@
+/*
+ * functions.c: Implementation of the XSLT extra functions
+ *
+ * Reference:
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ * Bjorn Reese <breese@users.sourceforge.net> for number formatting
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/valid.h>
+#include <libxml/hash.h>
+#include <libxml/xmlerror.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include <libxml/parserInternals.h>
+#include <libxml/uri.h>
+#include <libxml/xpointer.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "xsltutils.h"
+#include "functions.h"
+#include "extensions.h"
+#include "numbersInternals.h"
+#include "keys.h"
+#include "documents.h"
+
+#ifdef WITH_XSLT_DEBUG
+#define WITH_XSLT_DEBUG_FUNCTION
+#endif
+
+/*
+ * Some versions of DocBook XSL use the vendor string to detect
+ * supporting chunking, this is a workaround to be considered
+ * in the list of decent XSLT processors <grin/>
+ */
+#define DOCBOOK_XSL_HACK
+
+/**
+ * xsltXPathFunctionLookup:
+ * @ctxt: a void * but the XSLT transformation context actually
+ * @name: the function name
+ * @ns_uri: the function namespace URI
+ *
+ * This is the entry point when a function is needed by the XPath
+ * interpretor.
+ *
+ * Returns the callback function or NULL if not found
+ */
+xmlXPathFunction
+xsltXPathFunctionLookup (xmlXPathContextPtr ctxt,
+ const xmlChar *name, const xmlChar *ns_uri) {
+ xmlXPathFunction ret;
+
+ if ((ctxt == NULL) || (name == NULL) || (ns_uri == NULL))
+ return (NULL);
+
+#ifdef WITH_XSLT_DEBUG_FUNCTION
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Lookup function {%s}%s\n", ns_uri, name);
+#endif
+
+ /* give priority to context-level functions */
+ /*
+ ret = (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri);
+ */
+ XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri);
+
+ if (ret == NULL)
+ ret = xsltExtModuleFunctionLookup(name, ns_uri);
+
+#ifdef WITH_XSLT_DEBUG_FUNCTION
+ if (ret != NULL)
+ xsltGenericDebug(xsltGenericDebugContext,
+ "found function %s\n", name);
+#endif
+ return(ret);
+}
+
+
+/************************************************************************
+ * *
+ * Module interfaces *
+ * *
+ ************************************************************************/
+
+static void
+xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI)
+{
+ xsltTransformContextPtr tctxt;
+ xmlURIPtr uri;
+ xmlChar *fragment;
+ xsltDocumentPtr idoc; /* document info */
+ xmlDocPtr doc;
+ xmlXPathContextPtr xptrctxt = NULL;
+ xmlXPathObjectPtr resObj = NULL;
+
+ tctxt = xsltXPathGetTransformContext(ctxt);
+ if (tctxt == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "document() : internal error tctxt == NULL\n");
+ valuePush(ctxt, xmlXPathNewNodeSet(NULL));
+ return;
+ }
+
+ uri = xmlParseURI((const char *) URI);
+ if (uri == NULL) {
+ xsltTransformError(tctxt, NULL, NULL,
+ "document() : failed to parse URI\n");
+ valuePush(ctxt, xmlXPathNewNodeSet(NULL));
+ return;
+ }
+
+ /*
+ * check for and remove fragment identifier
+ */
+ fragment = (xmlChar *)uri->fragment;
+ if (fragment != NULL) {
+ xmlChar *newURI;
+ uri->fragment = NULL;
+ newURI = xmlSaveUri(uri);
+ idoc = xsltLoadDocument(tctxt, newURI);
+ xmlFree(newURI);
+ } else
+ idoc = xsltLoadDocument(tctxt, URI);
+ xmlFreeURI(uri);
+
+ if (idoc == NULL) {
+ if ((URI == NULL) ||
+ (URI[0] == '#') ||
+ ((tctxt->style->doc != NULL) &&
+ (xmlStrEqual(tctxt->style->doc->URL, URI))))
+ {
+ /*
+ * This selects the stylesheet's doc itself.
+ */
+ doc = tctxt->style->doc;
+ } else {
+ valuePush(ctxt, xmlXPathNewNodeSet(NULL));
+
+ if (fragment != NULL)
+ xmlFree(fragment);
+
+ return;
+ }
+ } else
+ doc = idoc->doc;
+
+ if (fragment == NULL) {
+ valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc));
+ return;
+ }
+
+ /* use XPointer of HTML location for fragment ID */
+#ifdef LIBXML_XPTR_ENABLED
+ xptrctxt = xmlXPtrNewContext(doc, NULL, NULL);
+ if (xptrctxt == NULL) {
+ xsltTransformError(tctxt, NULL, NULL,
+ "document() : internal error xptrctxt == NULL\n");
+ goto out_fragment;
+ }
+
+ resObj = xmlXPtrEval(fragment, xptrctxt);
+ xmlXPathFreeContext(xptrctxt);
+#endif
+
+ if (resObj == NULL)
+ goto out_fragment;
+
+ switch (resObj->type) {
+ case XPATH_NODESET:
+ break;
+ case XPATH_UNDEFINED:
+ case XPATH_BOOLEAN:
+ case XPATH_NUMBER:
+ case XPATH_STRING:
+ case XPATH_POINT:
+ case XPATH_USERS:
+ case XPATH_XSLT_TREE:
+ case XPATH_RANGE:
+ case XPATH_LOCATIONSET:
+ xsltTransformError(tctxt, NULL, NULL,
+ "document() : XPointer does not select a node set: #%s\n",
+ fragment);
+ goto out_object;
+ }
+
+ valuePush(ctxt, resObj);
+ xmlFree(fragment);
+ return;
+
+out_object:
+ xmlXPathFreeObject(resObj);
+
+out_fragment:
+ valuePush(ctxt, xmlXPathNewNodeSet(NULL));
+ xmlFree(fragment);
+}
+
+/**
+ * xsltDocumentFunction:
+ * @ctxt: the XPath Parser context
+ * @nargs: the number of arguments
+ *
+ * Implement the document() XSLT function
+ * node-set document(object, node-set?)
+ */
+void
+xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlXPathObjectPtr obj, obj2 = NULL;
+ xmlChar *base = NULL, *URI;
+
+
+ if ((nargs < 1) || (nargs > 2)) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "document() : invalid number of args %d\n",
+ nargs);
+ ctxt->error = XPATH_INVALID_ARITY;
+ return;
+ }
+ if (ctxt->value == NULL) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "document() : invalid arg value\n");
+ ctxt->error = XPATH_INVALID_TYPE;
+ return;
+ }
+
+ if (nargs == 2) {
+ if (ctxt->value->type != XPATH_NODESET) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "document() : invalid arg expecting a nodeset\n");
+ ctxt->error = XPATH_INVALID_TYPE;
+ return;
+ }
+
+ obj2 = valuePop(ctxt);
+ }
+
+ if (ctxt->value->type == XPATH_NODESET) {
+ int i;
+ xmlXPathObjectPtr newobj, ret;
+
+ obj = valuePop(ctxt);
+ ret = xmlXPathNewNodeSet(NULL);
+
+ if ((obj != NULL) && obj->nodesetval) {
+ for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+ valuePush(ctxt,
+ xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
+ xmlXPathStringFunction(ctxt, 1);
+ if (nargs == 2) {
+ valuePush(ctxt, xmlXPathObjectCopy(obj2));
+ } else {
+ valuePush(ctxt,
+ xmlXPathNewNodeSet(obj->nodesetval->
+ nodeTab[i]));
+ }
+ xsltDocumentFunction(ctxt, 2);
+ newobj = valuePop(ctxt);
+ ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
+ newobj->nodesetval);
+ xmlXPathFreeObject(newobj);
+ }
+ }
+
+ if (obj != NULL)
+ xmlXPathFreeObject(obj);
+ if (obj2 != NULL)
+ xmlXPathFreeObject(obj2);
+ valuePush(ctxt, ret);
+ return;
+ }
+ /*
+ * Make sure it's converted to a string
+ */
+ xmlXPathStringFunction(ctxt, 1);
+ if (ctxt->value->type != XPATH_STRING) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "document() : invalid arg expecting a string\n");
+ ctxt->error = XPATH_INVALID_TYPE;
+ if (obj2 != NULL)
+ xmlXPathFreeObject(obj2);
+ return;
+ }
+ obj = valuePop(ctxt);
+ if (obj->stringval == NULL) {
+ valuePush(ctxt, xmlXPathNewNodeSet(NULL));
+ } else {
+ xsltTransformContextPtr tctxt;
+ tctxt = xsltXPathGetTransformContext(ctxt);
+ if ((obj2 != NULL) && (obj2->nodesetval != NULL) &&
+ (obj2->nodesetval->nodeNr > 0) &&
+ IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) {
+ xmlNodePtr target;
+
+ target = obj2->nodesetval->nodeTab[0];
+ if ((target->type == XML_ATTRIBUTE_NODE) ||
+ (target->type == XML_PI_NODE)) {
+ target = ((xmlAttrPtr) target)->parent;
+ }
+ base = xmlNodeGetBase(target->doc, target);
+ } else {
+ if ((tctxt != NULL) && (tctxt->inst != NULL)) {
+ base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst);
+ } else if ((tctxt != NULL) && (tctxt->style != NULL) &&
+ (tctxt->style->doc != NULL)) {
+ base = xmlNodeGetBase(tctxt->style->doc,
+ (xmlNodePtr) tctxt->style->doc);
+ }
+ }
+ URI = xmlBuildURI(obj->stringval, base);
+ if (base != NULL)
+ xmlFree(base);
+ if (URI == NULL) {
+ if ((tctxt != NULL) && (tctxt->style != NULL) &&
+ (tctxt->style->doc != NULL) &&
+ (xmlStrEqual(URI, tctxt->style->doc->URL))) {
+ /* This selects the stylesheet's doc itself. */
+ valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) tctxt->style->doc));
+ } else {
+ valuePush(ctxt, xmlXPathNewNodeSet(NULL));
+ }
+ } else {
+ xsltDocumentFunctionLoadDocument( ctxt, URI );
+ xmlFree(URI);
+ }
+ }
+ xmlXPathFreeObject(obj);
+ if (obj2 != NULL)
+ xmlXPathFreeObject(obj2);
+}
+
+/**
+ * xsltKeyFunction:
+ * @ctxt: the XPath Parser context
+ * @nargs: the number of arguments
+ *
+ * Implement the key() XSLT function
+ * node-set key(string, object)
+ */
+void
+xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
+ xmlXPathObjectPtr obj1, obj2;
+
+ if (nargs != 2) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "key() : expects two arguments\n");
+ ctxt->error = XPATH_INVALID_ARITY;
+ return;
+ }
+
+ /*
+ * Get the key's value.
+ */
+ obj2 = valuePop(ctxt);
+ xmlXPathStringFunction(ctxt, 1);
+ if ((obj2 == NULL) ||
+ (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "key() : invalid arg expecting a string\n");
+ ctxt->error = XPATH_INVALID_TYPE;
+ xmlXPathFreeObject(obj2);
+
+ return;
+ }
+ /*
+ * Get the key's name.
+ */
+ obj1 = valuePop(ctxt);
+
+ if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) {
+ int i;
+ xmlXPathObjectPtr newobj, ret;
+
+ ret = xmlXPathNewNodeSet(NULL);
+
+ if (obj2->nodesetval != NULL) {
+ for (i = 0; i < obj2->nodesetval->nodeNr; i++) {
+ valuePush(ctxt, xmlXPathObjectCopy(obj1));
+ valuePush(ctxt,
+ xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i]));
+ xmlXPathStringFunction(ctxt, 1);
+ xsltKeyFunction(ctxt, 2);
+ newobj = valuePop(ctxt);
+ ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
+ newobj->nodesetval);
+ xmlXPathFreeObject(newobj);
+ }
+ }
+ valuePush(ctxt, ret);
+ } else {
+ xmlNodeSetPtr nodelist = NULL;
+ xmlChar *key = NULL, *value;
+ const xmlChar *keyURI;
+ xsltTransformContextPtr tctxt;
+ xmlChar *qname, *prefix;
+ xmlXPathContextPtr xpctxt = ctxt->context;
+ xmlNodePtr tmpNode = NULL;
+ xsltDocumentPtr oldDocInfo;
+
+ tctxt = xsltXPathGetTransformContext(ctxt);
+
+ oldDocInfo = tctxt->document;
+
+ if (xpctxt->node == NULL) {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "Internal error in xsltKeyFunction(): "
+ "The context node is not set on the XPath context.\n");
+ tctxt->state = XSLT_STATE_STOPPED;
+ goto error;
+ }
+ /*
+ * Get the associated namespace URI if qualified name
+ */
+ qname = obj1->stringval;
+ key = xmlSplitQName2(qname, &prefix);
+ if (key == NULL) {
+ key = xmlStrdup(obj1->stringval);
+ keyURI = NULL;
+ if (prefix != NULL)
+ xmlFree(prefix);
+ } else {
+ if (prefix != NULL) {
+ keyURI = xmlXPathNsLookup(xpctxt, prefix);
+ if (keyURI == NULL) {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "key() : prefix %s is not bound\n", prefix);
+ /*
+ * TODO: Shouldn't we stop here?
+ */
+ }
+ xmlFree(prefix);
+ } else {
+ keyURI = NULL;
+ }
+ }
+
+ /*
+ * Force conversion of first arg to string
+ */
+ valuePush(ctxt, obj2);
+ xmlXPathStringFunction(ctxt, 1);
+ if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "key() : invalid arg expecting a string\n");
+ ctxt->error = XPATH_INVALID_TYPE;
+ goto error;
+ }
+ obj2 = valuePop(ctxt);
+ value = obj2->stringval;
+
+ /*
+ * We need to ensure that ctxt->document is available for
+ * xsltGetKey().
+ * First find the relevant doc, which is the context node's
+ * owner doc; using context->doc is not safe, since
+ * the doc could have been acquired via the document() function,
+ * or the doc might be a Result Tree Fragment.
+ * FUTURE INFO: In XSLT 2.0 the key() function takes an additional
+ * argument indicating the doc to use.
+ */
+ if (xpctxt->node->type == XML_NAMESPACE_DECL) {
+ /*
+ * REVISIT: This is a libxml hack! Check xpath.c for details.
+ * The XPath module sets the owner element of a ns-node on
+ * the ns->next field.
+ */
+ if ((((xmlNsPtr) xpctxt->node)->next != NULL) &&
+ (((xmlNsPtr) xpctxt->node)->next->type == XML_ELEMENT_NODE))
+ {
+ tmpNode = (xmlNodePtr) ((xmlNsPtr) xpctxt->node)->next;
+ }
+ } else
+ tmpNode = xpctxt->node;
+
+ if ((tmpNode == NULL) || (tmpNode->doc == NULL)) {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "Internal error in xsltKeyFunction(): "
+ "Couldn't get the doc of the XPath context node.\n");
+ goto error;
+ }
+
+ if ((tctxt->document == NULL) ||
+ (tctxt->document->doc != tmpNode->doc))
+ {
+ if (tmpNode->doc->name && (tmpNode->doc->name[0] == ' ')) {
+ /*
+ * This is a Result Tree Fragment.
+ */
+ if (tmpNode->doc->_private == NULL) {
+ tmpNode->doc->_private = xsltNewDocument(tctxt, tmpNode->doc);
+ if (tmpNode->doc->_private == NULL)
+ goto error;
+ }
+ tctxt->document = (xsltDocumentPtr) tmpNode->doc->_private;
+ } else {
+ /*
+ * May be the initial source doc or a doc acquired via the
+ * document() function.
+ */
+ tctxt->document = xsltFindDocument(tctxt, tmpNode->doc);
+ }
+ if (tctxt->document == NULL) {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "Internal error in xsltKeyFunction(): "
+ "Could not get the document info of a context doc.\n");
+ tctxt->state = XSLT_STATE_STOPPED;
+ goto error;
+ }
+ }
+ /*
+ * Get/compute the key value.
+ */
+ nodelist = xsltGetKey(tctxt, key, keyURI, value);
+
+error:
+ tctxt->document = oldDocInfo;
+ valuePush(ctxt, xmlXPathWrapNodeSet(
+ xmlXPathNodeSetMerge(NULL, nodelist)));
+ if (key != NULL)
+ xmlFree(key);
+ }
+
+ if (obj1 != NULL)
+ xmlXPathFreeObject(obj1);
+ if (obj2 != NULL)
+ xmlXPathFreeObject(obj2);
+}
+
+/**
+ * xsltUnparsedEntityURIFunction:
+ * @ctxt: the XPath Parser context
+ * @nargs: the number of arguments
+ *
+ * Implement the unparsed-entity-uri() XSLT function
+ * string unparsed-entity-uri(string)
+ */
+void
+xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){
+ xmlXPathObjectPtr obj;
+ xmlChar *str;
+
+ if ((nargs != 1) || (ctxt->value == NULL)) {
+ xsltGenericError(xsltGenericErrorContext,
+ "unparsed-entity-uri() : expects one string arg\n");
+ ctxt->error = XPATH_INVALID_ARITY;
+ return;
+ }
+ obj = valuePop(ctxt);
+ if (obj->type != XPATH_STRING) {
+ obj = xmlXPathConvertString(obj);
+ }
+
+ str = obj->stringval;
+ if (str == NULL) {
+ valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
+ } else {
+ xmlEntityPtr entity;
+
+ entity = xmlGetDocEntity(ctxt->context->doc, str);
+ if (entity == NULL) {
+ valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
+ } else {
+ if (entity->URI != NULL)
+ valuePush(ctxt, xmlXPathNewString(entity->URI));
+ else
+ valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
+ }
+ }
+ xmlXPathFreeObject(obj);
+}
+
+/**
+ * xsltFormatNumberFunction:
+ * @ctxt: the XPath Parser context
+ * @nargs: the number of arguments
+ *
+ * Implement the format-number() XSLT function
+ * string format-number(number, string, string?)
+ */
+void
+xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ xmlXPathObjectPtr numberObj = NULL;
+ xmlXPathObjectPtr formatObj = NULL;
+ xmlXPathObjectPtr decimalObj = NULL;
+ xsltStylesheetPtr sheet;
+ xsltDecimalFormatPtr formatValues;
+ xmlChar *result;
+ xsltTransformContextPtr tctxt;
+
+ tctxt = xsltXPathGetTransformContext(ctxt);
+ if (tctxt == NULL)
+ return;
+ sheet = tctxt->style;
+ if (sheet == NULL)
+ return;
+ formatValues = sheet->decimalFormat;
+
+ switch (nargs) {
+ case 3:
+ CAST_TO_STRING;
+ decimalObj = valuePop(ctxt);
+ formatValues = xsltDecimalFormatGetByName(sheet, decimalObj->stringval);
+ if (formatValues == NULL) {
+ xsltTransformError(tctxt, NULL, NULL,
+ "format-number() : undeclared decimal format '%s'\n",
+ decimalObj->stringval);
+ }
+ /* Intentional fall-through */
+ case 2:
+ CAST_TO_STRING;
+ formatObj = valuePop(ctxt);
+ CAST_TO_NUMBER;
+ numberObj = valuePop(ctxt);
+ break;
+ default:
+ XP_ERROR(XPATH_INVALID_ARITY);
+ }
+
+ if (formatValues != NULL) {
+ if (xsltFormatNumberConversion(formatValues,
+ formatObj->stringval,
+ numberObj->floatval,
+ &result) == XPATH_EXPRESSION_OK) {
+ valuePush(ctxt, xmlXPathNewString(result));
+ xmlFree(result);
+ }
+ }
+
+ xmlXPathFreeObject(numberObj);
+ xmlXPathFreeObject(formatObj);
+ xmlXPathFreeObject(decimalObj);
+}
+
+/**
+ * xsltGenerateIdFunction:
+ * @ctxt: the XPath Parser context
+ * @nargs: the number of arguments
+ *
+ * Implement the generate-id() XSLT function
+ * string generate-id(node-set?)
+ */
+void
+xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
+ static char base_address;
+ xmlNodePtr cur = NULL;
+ xmlXPathObjectPtr obj = NULL;
+ long val;
+ xmlChar str[30];
+
+ if (nargs == 0) {
+ cur = ctxt->context->node;
+ } else if (nargs == 1) {
+ xmlNodeSetPtr nodelist;
+ int i, ret;
+
+ if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) {
+ ctxt->error = XPATH_INVALID_TYPE;
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "generate-id() : invalid arg expecting a node-set\n");
+ return;
+ }
+ obj = valuePop(ctxt);
+ nodelist = obj->nodesetval;
+ if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) {
+ xmlXPathFreeObject(obj);
+ valuePush(ctxt, xmlXPathNewCString(""));
+ return;
+ }
+ cur = nodelist->nodeTab[0];
+ for (i = 1;i < nodelist->nodeNr;i++) {
+ ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]);
+ if (ret == -1)
+ cur = nodelist->nodeTab[i];
+ }
+ } else {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "generate-id() : invalid number of args %d\n", nargs);
+ ctxt->error = XPATH_INVALID_ARITY;
+ return;
+ }
+
+ if (obj)
+ xmlXPathFreeObject(obj);
+
+ val = (long)((char *)cur - (char *)&base_address);
+ if (val >= 0) {
+ snprintf((char *)str, sizeof(str), "idp%ld", val);
+ } else {
+ snprintf((char *)str, sizeof(str), "idm%ld", -val);
+ }
+ valuePush(ctxt, xmlXPathNewString(str));
+}
+
+/**
+ * xsltSystemPropertyFunction:
+ * @ctxt: the XPath Parser context
+ * @nargs: the number of arguments
+ *
+ * Implement the system-property() XSLT function
+ * object system-property(string)
+ */
+void
+xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){
+ xmlXPathObjectPtr obj;
+ xmlChar *prefix, *name;
+ const xmlChar *nsURI = NULL;
+
+ if (nargs != 1) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "system-property() : expects one string arg\n");
+ ctxt->error = XPATH_INVALID_ARITY;
+ return;
+ }
+ if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "system-property() : invalid arg expecting a string\n");
+ ctxt->error = XPATH_INVALID_TYPE;
+ return;
+ }
+ obj = valuePop(ctxt);
+ if (obj->stringval == NULL) {
+ valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
+ } else {
+ name = xmlSplitQName2(obj->stringval, &prefix);
+ if (name == NULL) {
+ name = xmlStrdup(obj->stringval);
+ } else {
+ nsURI = xmlXPathNsLookup(ctxt->context, prefix);
+ if (nsURI == NULL) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "system-property() : prefix %s is not bound\n", prefix);
+ }
+ }
+
+ if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) {
+#ifdef DOCBOOK_XSL_HACK
+ if (xmlStrEqual(name, (const xmlChar *)"vendor")) {
+ xsltStylesheetPtr sheet;
+ xsltTransformContextPtr tctxt;
+
+ tctxt = xsltXPathGetTransformContext(ctxt);
+ if ((tctxt != NULL) && (tctxt->inst != NULL) &&
+ (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) &&
+ (tctxt->inst->parent != NULL) &&
+ (xmlStrEqual(tctxt->inst->parent->name,
+ BAD_CAST "template")))
+ sheet = tctxt->style;
+ else
+ sheet = NULL;
+ if ((sheet != NULL) && (sheet->doc != NULL) &&
+ (sheet->doc->URL != NULL) &&
+ (xmlStrstr(sheet->doc->URL,
+ (const xmlChar *)"chunk") != NULL)) {
+ valuePush(ctxt, xmlXPathNewString(
+ (const xmlChar *)"libxslt (SAXON 6.2 compatible)"));
+
+ } else {
+ valuePush(ctxt, xmlXPathNewString(
+ (const xmlChar *)XSLT_DEFAULT_VENDOR));
+ }
+ } else
+#else
+ if (xmlStrEqual(name, (const xmlChar *)"vendor")) {
+ valuePush(ctxt, xmlXPathNewString(
+ (const xmlChar *)XSLT_DEFAULT_VENDOR));
+ } else
+#endif
+ if (xmlStrEqual(name, (const xmlChar *)"version")) {
+ valuePush(ctxt, xmlXPathNewString(
+ (const xmlChar *)XSLT_DEFAULT_VERSION));
+ } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) {
+ valuePush(ctxt, xmlXPathNewString(
+ (const xmlChar *)XSLT_DEFAULT_URL));
+ } else {
+ valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
+ }
+ } else {
+ valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
+ }
+ if (name != NULL)
+ xmlFree(name);
+ if (prefix != NULL)
+ xmlFree(prefix);
+ }
+ xmlXPathFreeObject(obj);
+}
+
+/**
+ * xsltElementAvailableFunction:
+ * @ctxt: the XPath Parser context
+ * @nargs: the number of arguments
+ *
+ * Implement the element-available() XSLT function
+ * boolean element-available(string)
+ */
+void
+xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
+ xmlXPathObjectPtr obj;
+ xmlChar *prefix, *name;
+ const xmlChar *nsURI = NULL;
+ xsltTransformContextPtr tctxt;
+
+ if (nargs != 1) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "element-available() : expects one string arg\n");
+ ctxt->error = XPATH_INVALID_ARITY;
+ return;
+ }
+ xmlXPathStringFunction(ctxt, 1);
+ if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "element-available() : invalid arg expecting a string\n");
+ ctxt->error = XPATH_INVALID_TYPE;
+ return;
+ }
+ obj = valuePop(ctxt);
+ tctxt = xsltXPathGetTransformContext(ctxt);
+ if (tctxt == NULL) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "element-available() : internal error tctxt == NULL\n");
+ xmlXPathFreeObject(obj);
+ valuePush(ctxt, xmlXPathNewBoolean(0));
+ return;
+ }
+
+
+ name = xmlSplitQName2(obj->stringval, &prefix);
+ if (name == NULL) {
+ xmlNsPtr ns;
+
+ name = xmlStrdup(obj->stringval);
+ ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL);
+ if (ns != NULL) nsURI = xmlStrdup(ns->href);
+ } else {
+ nsURI = xmlXPathNsLookup(ctxt->context, prefix);
+ if (nsURI == NULL) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "element-available() : prefix %s is not bound\n", prefix);
+ }
+ }
+
+ if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) {
+ valuePush(ctxt, xmlXPathNewBoolean(1));
+ } else {
+ valuePush(ctxt, xmlXPathNewBoolean(0));
+ }
+
+ xmlXPathFreeObject(obj);
+ if (name != NULL)
+ xmlFree(name);
+ if (prefix != NULL)
+ xmlFree(prefix);
+}
+
+/**
+ * xsltFunctionAvailableFunction:
+ * @ctxt: the XPath Parser context
+ * @nargs: the number of arguments
+ *
+ * Implement the function-available() XSLT function
+ * boolean function-available(string)
+ */
+void
+xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
+ xmlXPathObjectPtr obj;
+ xmlChar *prefix, *name;
+ const xmlChar *nsURI = NULL;
+
+ if (nargs != 1) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "function-available() : expects one string arg\n");
+ ctxt->error = XPATH_INVALID_ARITY;
+ return;
+ }
+ xmlXPathStringFunction(ctxt, 1);
+ if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "function-available() : invalid arg expecting a string\n");
+ ctxt->error = XPATH_INVALID_TYPE;
+ return;
+ }
+ obj = valuePop(ctxt);
+
+ name = xmlSplitQName2(obj->stringval, &prefix);
+ if (name == NULL) {
+ name = xmlStrdup(obj->stringval);
+ } else {
+ nsURI = xmlXPathNsLookup(ctxt->context, prefix);
+ if (nsURI == NULL) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "function-available() : prefix %s is not bound\n", prefix);
+ }
+ }
+
+ if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) {
+ valuePush(ctxt, xmlXPathNewBoolean(1));
+ } else {
+ valuePush(ctxt, xmlXPathNewBoolean(0));
+ }
+
+ xmlXPathFreeObject(obj);
+ if (name != NULL)
+ xmlFree(name);
+ if (prefix != NULL)
+ xmlFree(prefix);
+}
+
+/**
+ * xsltCurrentFunction:
+ * @ctxt: the XPath Parser context
+ * @nargs: the number of arguments
+ *
+ * Implement the current() XSLT function
+ * node-set current()
+ */
+static void
+xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){
+ xsltTransformContextPtr tctxt;
+
+ if (nargs != 0) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "current() : function uses no argument\n");
+ ctxt->error = XPATH_INVALID_ARITY;
+ return;
+ }
+ tctxt = xsltXPathGetTransformContext(ctxt);
+ if (tctxt == NULL) {
+ xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+ "current() : internal error tctxt == NULL\n");
+ valuePush(ctxt, xmlXPathNewNodeSet(NULL));
+ } else {
+ valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */
+ }
+}
+
+/************************************************************************
+ * *
+ * Registration of XSLT and libxslt functions *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltRegisterAllFunctions:
+ * @ctxt: the XPath context
+ *
+ * Registers all default XSLT functions in this context
+ */
+void
+xsltRegisterAllFunctions(xmlXPathContextPtr ctxt)
+{
+ xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current",
+ xsltCurrentFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document",
+ xsltDocumentFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri",
+ xsltUnparsedEntityURIFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number",
+ xsltFormatNumberFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id",
+ xsltGenerateIdFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property",
+ xsltSystemPropertyFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available",
+ xsltElementAvailableFunction);
+ xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available",
+ xsltFunctionAvailableFunction);
+}
diff --git a/libxslt/functions.h b/libxslt/functions.h
new file mode 100644
index 0000000..e0e0bf9
--- /dev/null
+++ b/libxslt/functions.h
@@ -0,0 +1,78 @@
+/*
+ * Summary: interface for the XSLT functions not from XPath
+ * Description: a set of extra functions coming from XSLT but not in XPath
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard and Bjorn Reese <breese@users.sourceforge.net>
+ */
+
+#ifndef __XML_XSLT_FUNCTIONS_H__
+#define __XML_XSLT_FUNCTIONS_H__
+
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * XSLT_REGISTER_FUNCTION_LOOKUP:
+ *
+ * Registering macro, not general purpose at all but used in different modules.
+ */
+#define XSLT_REGISTER_FUNCTION_LOOKUP(ctxt) \
+ xmlXPathRegisterFuncLookup((ctxt)->xpathCtxt, \
+ (xmlXPathFuncLookupFunc) xsltXPathFunctionLookup, \
+ (void *)(ctxt->xpathCtxt));
+
+XSLTPUBFUN xmlXPathFunction XSLTCALL
+ xsltXPathFunctionLookup (xmlXPathContextPtr ctxt,
+ const xmlChar *name,
+ const xmlChar *ns_uri);
+
+/*
+ * Interfaces for the functions implementations.
+ */
+
+XSLTPUBFUN void XSLTCALL
+ xsltDocumentFunction (xmlXPathParserContextPtr ctxt,
+ int nargs);
+XSLTPUBFUN void XSLTCALL
+ xsltKeyFunction (xmlXPathParserContextPtr ctxt,
+ int nargs);
+XSLTPUBFUN void XSLTCALL
+ xsltUnparsedEntityURIFunction (xmlXPathParserContextPtr ctxt,
+ int nargs);
+XSLTPUBFUN void XSLTCALL
+ xsltFormatNumberFunction (xmlXPathParserContextPtr ctxt,
+ int nargs);
+XSLTPUBFUN void XSLTCALL
+ xsltGenerateIdFunction (xmlXPathParserContextPtr ctxt,
+ int nargs);
+XSLTPUBFUN void XSLTCALL
+ xsltSystemPropertyFunction (xmlXPathParserContextPtr ctxt,
+ int nargs);
+XSLTPUBFUN void XSLTCALL
+ xsltElementAvailableFunction (xmlXPathParserContextPtr ctxt,
+ int nargs);
+XSLTPUBFUN void XSLTCALL
+ xsltFunctionAvailableFunction (xmlXPathParserContextPtr ctxt,
+ int nargs);
+
+/*
+ * And the registration
+ */
+
+XSLTPUBFUN void XSLTCALL
+ xsltRegisterAllFunctions (xmlXPathContextPtr ctxt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_FUNCTIONS_H__ */
+
diff --git a/libxslt/imports.c b/libxslt/imports.c
new file mode 100644
index 0000000..7262aab
--- /dev/null
+++ b/libxslt/imports.c
@@ -0,0 +1,414 @@
+/*
+ * imports.c: Implementation of the XSLT imports
+ *
+ * Reference:
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+#ifdef HAVE_NAN_H
+#include <nan.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/hash.h>
+#include <libxml/xmlerror.h>
+#include <libxml/uri.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "xsltutils.h"
+#include "preproc.h"
+#include "imports.h"
+#include "documents.h"
+#include "security.h"
+#include "pattern.h"
+
+
+/************************************************************************
+ * *
+ * Module interfaces *
+ * *
+ ************************************************************************/
+/**
+ * xsltFixImportedCompSteps:
+ * @master: the "master" stylesheet
+ * @style: the stylesheet being imported by the master
+ *
+ * normalize the comp steps for the stylesheet being imported
+ * by the master, together with any imports within that.
+ *
+ */
+static void xsltFixImportedCompSteps(xsltStylesheetPtr master,
+ xsltStylesheetPtr style) {
+ xsltStylesheetPtr res;
+ xmlHashScan(style->templatesHash,
+ (xmlHashScanner) xsltNormalizeCompSteps, master);
+ master->extrasNr += style->extrasNr;
+ for (res = style->imports; res != NULL; res = res->next) {
+ xsltFixImportedCompSteps(master, res);
+ }
+}
+
+/**
+ * xsltParseStylesheetImport:
+ * @style: the XSLT stylesheet
+ * @cur: the import element
+ *
+ * parse an XSLT stylesheet import element
+ *
+ * Returns 0 in case of success -1 in case of failure.
+ */
+
+int
+xsltParseStylesheetImport(xsltStylesheetPtr style, xmlNodePtr cur) {
+ int ret = -1;
+ xmlDocPtr import = NULL;
+ xmlChar *base = NULL;
+ xmlChar *uriRef = NULL;
+ xmlChar *URI = NULL;
+ xsltStylesheetPtr res;
+ xsltSecurityPrefsPtr sec;
+
+ if ((cur == NULL) || (style == NULL))
+ return (ret);
+
+ uriRef = xmlGetNsProp(cur, (const xmlChar *)"href", NULL);
+ if (uriRef == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:import : missing href attribute\n");
+ goto error;
+ }
+
+ base = xmlNodeGetBase(style->doc, cur);
+ URI = xmlBuildURI(uriRef, base);
+ if (URI == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:import : invalid URI reference %s\n", uriRef);
+ goto error;
+ }
+
+ res = style;
+ while (res != NULL) {
+ if (res->doc == NULL)
+ break;
+ if (xmlStrEqual(res->doc->URL, URI)) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:import : recursion detected on imported URL %s\n", URI);
+ goto error;
+ }
+ res = res->parent;
+ }
+
+ /*
+ * Security framework check
+ */
+ sec = xsltGetDefaultSecurityPrefs();
+ if (sec != NULL) {
+ int secres;
+
+ secres = xsltCheckRead(sec, NULL, URI);
+ if (secres == 0) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsl:import: read rights for %s denied\n",
+ URI);
+ goto error;
+ }
+ }
+
+ import = xsltDocDefaultLoader(URI, style->dict, XSLT_PARSE_OPTIONS,
+ (void *) style, XSLT_LOAD_STYLESHEET);
+ if (import == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:import : unable to load %s\n", URI);
+ goto error;
+ }
+
+ res = xsltParseStylesheetImportedDoc(import, style);
+ if (res != NULL) {
+ res->next = style->imports;
+ style->imports = res;
+ if (style->parent == NULL) {
+ xsltFixImportedCompSteps(style, res);
+ }
+ ret = 0;
+ } else {
+ xmlFreeDoc(import);
+ }
+
+error:
+ if (uriRef != NULL)
+ xmlFree(uriRef);
+ if (base != NULL)
+ xmlFree(base);
+ if (URI != NULL)
+ xmlFree(URI);
+
+ return (ret);
+}
+
+/**
+ * xsltParseStylesheetInclude:
+ * @style: the XSLT stylesheet
+ * @cur: the include node
+ *
+ * parse an XSLT stylesheet include element
+ *
+ * Returns 0 in case of success -1 in case of failure
+ */
+
+int
+xsltParseStylesheetInclude(xsltStylesheetPtr style, xmlNodePtr cur) {
+ int ret = -1;
+ xmlDocPtr oldDoc;
+ xmlChar *base = NULL;
+ xmlChar *uriRef = NULL;
+ xmlChar *URI = NULL;
+ xsltStylesheetPtr result;
+ xsltDocumentPtr include;
+ xsltDocumentPtr docptr;
+ int oldNopreproc;
+
+ if ((cur == NULL) || (style == NULL))
+ return (ret);
+
+ uriRef = xmlGetNsProp(cur, (const xmlChar *)"href", NULL);
+ if (uriRef == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:include : missing href attribute\n");
+ goto error;
+ }
+
+ base = xmlNodeGetBase(style->doc, cur);
+ URI = xmlBuildURI(uriRef, base);
+ if (URI == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:include : invalid URI reference %s\n", uriRef);
+ goto error;
+ }
+
+ /*
+ * in order to detect recursion, we check all previously included
+ * stylesheets.
+ */
+ docptr = style->includes;
+ while (docptr != NULL) {
+ if (xmlStrEqual(docptr->doc->URL, URI)) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:include : recursion detected on included URL %s\n", URI);
+ goto error;
+ }
+ docptr = docptr->includes;
+ }
+
+ include = xsltLoadStyleDocument(style, URI);
+ if (include == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:include : unable to load %s\n", URI);
+ goto error;
+ }
+#ifdef XSLT_REFACTORED
+ if (IS_XSLT_ELEM_FAST(cur) && (cur->psvi != NULL)) {
+ ((xsltStyleItemIncludePtr) cur->psvi)->include = include;
+ } else {
+ xsltTransformError(NULL, style, cur,
+ "Internal error: (xsltParseStylesheetInclude) "
+ "The xsl:include element was not compiled.\n", URI);
+ style->errors++;
+ }
+#endif
+ oldDoc = style->doc;
+ style->doc = include->doc;
+ /* chain to stylesheet for recursion checking */
+ include->includes = style->includes;
+ style->includes = include;
+ oldNopreproc = style->nopreproc;
+ style->nopreproc = include->preproc;
+ /*
+ * TODO: This will change some values of the
+ * including stylesheet with every included module
+ * (e.g. excluded-result-prefixes)
+ * We need to strictly seperate such stylesheet-owned values.
+ */
+ result = xsltParseStylesheetProcess(style, include->doc);
+ style->nopreproc = oldNopreproc;
+ include->preproc = 1;
+ style->includes = include->includes;
+ style->doc = oldDoc;
+ if (result == NULL) {
+ ret = -1;
+ goto error;
+ }
+ ret = 0;
+
+error:
+ if (uriRef != NULL)
+ xmlFree(uriRef);
+ if (base != NULL)
+ xmlFree(base);
+ if (URI != NULL)
+ xmlFree(URI);
+
+ return (ret);
+}
+
+/**
+ * xsltNextImport:
+ * @cur: the current XSLT stylesheet
+ *
+ * Find the next stylesheet in import precedence.
+ *
+ * Returns the next stylesheet or NULL if it was the last one
+ */
+
+xsltStylesheetPtr
+xsltNextImport(xsltStylesheetPtr cur) {
+ if (cur == NULL)
+ return(NULL);
+ if (cur->imports != NULL)
+ return(cur->imports);
+ if (cur->next != NULL)
+ return(cur->next) ;
+ do {
+ cur = cur->parent;
+ if (cur == NULL) break;
+ if (cur->next != NULL) return(cur->next);
+ } while (cur != NULL);
+ return(cur);
+}
+
+/**
+ * xsltNeedElemSpaceHandling:
+ * @ctxt: an XSLT transformation context
+ *
+ * Checks whether that stylesheet requires white-space stripping
+ *
+ * Returns 1 if space should be stripped, 0 if not
+ */
+
+int
+xsltNeedElemSpaceHandling(xsltTransformContextPtr ctxt) {
+ xsltStylesheetPtr style;
+
+ if (ctxt == NULL)
+ return(0);
+ style = ctxt->style;
+ while (style != NULL) {
+ if (style->stripSpaces != NULL)
+ return(1);
+ style = xsltNextImport(style);
+ }
+ return(0);
+}
+
+/**
+ * xsltFindElemSpaceHandling:
+ * @ctxt: an XSLT transformation context
+ * @node: an XML node
+ *
+ * Find strip-space or preserve-space informations for an element
+ * respect the import precedence or the wildcards
+ *
+ * Returns 1 if space should be stripped, 0 if not, and 2 if everything
+ * should be CDTATA wrapped.
+ */
+
+int
+xsltFindElemSpaceHandling(xsltTransformContextPtr ctxt, xmlNodePtr node) {
+ xsltStylesheetPtr style;
+ const xmlChar *val;
+
+ if ((ctxt == NULL) || (node == NULL))
+ return(0);
+ style = ctxt->style;
+ while (style != NULL) {
+ if (node->ns != NULL) {
+ val = (const xmlChar *)
+ xmlHashLookup2(style->stripSpaces, node->name, node->ns->href);
+ if (val == NULL) {
+ val = (const xmlChar *)
+ xmlHashLookup2(style->stripSpaces, BAD_CAST "*",
+ node->ns->href);
+ }
+ } else {
+ val = (const xmlChar *)
+ xmlHashLookup2(style->stripSpaces, node->name, NULL);
+ }
+ if (val != NULL) {
+ if (xmlStrEqual(val, (xmlChar *) "strip"))
+ return(1);
+ if (xmlStrEqual(val, (xmlChar *) "preserve"))
+ return(0);
+ }
+ if (style->stripAll == 1)
+ return(1);
+ if (style->stripAll == -1)
+ return(0);
+
+ style = xsltNextImport(style);
+ }
+ return(0);
+}
+
+/**
+ * xsltFindTemplate:
+ * @ctxt: an XSLT transformation context
+ * @name: the template name
+ * @nameURI: the template name URI
+ *
+ * Finds the named template, apply import precedence rule.
+ * REVISIT TODO: We'll change the nameURI fields of
+ * templates to be in the string dict, so if the
+ * specified @nameURI is in the same dict, then use pointer
+ * comparison. Check if this can be done in a sane way.
+ * Maybe this function is not needed internally at
+ * transformation-time if we hard-wire the called templates
+ * to the caller.
+ *
+ * Returns the xsltTemplatePtr or NULL if not found
+ */
+xsltTemplatePtr
+xsltFindTemplate(xsltTransformContextPtr ctxt, const xmlChar *name,
+ const xmlChar *nameURI) {
+ xsltTemplatePtr cur;
+ xsltStylesheetPtr style;
+
+ if ((ctxt == NULL) || (name == NULL))
+ return(NULL);
+ style = ctxt->style;
+ while (style != NULL) {
+ if (style->namedTemplates != NULL) {
+ cur = (xsltTemplatePtr)
+ xmlHashLookup2(style->namedTemplates, name, nameURI);
+ if (cur != NULL)
+ return(cur);
+ }
+
+ style = xsltNextImport(style);
+ }
+ return(NULL);
+}
+
diff --git a/libxslt/imports.h b/libxslt/imports.h
new file mode 100644
index 0000000..95e44e5
--- /dev/null
+++ b/libxslt/imports.h
@@ -0,0 +1,75 @@
+/*
+ * Summary: interface for the XSLT import support
+ * Description: macros and fuctions needed to implement and
+ * access the import tree
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_IMPORTS_H__
+#define __XML_XSLT_IMPORTS_H__
+
+#include <libxml/tree.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * XSLT_GET_IMPORT_PTR:
+ *
+ * A macro to import pointers from the stylesheet cascading order.
+ */
+#define XSLT_GET_IMPORT_PTR(res, style, name) { \
+ xsltStylesheetPtr st = style; \
+ res = NULL; \
+ while (st != NULL) { \
+ if (st->name != NULL) { res = st->name; break; } \
+ st = xsltNextImport(st); \
+ }}
+
+/**
+ * XSLT_GET_IMPORT_INT:
+ *
+ * A macro to import intergers from the stylesheet cascading order.
+ */
+#define XSLT_GET_IMPORT_INT(res, style, name) { \
+ xsltStylesheetPtr st = style; \
+ res = -1; \
+ while (st != NULL) { \
+ if (st->name != -1) { res = st->name; break; } \
+ st = xsltNextImport(st); \
+ }}
+
+/*
+ * Module interfaces
+ */
+XSLTPUBFUN int XSLTCALL
+ xsltParseStylesheetImport(xsltStylesheetPtr style,
+ xmlNodePtr cur);
+XSLTPUBFUN int XSLTCALL
+ xsltParseStylesheetInclude
+ (xsltStylesheetPtr style,
+ xmlNodePtr cur);
+XSLTPUBFUN xsltStylesheetPtr XSLTCALL
+ xsltNextImport (xsltStylesheetPtr style);
+XSLTPUBFUN int XSLTCALL
+ xsltNeedElemSpaceHandling(xsltTransformContextPtr ctxt);
+XSLTPUBFUN int XSLTCALL
+ xsltFindElemSpaceHandling(xsltTransformContextPtr ctxt,
+ xmlNodePtr node);
+XSLTPUBFUN xsltTemplatePtr XSLTCALL
+ xsltFindTemplate (xsltTransformContextPtr ctxt,
+ const xmlChar *name,
+ const xmlChar *nameURI);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_IMPORTS_H__ */
+
diff --git a/libxslt/keys.c b/libxslt/keys.c
new file mode 100644
index 0000000..43a343e
--- /dev/null
+++ b/libxslt/keys.c
@@ -0,0 +1,935 @@
+/*
+ * keys.c: Implemetation of the keys support
+ *
+ * Reference:
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/valid.h>
+#include <libxml/hash.h>
+#include <libxml/xmlerror.h>
+#include <libxml/parserInternals.h>
+#include <libxml/xpathInternals.h>
+#include <libxml/xpath.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "xsltutils.h"
+#include "imports.h"
+#include "templates.h"
+#include "keys.h"
+
+#ifdef WITH_XSLT_DEBUG
+#define WITH_XSLT_DEBUG_KEYS
+#endif
+
+static int
+xsltInitDocKeyTable(xsltTransformContextPtr ctxt, const xmlChar *name,
+ const xmlChar *nameURI);
+
+/************************************************************************
+ * *
+ * Type functions *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltNewKeyDef:
+ * @name: the key name or NULL
+ * @nameURI: the name URI or NULL
+ *
+ * Create a new XSLT KeyDef
+ *
+ * Returns the newly allocated xsltKeyDefPtr or NULL in case of error
+ */
+static xsltKeyDefPtr
+xsltNewKeyDef(const xmlChar *name, const xmlChar *nameURI) {
+ xsltKeyDefPtr cur;
+
+ cur = (xsltKeyDefPtr) xmlMalloc(sizeof(xsltKeyDef));
+ if (cur == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltNewKeyDef : malloc failed\n");
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltKeyDef));
+ if (name != NULL)
+ cur->name = xmlStrdup(name);
+ if (nameURI != NULL)
+ cur->nameURI = xmlStrdup(nameURI);
+ cur->nsList = NULL;
+ return(cur);
+}
+
+/**
+ * xsltFreeKeyDef:
+ * @keyd: an XSLT key definition
+ *
+ * Free up the memory allocated by @keyd
+ */
+static void
+xsltFreeKeyDef(xsltKeyDefPtr keyd) {
+ if (keyd == NULL)
+ return;
+ if (keyd->comp != NULL)
+ xmlXPathFreeCompExpr(keyd->comp);
+ if (keyd->usecomp != NULL)
+ xmlXPathFreeCompExpr(keyd->usecomp);
+ if (keyd->name != NULL)
+ xmlFree(keyd->name);
+ if (keyd->nameURI != NULL)
+ xmlFree(keyd->nameURI);
+ if (keyd->match != NULL)
+ xmlFree(keyd->match);
+ if (keyd->use != NULL)
+ xmlFree(keyd->use);
+ if (keyd->nsList != NULL)
+ xmlFree(keyd->nsList);
+ memset(keyd, -1, sizeof(xsltKeyDef));
+ xmlFree(keyd);
+}
+
+/**
+ * xsltFreeKeyDefList:
+ * @keyd: an XSLT key definition list
+ *
+ * Free up the memory allocated by all the elements of @keyd
+ */
+static void
+xsltFreeKeyDefList(xsltKeyDefPtr keyd) {
+ xsltKeyDefPtr cur;
+
+ while (keyd != NULL) {
+ cur = keyd;
+ keyd = keyd->next;
+ xsltFreeKeyDef(cur);
+ }
+}
+
+/**
+ * xsltNewKeyTable:
+ * @name: the key name or NULL
+ * @nameURI: the name URI or NULL
+ *
+ * Create a new XSLT KeyTable
+ *
+ * Returns the newly allocated xsltKeyTablePtr or NULL in case of error
+ */
+static xsltKeyTablePtr
+xsltNewKeyTable(const xmlChar *name, const xmlChar *nameURI) {
+ xsltKeyTablePtr cur;
+
+ cur = (xsltKeyTablePtr) xmlMalloc(sizeof(xsltKeyTable));
+ if (cur == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltNewKeyTable : malloc failed\n");
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltKeyTable));
+ if (name != NULL)
+ cur->name = xmlStrdup(name);
+ if (nameURI != NULL)
+ cur->nameURI = xmlStrdup(nameURI);
+ cur->keys = xmlHashCreate(0);
+ return(cur);
+}
+
+/**
+ * xsltFreeKeyTable:
+ * @keyt: an XSLT key table
+ *
+ * Free up the memory allocated by @keyt
+ */
+static void
+xsltFreeKeyTable(xsltKeyTablePtr keyt) {
+ if (keyt == NULL)
+ return;
+ if (keyt->name != NULL)
+ xmlFree(keyt->name);
+ if (keyt->nameURI != NULL)
+ xmlFree(keyt->nameURI);
+ if (keyt->keys != NULL)
+ xmlHashFree(keyt->keys,
+ (xmlHashDeallocator) xmlXPathFreeNodeSet);
+ memset(keyt, -1, sizeof(xsltKeyTable));
+ xmlFree(keyt);
+}
+
+/**
+ * xsltFreeKeyTableList:
+ * @keyt: an XSLT key table list
+ *
+ * Free up the memory allocated by all the elements of @keyt
+ */
+static void
+xsltFreeKeyTableList(xsltKeyTablePtr keyt) {
+ xsltKeyTablePtr cur;
+
+ while (keyt != NULL) {
+ cur = keyt;
+ keyt = keyt->next;
+ xsltFreeKeyTable(cur);
+ }
+}
+
+/************************************************************************
+ * *
+ * The interpreter for the precompiled patterns *
+ * *
+ ************************************************************************/
+
+
+/**
+ * xsltFreeKeys:
+ * @style: an XSLT stylesheet
+ *
+ * Free up the memory used by XSLT keys in a stylesheet
+ */
+void
+xsltFreeKeys(xsltStylesheetPtr style) {
+ if (style->keys)
+ xsltFreeKeyDefList((xsltKeyDefPtr) style->keys);
+}
+
+/**
+ * skipString:
+ * @cur: the current pointer
+ * @end: the current offset
+ *
+ * skip a string delimited by " or '
+ *
+ * Returns the byte after the string or -1 in case of error
+ */
+static int
+skipString(const xmlChar *cur, int end) {
+ xmlChar limit;
+
+ if ((cur == NULL) || (end < 0)) return(-1);
+ if ((cur[end] == '\'') || (cur[end] == '"')) limit = cur[end];
+ else return(end);
+ end++;
+ while (cur[end] != 0) {
+ if (cur[end] == limit)
+ return(end + 1);
+ end++;
+ }
+ return(-1);
+}
+
+/**
+ * skipPredicate:
+ * @cur: the current pointer
+ * @end: the current offset
+ *
+ * skip a predicate
+ *
+ * Returns the byte after the predicate or -1 in case of error
+ */
+static int
+skipPredicate(const xmlChar *cur, int end) {
+ if ((cur == NULL) || (end < 0)) return(-1);
+ if (cur[end] != '[') return(end);
+ end++;
+ while (cur[end] != 0) {
+ if ((cur[end] == '\'') || (cur[end] == '"')) {
+ end = skipString(cur, end);
+ if (end <= 0)
+ return(-1);
+ continue;
+ } else if (cur[end] == '[') {
+ end = skipPredicate(cur, end);
+ if (end <= 0)
+ return(-1);
+ continue;
+ } else if (cur[end] == ']')
+ return(end + 1);
+ end++;
+ }
+ return(-1);
+}
+
+/**
+ * xsltAddKey:
+ * @style: an XSLT stylesheet
+ * @name: the key name or NULL
+ * @nameURI: the name URI or NULL
+ * @match: the match value
+ * @use: the use value
+ * @inst: the key instruction
+ *
+ * add a key definition to a stylesheet
+ *
+ * Returns 0 in case of success, and -1 in case of failure.
+ */
+int
+xsltAddKey(xsltStylesheetPtr style, const xmlChar *name,
+ const xmlChar *nameURI, const xmlChar *match,
+ const xmlChar *use, xmlNodePtr inst) {
+ xsltKeyDefPtr key;
+ xmlChar *pattern = NULL;
+ int current, end, start, i = 0;
+
+ if ((style == NULL) || (name == NULL) || (match == NULL) || (use == NULL))
+ return(-1);
+
+#ifdef WITH_XSLT_DEBUG_KEYS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Add key %s, match %s, use %s\n", name, match, use);
+#endif
+
+ key = xsltNewKeyDef(name, nameURI);
+ key->match = xmlStrdup(match);
+ key->use = xmlStrdup(use);
+ key->inst = inst;
+ key->nsList = xmlGetNsList(inst->doc, inst);
+ if (key->nsList != NULL) {
+ while (key->nsList[i] != NULL)
+ i++;
+ }
+ key->nsNr = i;
+
+ /*
+ * Split the | and register it as as many keys
+ */
+ current = end = 0;
+ while (match[current] != 0) {
+ start = current;
+ while (IS_BLANK_CH(match[current]))
+ current++;
+ end = current;
+ while ((match[end] != 0) && (match[end] != '|')) {
+ if (match[end] == '[') {
+ end = skipPredicate(match, end);
+ if (end <= 0) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:key : 'match' pattern is malformed: %s",
+ key->match);
+ if (style != NULL) style->errors++;
+ goto error;
+ }
+ } else
+ end++;
+ }
+ if (current == end) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:key : 'match' pattern is empty\n");
+ if (style != NULL) style->errors++;
+ goto error;
+ }
+ if (match[start] != '/') {
+ pattern = xmlStrcat(pattern, (xmlChar *)"//");
+ if (pattern == NULL) {
+ if (style != NULL) style->errors++;
+ goto error;
+ }
+ }
+ pattern = xmlStrncat(pattern, &match[start], end - start);
+ if (pattern == NULL) {
+ if (style != NULL) style->errors++;
+ goto error;
+ }
+
+ if (match[end] == '|') {
+ pattern = xmlStrcat(pattern, (xmlChar *)"|");
+ end++;
+ }
+ current = end;
+ }
+ if (pattern == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:key : 'match' pattern is empty\n");
+ if (style != NULL) style->errors++;
+ goto error;
+ }
+#ifdef WITH_XSLT_DEBUG_KEYS
+ xsltGenericDebug(xsltGenericDebugContext,
+ " resulting pattern %s\n", pattern);
+#endif
+ /*
+ * XSLT-1: "It is an error for the value of either the use
+ * attribute or the match attribute to contain a
+ * VariableReference."
+ * TODO: We should report a variable-reference at compile-time.
+ * Maybe a search for "$", if it occurs outside of quotation
+ * marks, could be sufficient.
+ */
+#ifdef XML_XPATH_NOVAR
+ key->comp = xsltXPathCompileFlags(style, pattern, XML_XPATH_NOVAR);
+#else
+ key->comp = xsltXPathCompile(style, pattern);
+#endif
+ if (key->comp == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:key : 'match' pattern compilation failed '%s'\n",
+ pattern);
+ if (style != NULL) style->errors++;
+ }
+#ifdef XML_XPATH_NOVAR
+ key->usecomp = xsltXPathCompileFlags(style, use, XML_XPATH_NOVAR);
+#else
+ key->usecomp = xsltXPathCompile(style, use);
+#endif
+ if (key->usecomp == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:key : 'use' expression compilation failed '%s'\n",
+ use);
+ if (style != NULL) style->errors++;
+ }
+
+ /*
+ * Sometimes the stylesheet writer use the order to ease the
+ * resolution of keys when they are dependant, keep the provided
+ * order so add the new one at the end.
+ */
+ if (style->keys == NULL) {
+ style->keys = key;
+ } else {
+ xsltKeyDefPtr prev = style->keys;
+
+ while (prev->next != NULL)
+ prev = prev->next;
+
+ prev->next = key;
+ }
+ key->next = NULL;
+
+error:
+ if (pattern != NULL)
+ xmlFree(pattern);
+ return(0);
+}
+
+/**
+ * xsltGetKey:
+ * @ctxt: an XSLT transformation context
+ * @name: the key name or NULL
+ * @nameURI: the name URI or NULL
+ * @value: the key value to look for
+ *
+ * Looks up a key of the in current source doc (the document info
+ * on @ctxt->document). Computes the key if not already done
+ * for the current source doc.
+ *
+ * Returns the nodeset resulting from the query or NULL
+ */
+xmlNodeSetPtr
+xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name,
+ const xmlChar *nameURI, const xmlChar *value) {
+ xmlNodeSetPtr ret;
+ xsltKeyTablePtr table;
+ int init_table = 0;
+
+ if ((ctxt == NULL) || (name == NULL) || (value == NULL) ||
+ (ctxt->document == NULL))
+ return(NULL);
+
+#ifdef WITH_XSLT_DEBUG_KEYS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Get key %s, value %s\n", name, value);
+#endif
+
+ /*
+ * keys are computed only on-demand on first key access for a document
+ */
+ if ((ctxt->document->nbKeysComputed < ctxt->nbKeys) &&
+ (ctxt->keyInitLevel == 0)) {
+ /*
+ * If non-recursive behaviour, just try to initialize all keys
+ */
+ if (xsltInitAllDocKeys(ctxt))
+ return(NULL);
+ }
+
+retry:
+ table = (xsltKeyTablePtr) ctxt->document->keys;
+ while (table != NULL) {
+ if (((nameURI != NULL) == (table->nameURI != NULL)) &&
+ xmlStrEqual(table->name, name) &&
+ xmlStrEqual(table->nameURI, nameURI))
+ {
+ ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value);
+ return(ret);
+ }
+ table = table->next;
+ }
+
+ if ((ctxt->keyInitLevel != 0) && (init_table == 0)) {
+ /*
+ * Apparently one key is recursive and this one is needed,
+ * initialize just it, that time and retry
+ */
+ xsltInitDocKeyTable(ctxt, name, nameURI);
+ init_table = 1;
+ goto retry;
+ }
+
+ return(NULL);
+}
+
+
+/**
+ * xsltInitDocKeyTable:
+ *
+ * INTERNAL ROUTINE ONLY
+ *
+ * Check if any keys on the current document need to be computed
+ */
+static int
+xsltInitDocKeyTable(xsltTransformContextPtr ctxt, const xmlChar *name,
+ const xmlChar *nameURI)
+{
+ xsltStylesheetPtr style;
+ xsltKeyDefPtr keyd = NULL;
+ int found = 0;
+
+#ifdef KEY_INIT_DEBUG
+fprintf(stderr, "xsltInitDocKeyTable %s\n", name);
+#endif
+
+ style = ctxt->style;
+ while (style != NULL) {
+ keyd = (xsltKeyDefPtr) style->keys;
+ while (keyd != NULL) {
+ if (((keyd->nameURI != NULL) ==
+ (nameURI != NULL)) &&
+ xmlStrEqual(keyd->name, name) &&
+ xmlStrEqual(keyd->nameURI, nameURI))
+ {
+ xsltInitCtxtKey(ctxt, ctxt->document, keyd);
+ if (ctxt->document->nbKeysComputed == ctxt->nbKeys)
+ return(0);
+ found = 1;
+ }
+ keyd = keyd->next;
+ }
+ style = xsltNextImport(style);
+ }
+ if (found == 0) {
+#ifdef WITH_XSLT_DEBUG_KEYS
+ XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltInitDocKeyTable: did not found %s\n", name));
+#endif
+ xsltTransformError(ctxt, NULL, keyd? keyd->inst : NULL,
+ "Failed to find key definition for %s\n", name);
+ ctxt->state = XSLT_STATE_STOPPED;
+ return(-1);
+ }
+#ifdef KEY_INIT_DEBUG
+fprintf(stderr, "xsltInitDocKeyTable %s done\n", name);
+#endif
+ return(0);
+}
+
+/**
+ * xsltInitAllDocKeys:
+ * @ctxt: transformation context
+ *
+ * INTERNAL ROUTINE ONLY
+ *
+ * Check if any keys on the current document need to be computed
+ *
+ * Returns 0 in case of success, -1 in case of failure
+ */
+int
+xsltInitAllDocKeys(xsltTransformContextPtr ctxt)
+{
+ xsltStylesheetPtr style;
+ xsltKeyDefPtr keyd;
+ xsltKeyTablePtr table;
+
+ if (ctxt == NULL)
+ return(-1);
+
+#ifdef KEY_INIT_DEBUG
+fprintf(stderr, "xsltInitAllDocKeys %d %d\n",
+ ctxt->document->nbKeysComputed, ctxt->nbKeys);
+#endif
+
+ if (ctxt->document->nbKeysComputed == ctxt->nbKeys)
+ return(0);
+
+
+ /*
+ * TODO: This could be further optimized
+ */
+ style = ctxt->style;
+ while (style) {
+ keyd = (xsltKeyDefPtr) style->keys;
+ while (keyd != NULL) {
+#ifdef KEY_INIT_DEBUG
+fprintf(stderr, "Init key %s\n", keyd->name);
+#endif
+ /*
+ * Check if keys with this QName have been already
+ * computed.
+ */
+ table = (xsltKeyTablePtr) ctxt->document->keys;
+ while (table) {
+ if (((keyd->nameURI != NULL) == (table->nameURI != NULL)) &&
+ xmlStrEqual(keyd->name, table->name) &&
+ xmlStrEqual(keyd->nameURI, table->nameURI))
+ {
+ break;
+ }
+ table = table->next;
+ }
+ if (table == NULL) {
+ /*
+ * Keys with this QName have not been yet computed.
+ */
+ xsltInitDocKeyTable(ctxt, keyd->name, keyd->nameURI);
+ }
+ keyd = keyd->next;
+ }
+ style = xsltNextImport(style);
+ }
+#ifdef KEY_INIT_DEBUG
+fprintf(stderr, "xsltInitAllDocKeys: done\n");
+#endif
+ return(0);
+}
+
+/**
+ * xsltInitCtxtKey:
+ * @ctxt: an XSLT transformation context
+ * @idoc: the document information (holds key values)
+ * @keyDef: the key definition
+ *
+ * Computes the key tables this key and for the current input document.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int
+xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc,
+ xsltKeyDefPtr keyDef)
+{
+ int i, len, k;
+ xmlNodeSetPtr matchList = NULL, keylist;
+ xmlXPathObjectPtr matchRes = NULL, useRes = NULL;
+ xmlChar *str = NULL;
+ xsltKeyTablePtr table;
+ xmlNodePtr oldInst, cur;
+ xmlNodePtr oldContextNode;
+ xsltDocumentPtr oldDocInfo;
+ int oldXPPos, oldXPSize;
+ xmlDocPtr oldXPDoc;
+ int oldXPNsNr;
+ xmlNsPtr *oldXPNamespaces;
+ xmlXPathContextPtr xpctxt;
+
+#ifdef KEY_INIT_DEBUG
+fprintf(stderr, "xsltInitCtxtKey %s : %d\n", keyDef->name, ctxt->keyInitLevel);
+#endif
+
+ if ((keyDef->comp == NULL) || (keyDef->usecomp == NULL))
+ return(-1);
+
+ /*
+ * Detect recursive keys
+ */
+ if (ctxt->keyInitLevel > ctxt->nbKeys) {
+#ifdef WITH_XSLT_DEBUG_KEYS
+ XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltInitCtxtKey: key definition of %s is recursive\n",
+ keyDef->name));
+#endif
+ xsltTransformError(ctxt, NULL, keyDef->inst,
+ "Key definition for %s is recursive\n", keyDef->name);
+ ctxt->state = XSLT_STATE_STOPPED;
+ return(-1);
+ }
+ ctxt->keyInitLevel++;
+
+ xpctxt = ctxt->xpathCtxt;
+ idoc->nbKeysComputed++;
+ /*
+ * Save context state.
+ */
+ oldInst = ctxt->inst;
+ oldDocInfo = ctxt->document;
+ oldContextNode = ctxt->node;
+
+ oldXPDoc = xpctxt->doc;
+ oldXPPos = xpctxt->proximityPosition;
+ oldXPSize = xpctxt->contextSize;
+ oldXPNsNr = xpctxt->nsNr;
+ oldXPNamespaces = xpctxt->namespaces;
+
+ /*
+ * Set up contexts.
+ */
+ ctxt->document = idoc;
+ ctxt->node = (xmlNodePtr) idoc->doc;
+ ctxt->inst = keyDef->inst;
+
+ xpctxt->doc = idoc->doc;
+ xpctxt->node = (xmlNodePtr) idoc->doc;
+ /* TODO : clarify the use of namespaces in keys evaluation */
+ xpctxt->namespaces = keyDef->nsList;
+ xpctxt->nsNr = keyDef->nsNr;
+
+ /*
+ * Evaluate the 'match' expression of the xsl:key.
+ * TODO: The 'match' is a *pattern*.
+ */
+ matchRes = xmlXPathCompiledEval(keyDef->comp, xpctxt);
+ if (matchRes == NULL) {
+
+#ifdef WITH_XSLT_DEBUG_KEYS
+ XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltInitCtxtKey: %s evaluation failed\n", keyDef->match));
+#endif
+ xsltTransformError(ctxt, NULL, keyDef->inst,
+ "Failed to evaluate the 'match' expression.\n");
+ ctxt->state = XSLT_STATE_STOPPED;
+ goto error;
+ } else {
+ if (matchRes->type == XPATH_NODESET) {
+ matchList = matchRes->nodesetval;
+
+#ifdef WITH_XSLT_DEBUG_KEYS
+ if (matchList != NULL)
+ XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltInitCtxtKey: %s evaluates to %d nodes\n",
+ keyDef->match, matchList->nodeNr));
+#endif
+ } else {
+ /*
+ * Is not a node set, but must be.
+ */
+#ifdef WITH_XSLT_DEBUG_KEYS
+ XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltInitCtxtKey: %s is not a node set\n", keyDef->match));
+#endif
+ xsltTransformError(ctxt, NULL, keyDef->inst,
+ "The 'match' expression did not evaluate to a node set.\n");
+ ctxt->state = XSLT_STATE_STOPPED;
+ goto error;
+ }
+ }
+ if ((matchList == NULL) || (matchList->nodeNr <= 0))
+ goto exit;
+
+ /**
+ * Multiple key definitions for the same name are allowed, so
+ * we must check if the key is already present for this doc
+ */
+ table = (xsltKeyTablePtr) idoc->keys;
+ while (table != NULL) {
+ if (xmlStrEqual(table->name, keyDef->name) &&
+ (((keyDef->nameURI == NULL) && (table->nameURI == NULL)) ||
+ ((keyDef->nameURI != NULL) && (table->nameURI != NULL) &&
+ (xmlStrEqual(table->nameURI, keyDef->nameURI)))))
+ break;
+ table = table->next;
+ }
+ /**
+ * If the key was not previously defined, create it now and
+ * chain it to the list of keys for the doc
+ */
+ if (table == NULL) {
+ table = xsltNewKeyTable(keyDef->name, keyDef->nameURI);
+ if (table == NULL)
+ goto error;
+ table->next = idoc->keys;
+ idoc->keys = table;
+ }
+
+ /*
+ * SPEC XSLT 1.0 (XSLT 2.0 does not clarify the context size!)
+ * "...the use attribute of the xsl:key element is evaluated with x as
+ " the current node and with a node list containing just x as the
+ * current node list"
+ */
+ xpctxt->contextSize = 1;
+ xpctxt->proximityPosition = 1;
+
+ for (i = 0; i < matchList->nodeNr; i++) {
+ cur = matchList->nodeTab[i];
+ if (! IS_XSLT_REAL_NODE(cur))
+ continue;
+ ctxt->node = cur;
+ xpctxt->node = cur;
+ /*
+ * Process the 'use' of the xsl:key.
+ * SPEC XSLT 1.0:
+ * "The use attribute is an expression specifying the values of
+ * the key; the expression is evaluated once for each node that
+ * matches the pattern."
+ */
+ if (useRes != NULL)
+ xmlXPathFreeObject(useRes);
+ useRes = xmlXPathCompiledEval(keyDef->usecomp, xpctxt);
+ if (useRes == NULL) {
+ xsltTransformError(ctxt, NULL, keyDef->inst,
+ "Failed to evaluate the 'use' expression.\n");
+ ctxt->state = XSLT_STATE_STOPPED;
+ break;
+ }
+ if (useRes->type == XPATH_NODESET) {
+ if ((useRes->nodesetval != NULL) &&
+ (useRes->nodesetval->nodeNr != 0))
+ {
+ len = useRes->nodesetval->nodeNr;
+ str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[0]);
+ } else {
+ continue;
+ }
+ } else {
+ len = 1;
+ if (useRes->type == XPATH_STRING) {
+ /*
+ * Consume the string value.
+ */
+ str = useRes->stringval;
+ useRes->stringval = NULL;
+ } else {
+ str = xmlXPathCastToString(useRes);
+ }
+ }
+ /*
+ * Process all strings.
+ */
+ k = 0;
+ while (1) {
+ if (str == NULL)
+ goto next_string;
+
+#ifdef WITH_XSLT_DEBUG_KEYS
+ XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
+ "xsl:key : node associated to ('%s', '%s')\n", keyDef->name, str));
+#endif
+
+ keylist = xmlHashLookup(table->keys, str);
+ if (keylist == NULL) {
+ keylist = xmlXPathNodeSetCreate(cur);
+ if (keylist == NULL)
+ goto error;
+ xmlHashAddEntry(table->keys, str, keylist);
+ } else {
+ /*
+ * TODO: How do we know if this function failed?
+ */
+ xmlXPathNodeSetAdd(keylist, cur);
+ }
+ switch (cur->type) {
+ case XML_ELEMENT_NODE:
+ case XML_TEXT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_PI_NODE:
+ case XML_COMMENT_NODE:
+ cur->psvi = keyDef;
+ break;
+ case XML_ATTRIBUTE_NODE:
+ ((xmlAttrPtr) cur)->psvi = keyDef;
+ break;
+ case XML_DOCUMENT_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+ ((xmlDocPtr) cur)->psvi = keyDef;
+ break;
+ default:
+ break;
+ }
+ xmlFree(str);
+ str = NULL;
+
+next_string:
+ k++;
+ if (k >= len)
+ break;
+ str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[k]);
+ }
+ }
+
+exit:
+error:
+ ctxt->keyInitLevel--;
+ /*
+ * Restore context state.
+ */
+ xpctxt->doc = oldXPDoc;
+ xpctxt->nsNr = oldXPNsNr;
+ xpctxt->namespaces = oldXPNamespaces;
+ xpctxt->proximityPosition = oldXPPos;
+ xpctxt->contextSize = oldXPSize;
+
+ ctxt->node = oldContextNode;
+ ctxt->document = oldDocInfo;
+ ctxt->inst = oldInst;
+
+ if (str)
+ xmlFree(str);
+ if (useRes != NULL)
+ xmlXPathFreeObject(useRes);
+ if (matchRes != NULL)
+ xmlXPathFreeObject(matchRes);
+ return(0);
+}
+
+/**
+ * xsltInitCtxtKeys:
+ * @ctxt: an XSLT transformation context
+ * @idoc: a document info
+ *
+ * Computes all the keys tables for the current input document.
+ * Should be done before global varibales are initialized.
+ * NOTE: Not used anymore in the refactored code.
+ */
+void
+xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc) {
+ xsltStylesheetPtr style;
+ xsltKeyDefPtr keyDef;
+
+ if ((ctxt == NULL) || (idoc == NULL))
+ return;
+
+#ifdef KEY_INIT_DEBUG
+fprintf(stderr, "xsltInitCtxtKeys on document\n");
+#endif
+
+#ifdef WITH_XSLT_DEBUG_KEYS
+ if ((idoc->doc != NULL) && (idoc->doc->URL != NULL))
+ XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n",
+ idoc->doc->URL));
+#endif
+ style = ctxt->style;
+ while (style != NULL) {
+ keyDef = (xsltKeyDefPtr) style->keys;
+ while (keyDef != NULL) {
+ xsltInitCtxtKey(ctxt, idoc, keyDef);
+
+ keyDef = keyDef->next;
+ }
+
+ style = xsltNextImport(style);
+ }
+
+#ifdef KEY_INIT_DEBUG
+fprintf(stderr, "xsltInitCtxtKeys on document: done\n");
+#endif
+
+}
+
+/**
+ * xsltFreeDocumentKeys:
+ * @idoc: a XSLT document
+ *
+ * Free the keys associated to a document
+ */
+void
+xsltFreeDocumentKeys(xsltDocumentPtr idoc) {
+ if (idoc != NULL)
+ xsltFreeKeyTableList(idoc->keys);
+}
+
diff --git a/libxslt/keys.h b/libxslt/keys.h
new file mode 100644
index 0000000..757d122
--- /dev/null
+++ b/libxslt/keys.h
@@ -0,0 +1,53 @@
+/*
+ * Summary: interface for the key matching used in key() and template matches.
+ * Description: implementation of the key mechanims.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_KEY_H__
+#define __XML_XSLT_KEY_H__
+
+#include <libxml/xpath.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * NODE_IS_KEYED:
+ *
+ * check for bit 15 set
+ */
+#define NODE_IS_KEYED (1 >> 15)
+
+XSLTPUBFUN int XSLTCALL
+ xsltAddKey (xsltStylesheetPtr style,
+ const xmlChar *name,
+ const xmlChar *nameURI,
+ const xmlChar *match,
+ const xmlChar *use,
+ xmlNodePtr inst);
+XSLTPUBFUN xmlNodeSetPtr XSLTCALL
+ xsltGetKey (xsltTransformContextPtr ctxt,
+ const xmlChar *name,
+ const xmlChar *nameURI,
+ const xmlChar *value);
+XSLTPUBFUN void XSLTCALL
+ xsltInitCtxtKeys (xsltTransformContextPtr ctxt,
+ xsltDocumentPtr doc);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeKeys (xsltStylesheetPtr style);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeDocumentKeys (xsltDocumentPtr doc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_H__ */
+
diff --git a/libxslt/libxslt.3 b/libxslt/libxslt.3
new file mode 100644
index 0000000..06f256d
--- /dev/null
+++ b/libxslt/libxslt.3
@@ -0,0 +1,31 @@
+.TH libxslt 3 "30 August 2001"
+.SH NAME
+libxslt \- library used to do XSL transformations on XML documents
+.SH DESCRIPTION
+The
+.I libxslt
+library is used to do XSL transformations on XML documents that
+have been loaded into memory with functions from
+.I libxml.
+.LP
+.SH FILES
+.TP 2.2i
+.B /usr/lib/libxslt_1.0.0/libxslt.a
+static library
+.TP
+.B /usr/lib/libxslt_1.0.0/libxslt.so
+sharable library
+.TP
+.B /usr/package/libxslt_1.0.0/bin/xsltproc
+binary application to do XSL transformations on the command line
+.SH AUTHORS
+Daniel Veillard (daniel@veillard.com).
+If you download and install this package look at instructions on the
+Web site http://xmlsoft.org/XSLT/ .
+Manual page by Heiko W. Rupp (hwr@pilhuhn.de)
+.SH SEE ALSO
+.IR libexslt (3),
+.IR libxml (3),
+.IR xsltproc (1),
+.IR xmllint (1)
+.\" end of manual page
diff --git a/libxslt/libxslt.h b/libxslt/libxslt.h
new file mode 100644
index 0000000..e6d4c83
--- /dev/null
+++ b/libxslt/libxslt.h
@@ -0,0 +1,36 @@
+/*
+ * Summary: internal header only used during the compilation of libxslt
+ * Description: internal header only used during the compilation of libxslt
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XSLT_LIBXSLT_H__
+#define __XSLT_LIBXSLT_H__
+
+#if defined(WIN32) && !defined (__CYGWIN__) && !defined (__MINGW32__)
+#include <win32config.h>
+#else
+#include "config.h"
+#endif
+
+#include <libxslt/xsltconfig.h>
+#include <libxml/xmlversion.h>
+
+#if !defined LIBXSLT_PUBLIC
+#if (defined (__CYGWIN__) || defined _MSC_VER) && !defined IN_LIBXSLT && !defined LIBXSLT_STATIC
+#define LIBXSLT_PUBLIC __declspec(dllimport)
+#else
+#define LIBXSLT_PUBLIC
+#endif
+#endif
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#include <io.h>
+#include <direct.h>
+#define mkdir(p,m) _mkdir(p)
+#endif
+
+#endif /* ! __XSLT_LIBXSLT_H__ */
diff --git a/libxslt/libxslt.syms b/libxslt/libxslt.syms
new file mode 100644
index 0000000..3d9b5c6
--- /dev/null
+++ b/libxslt/libxslt.syms
@@ -0,0 +1,490 @@
+#
+# Officially exported symbols, for which header
+# file definitions are installed in /usr/include/libxslt
+#
+# Automatically generated from symbols.xml and syms.xsl
+#
+# Versions here are *fixed* to match the libxslt version
+# at which the symbol was introduced. This ensures that
+# a new client app requiring symbol foo() can't accidentally
+# run with old libxslt.so not providing foo() - the global
+# soname version info can't enforce this since we never
+# change the soname
+#
+
+LIBXML2_1.0.11 {
+ global:
+
+# attributes
+ xsltApplyAttributeSet;
+ xsltFreeAttributeSetsHashes;
+ xsltParseStylesheetAttributeSet;
+
+# documents
+ xsltFindDocument;
+ xsltFreeDocuments;
+ xsltFreeStyleDocuments;
+ xsltLoadDocument;
+ xsltLoadStyleDocument;
+ xsltNewDocument;
+ xsltNewStyleDocument;
+
+# extensions
+ xsltCheckExtPrefix;
+ xsltExtElementLookup;
+ xsltExtModuleElementLookup;
+ xsltExtModuleFunctionLookup;
+ xsltExtModuleTopLevelLookup;
+ xsltFreeCtxtExts;
+ xsltFreeExts;
+ xsltGetExtData;
+ xsltInitCtxtExts;
+ xsltInitElemPreComp;
+ xsltNewElemPreComp;
+ xsltPreComputeExtModuleElement;
+ xsltRegisterExtElement;
+ xsltRegisterExtFunction;
+ xsltRegisterExtModuleElement;
+ xsltRegisterExtModuleFull;
+ xsltRegisterExtModuleFunction;
+ xsltRegisterExtModule;
+ xsltRegisterExtModuleTopLevel;
+ xsltRegisterExtPrefix;
+ xsltRegisterTestModule;
+ xsltShutdownCtxtExts;
+ xsltShutdownExts;
+ xsltStyleGetExtData;
+ xsltUnregisterExtModuleElement;
+ xsltUnregisterExtModuleFunction;
+ xsltUnregisterExtModule;
+ xsltUnregisterExtModuleTopLevel;
+
+# extra
+ xsltDebug;
+ xsltFunctionNodeSet;
+ xsltRegisterAllExtras;
+ xsltRegisterExtras;
+
+# functions
+ xsltDocumentFunction;
+ xsltElementAvailableFunction;
+ xsltFormatNumberFunction;
+ xsltFunctionAvailableFunction;
+ xsltGenerateIdFunction;
+ xsltKeyFunction;
+ xsltRegisterAllFunctions;
+ xsltSystemPropertyFunction;
+ xsltUnparsedEntityURIFunction;
+ xsltXPathFunctionLookup;
+
+# imports
+ xsltFindElemSpaceHandling;
+ xsltFindTemplate;
+ xsltNeedElemSpaceHandling;
+ xsltNextImport;
+ xsltParseStylesheetImport;
+ xsltParseStylesheetInclude;
+
+# keys
+ xsltAddKey;
+ xsltFreeDocumentKeys;
+ xsltFreeKeys;
+ xsltGetKey;
+ xsltInitCtxtKeys;
+
+# namespaces
+ xsltCopyNamespaceList;
+ xsltCopyNamespace;
+ xsltFreeNamespaceAliasHashes;
+ xsltGetNamespace;
+ xsltGetSpecialNamespace;
+ xsltNamespaceAlias;
+
+# pattern
+ xsltAddTemplate;
+ xsltCleanupTemplates;
+ xsltCompilePattern;
+ xsltFreeCompMatchList;
+ xsltFreeTemplateHashes;
+ xsltGetTemplate;
+ xsltMatchPattern;
+ xsltTestCompMatchList;
+
+# preproc
+ xsltDocumentComp;
+ xsltFreeStylePreComps;
+ xsltStylePreCompute;
+
+# templates
+ xsltAttrListTemplateProcess;
+ xsltAttrTemplateProcess;
+ xsltAttrTemplateValueProcess;
+ xsltEvalAttrValueTemplate;
+ xsltEvalStaticAttrValueTemplate;
+ xsltEvalTemplateString;
+ xsltEvalXPathPredicate;
+ xsltEvalXPathString;
+ xsltTemplateProcess;
+
+# transform
+ xslHandleDebugger;
+ xsltApplyImports;
+ xsltApplyOneTemplate;
+ xsltApplyStripSpaces;
+ xsltApplyStylesheet;
+ xsltApplyStylesheetUser;
+ xsltApplyTemplates;
+ xsltAttribute;
+ xsltCallTemplate;
+ xsltChoose;
+ xsltComment;
+ xsltCopyOf;
+ xsltCopy;
+ xsltDocumentElem;
+ xsltElement;
+ xsltForEach;
+ xsltFreeTransformContext;
+ xsltGetXIncludeDefault;
+ xsltIf;
+ xsltNewTransformContext;
+ xsltNumber;
+ xsltProcessingInstruction;
+ xsltProfileStylesheet;
+ xsltRegisterAllElement;
+ xsltRunStylesheet;
+ xsltSetXIncludeDefault;
+ xsltSort;
+ xsltText;
+ xsltValueOf;
+
+# variables
+ xsltAddStackElemList;
+ xsltEvalGlobalVariables;
+ xsltEvalOneUserParam;
+ xsltEvalUserParams;
+ xsltFreeGlobalVariables;
+ xsltParseGlobalParam;
+ xsltParseGlobalVariable;
+ xsltParseStylesheetCallerParam;
+ xsltParseStylesheetParam;
+ xsltParseStylesheetVariable;
+ xsltQuoteOneUserParam;
+ xsltQuoteUserParams;
+ xsltVariableLookup;
+ xsltXPathVariableLookup;
+
+# xsltInternals
+ xsltDecimalFormatGetByName;
+ xsltFormatNumberConversion;
+ xsltFreeStackElemList;
+ xsltFreeStylesheet;
+ xsltIsBlank;
+ xsltLoadStylesheetPI;
+ xsltNewStylesheet;
+ xsltNumberFormat;
+ xsltParseStylesheetDoc;
+ xsltParseStylesheetFile;
+ xsltParseStylesheetOutput;
+ xsltParseStylesheetProcess;
+ xsltParseTemplateContent;
+
+# xsltutils
+ xslAddCall;
+ xslDropCall;
+ xsltCalibrateAdjust;
+ xsltDocumentSortFunction;
+ xsltDoSortFunction;
+ xsltGetNsProp;
+ xsltGetQNameURI;
+ xsltMessage;
+ xsltPrintErrorContext;
+ xsltSaveProfiling;
+ xsltSaveResultToFd;
+ xsltSaveResultToFilename;
+ xsltSaveResultToFile;
+ xsltSaveResultTo;
+ xsltSetDebuggerCallbacks;
+ xsltSetGenericDebugFunc;
+ xsltSetGenericErrorFunc;
+ xsltTimestamp;
+
+# xslt
+ xsltCleanupGlobals;
+} ;
+
+LIBXML2_1.0.12 {
+ global:
+
+# xsltInternals
+ xsltAllocateExtraCtxt;
+ xsltAllocateExtra;
+} LIBXML2_1.0.11;
+
+LIBXML2_1.0.13 {
+ global:
+
+# extensions
+ xsltExtModuleElementPreComputeLookup;
+ xsltXPathGetTransformContext;
+} LIBXML2_1.0.12;
+
+LIBXML2_1.0.16 {
+ global:
+
+# attributes
+ xsltResolveStylesheetAttributeSet;
+} LIBXML2_1.0.13;
+
+LIBXML2_1.0.17 {
+ global:
+
+# transform
+ xsltRunStylesheetUser;
+} LIBXML2_1.0.16;
+
+LIBXML2_1.0.18 {
+ global:
+
+# extensions
+ xsltDebugDumpExtensions;
+
+# xsltutils
+ xsltSaveResultToString;
+} LIBXML2_1.0.17;
+
+LIBXML2_1.0.22 {
+ global:
+
+# templates
+ xsltAttrTemplateValueProcessNode;
+
+# security
+ xsltCheckRead;
+ xsltCheckWrite;
+
+# templates
+ xsltEvalXPathStringNs;
+
+# security
+ xsltFreeSecurityPrefs;
+ xsltGetDefaultSecurityPrefs;
+ xsltGetSecurityPrefs;
+ xsltNewSecurityPrefs;
+ xsltSecurityAllow;
+ xsltSecurityForbid;
+ xsltSetCtxtSecurityPrefs;
+ xsltSetDefaultSecurityPrefs;
+ xsltSetSecurityPrefs;
+
+# xsltutils
+ xsltSetTransformErrorFunc;
+ xsltTransformError;
+} LIBXML2_1.0.18;
+
+LIBXML2_1.0.24 {
+ global:
+
+# xsltutils
+ xslDebugStatus; # variable
+ xsltComputeSortResult;
+ xsltDefaultSortFunction;
+
+# xslt
+ xsltEngineVersion; # variable
+
+# preproc
+ xsltExtMarker; # variable
+
+# xsltutils
+ xsltGenericDebugContext; # variable
+ xsltGenericDebug; # variable
+ xsltGenericErrorContext; # variable
+ xsltGenericError; # variable
+ xsltGetProfileInformation;
+ xsltGetUTF8Char;
+
+# xslt
+ xsltLibxmlVersion; # variable
+ xsltLibxsltVersion; # variable
+ xsltMaxDepth; # variable
+ xsltMaxVars; # variable
+
+# xsltInternals
+ xsltParseStylesheetImportedDoc;
+
+# xsltutils
+ xsltSetCtxtSortFunc;
+ xsltSetSortFunc;
+} LIBXML2_1.0.22;
+
+LIBXML2_1.0.30 {
+ global:
+
+# xsltInternals
+ xsltCreateRVT;
+ xsltFreeRVTs;
+ xsltRegisterPersistRVT;
+ xsltRegisterTmpRVT;
+} LIBXML2_1.0.24;
+
+LIBXML2_1.0.32 {
+ global:
+
+# transform
+ xsltCopyTextString;
+
+# extensions
+ xsltGetExtInfo;
+} LIBXML2_1.0.30;
+
+LIBXML2_1.0.33 {
+ global:
+
+# pattern
+ xsltNormalizeCompSteps;
+} LIBXML2_1.0.32;
+
+LIBXML2_1.1.0 {
+ global:
+
+# xsltutils
+ xsltGetDebuggerStatus;
+ xsltSetDebuggerStatus;
+} LIBXML2_1.0.33;
+
+LIBXML2_1.1.1 {
+ global:
+
+# xsltutils
+ xsltDebugGetDefaultTrace;
+ xsltDebugSetDefaultTrace;
+} LIBXML2_1.1.0;
+
+LIBXML2_1.1.2 {
+ global:
+
+# xsltutils
+ xsltSetCtxtParseOptions;
+} LIBXML2_1.1.1;
+
+LIBXML2_1.1.3 {
+ global:
+
+# xsltInternals
+ xsltCompileAttr;
+ xsltEvalAVT;
+ xsltFreeAVTList;
+
+# xsltutils
+ xsltGetCNsProp;
+ xsltSplitQName;
+ xsltXPathCompile;
+} LIBXML2_1.1.2;
+
+LIBXML2_1.1.5 {
+ global:
+
+# xsltutils
+ xsltGetQNameURI2;
+} LIBXML2_1.1.3;
+
+LIBXML2_1.1.7 {
+ global:
+
+# namespaces
+ xsltGetPlainNamespace;
+} LIBXML2_1.1.5;
+
+LIBXML2_1.1.9 {
+ global:
+
+# documents
+ xsltDocDefaultLoader; # variable
+ xsltSetLoaderFunc;
+} LIBXML2_1.1.7;
+
+LIBXML2_1.1.18 {
+ global:
+
+# xsltInternals
+ xsltConstNamespaceNameXSLT; # variable
+ xsltExtensionInstructionResultFinalize;
+ xsltExtensionInstructionResultRegister;
+ xsltInitCtxtKey;
+
+# xslt
+ xsltInit;
+
+# xsltInternals
+ xsltParseAnyXSLTElem;
+ xsltParseSequenceConstructor;
+ xsltPointerListAddSize;
+ xsltPointerListClear;
+ xsltPointerListCreate;
+ xsltPointerListFree;
+ xsltRegisterLocalRVT;
+ xsltReleaseRVT;
+ xsltRestoreDocumentNamespaces;
+
+# extensions
+ xsltStyleStylesheetLevelGetExtData;
+
+# xsltInternals
+# xsltTransStorageAdd; removed in 1.1.28
+# xsltTransStorageRemove; removed in 1.1.28
+ xsltUninit;
+ xsltXSLTAttrMarker; # variable
+} LIBXML2_1.1.9;
+
+LIBXML2_1.1.20 {
+ global:
+
+# transform
+ xsltLocalVariablePop;
+ xsltLocalVariablePush;
+} LIBXML2_1.1.18;
+
+LIBXML2_1.1.23 {
+ global:
+
+# xsltInternals
+ xsltInitAllDocKeys;
+} LIBXML2_1.1.20;
+
+LIBXML2_1.1.24 {
+ global:
+
+# extensions
+ xsltCheckExtURI;
+} LIBXML2_1.1.23;
+
+LIBXML2_1.1.25 {
+ global:
+
+# xsltlocale
+ xsltFreeLocale;
+ xsltLocaleStrcmp;
+ xsltNewLocale;
+ xsltStrxfrm;
+
+# extensions
+ xsltInitGlobals;
+} LIBXML2_1.1.24;
+
+LIBXML2_1.1.26 {
+ global:
+
+# transform
+ xsltProcessOneNode;
+} LIBXML2_1.1.25;
+
+LIBXML2_1.1.27 {
+ global:
+
+# xsltlocale
+ xsltFreeLocales;
+
+# xsltutils
+ xsltXPathCompileFlags;
+} LIBXML2_1.1.26;
+
diff --git a/libxslt/namespaces.c b/libxslt/namespaces.c
new file mode 100644
index 0000000..07a7705
--- /dev/null
+++ b/libxslt/namespaces.c
@@ -0,0 +1,853 @@
+/*
+ * namespaces.c: Implementation of the XSLT namespaces handling
+ *
+ * Reference:
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+#ifdef HAVE_NAN_H
+#include <nan.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#ifndef XSLT_NEED_TRIO
+#include <stdio.h>
+#else
+#include <trio.h>
+#endif
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/hash.h>
+#include <libxml/xmlerror.h>
+#include <libxml/uri.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "xsltutils.h"
+#include "namespaces.h"
+#include "imports.h"
+
+/************************************************************************
+ * *
+ * Module interfaces *
+ * *
+ ************************************************************************/
+
+#ifdef XSLT_REFACTORED
+static xsltNsAliasPtr
+xsltNewNsAlias(xsltCompilerCtxtPtr cctxt)
+{
+ xsltNsAliasPtr ret;
+
+ if (cctxt == NULL)
+ return(NULL);
+
+ ret = (xsltNsAliasPtr) xmlMalloc(sizeof(xsltNsAlias));
+ if (ret == NULL) {
+ xsltTransformError(NULL, cctxt->style, NULL,
+ "Internal error in xsltNewNsAlias(): Memory allocation failed.\n");
+ cctxt->style->errors++;
+ return(NULL);
+ }
+ memset(ret, 0, sizeof(xsltNsAlias));
+ /*
+ * TODO: Store the item at current stylesheet-level.
+ */
+ ret->next = cctxt->nsAliases;
+ cctxt->nsAliases = ret;
+
+ return(ret);
+}
+#endif /* XSLT_REFACTORED */
+/**
+ * xsltNamespaceAlias:
+ * @style: the XSLT stylesheet
+ * @node: the xsl:namespace-alias node
+ *
+ * Read the stylesheet-prefix and result-prefix attributes, register
+ * them as well as the corresponding namespace.
+ */
+void
+xsltNamespaceAlias(xsltStylesheetPtr style, xmlNodePtr node)
+{
+ xmlChar *resultPrefix = NULL;
+ xmlChar *stylePrefix = NULL;
+ xmlNsPtr literalNs = NULL;
+ xmlNsPtr targetNs = NULL;
+
+#ifdef XSLT_REFACTORED
+ xsltNsAliasPtr alias;
+
+ if ((style == NULL) || (node == NULL))
+ return;
+
+ /*
+ * SPEC XSLT 1.0:
+ * "If a namespace URI is declared to be an alias for multiple
+ * different namespace URIs, then the declaration with the highest
+ * import precedence is used. It is an error if there is more than
+ * one such declaration. An XSLT processor may signal the error;
+ * if it does not signal the error, it must recover by choosing,
+ * from amongst the declarations with the highest import precedence,
+ * the one that occurs last in the stylesheet."
+ *
+ * SPEC TODO: Check for the errors mentioned above.
+ */
+ /*
+ * NOTE that the XSLT 2.0 also *does* use the NULL namespace if
+ * "#default" is used and there's no default namespace is scope.
+ * I.e., this is *not* an error.
+ * Most XSLT 1.0 implementations work this way.
+ * The XSLT 1.0 spec has nothing to say on the subject.
+ */
+ /*
+ * Attribute "stylesheet-prefix".
+ */
+ stylePrefix = xmlGetNsProp(node, (const xmlChar *)"stylesheet-prefix", NULL);
+ if (stylePrefix == NULL) {
+ xsltTransformError(NULL, style, node,
+ "The attribute 'stylesheet-prefix' is missing.\n");
+ return;
+ }
+ if (xmlStrEqual(stylePrefix, (const xmlChar *)"#default"))
+ literalNs = xmlSearchNs(node->doc, node, NULL);
+ else {
+ literalNs = xmlSearchNs(node->doc, node, stylePrefix);
+ if (literalNs == NULL) {
+ xsltTransformError(NULL, style, node,
+ "Attribute 'stylesheet-prefix': There's no namespace "
+ "declaration in scope for the prefix '%s'.\n",
+ stylePrefix);
+ goto error;
+ }
+ }
+ /*
+ * Attribute "result-prefix".
+ */
+ resultPrefix = xmlGetNsProp(node, (const xmlChar *)"result-prefix", NULL);
+ if (resultPrefix == NULL) {
+ xsltTransformError(NULL, style, node,
+ "The attribute 'result-prefix' is missing.\n");
+ goto error;
+ }
+ if (xmlStrEqual(resultPrefix, (const xmlChar *)"#default"))
+ targetNs = xmlSearchNs(node->doc, node, NULL);
+ else {
+ targetNs = xmlSearchNs(node->doc, node, resultPrefix);
+
+ if (targetNs == NULL) {
+ xsltTransformError(NULL, style, node,
+ "Attribute 'result-prefix': There's no namespace "
+ "declaration in scope for the prefix '%s'.\n",
+ stylePrefix);
+ goto error;
+ }
+ }
+ /*
+ *
+ * Same alias for multiple different target namespace URIs:
+ * TODO: The one with the highest import precedence is used.
+ * Example:
+ * <xsl:namespace-alias stylesheet-prefix="foo"
+ * result-prefix="bar"/>
+ *
+ * <xsl:namespace-alias stylesheet-prefix="foo"
+ * result-prefix="zar"/>
+ *
+ * Same target namespace URI for multiple different aliases:
+ * All alias-definitions will be used.
+ * Example:
+ * <xsl:namespace-alias stylesheet-prefix="bar"
+ * result-prefix="foo"/>
+ *
+ * <xsl:namespace-alias stylesheet-prefix="zar"
+ * result-prefix="foo"/>
+ * Cases using #default:
+ * <xsl:namespace-alias stylesheet-prefix="#default"
+ * result-prefix="#default"/>
+ * TODO: Has this an effect at all?
+ *
+ * <xsl:namespace-alias stylesheet-prefix="foo"
+ * result-prefix="#default"/>
+ * From namespace to no namespace.
+ *
+ * <xsl:namespace-alias stylesheet-prefix="#default"
+ * result-prefix="foo"/>
+ * From no namespace to namespace.
+ */
+
+
+ /*
+ * Store the ns-node in the alias-object.
+ */
+ alias = xsltNewNsAlias(XSLT_CCTXT(style));
+ if (alias == NULL)
+ return;
+ alias->literalNs = literalNs;
+ alias->targetNs = targetNs;
+ XSLT_CCTXT(style)->hasNsAliases = 1;
+
+
+#else /* XSLT_REFACTORED */
+ const xmlChar *literalNsName;
+ const xmlChar *targetNsName;
+
+
+ if ((style == NULL) || (node == NULL))
+ return;
+
+ stylePrefix = xmlGetNsProp(node, (const xmlChar *)"stylesheet-prefix", NULL);
+ if (stylePrefix == NULL) {
+ xsltTransformError(NULL, style, node,
+ "namespace-alias: stylesheet-prefix attribute missing\n");
+ return;
+ }
+ resultPrefix = xmlGetNsProp(node, (const xmlChar *)"result-prefix", NULL);
+ if (resultPrefix == NULL) {
+ xsltTransformError(NULL, style, node,
+ "namespace-alias: result-prefix attribute missing\n");
+ goto error;
+ }
+
+ if (xmlStrEqual(stylePrefix, (const xmlChar *)"#default")) {
+ literalNs = xmlSearchNs(node->doc, node, NULL);
+ if (literalNs == NULL) {
+ literalNsName = NULL;
+ } else
+ literalNsName = literalNs->href; /* Yes - set for nsAlias table */
+ } else {
+ literalNs = xmlSearchNs(node->doc, node, stylePrefix);
+
+ if ((literalNs == NULL) || (literalNs->href == NULL)) {
+ xsltTransformError(NULL, style, node,
+ "namespace-alias: prefix %s not bound to any namespace\n",
+ stylePrefix);
+ goto error;
+ } else
+ literalNsName = literalNs->href;
+ }
+
+ /*
+ * When "#default" is used for result, if a default namespace has not
+ * been explicitly declared the special value UNDEFINED_DEFAULT_NS is
+ * put into the nsAliases table
+ */
+ if (xmlStrEqual(resultPrefix, (const xmlChar *)"#default")) {
+ targetNs = xmlSearchNs(node->doc, node, NULL);
+ if (targetNs == NULL) {
+ targetNsName = UNDEFINED_DEFAULT_NS;
+ } else
+ targetNsName = targetNs->href;
+ } else {
+ targetNs = xmlSearchNs(node->doc, node, resultPrefix);
+
+ if ((targetNs == NULL) || (targetNs->href == NULL)) {
+ xsltTransformError(NULL, style, node,
+ "namespace-alias: prefix %s not bound to any namespace\n",
+ resultPrefix);
+ goto error;
+ } else
+ targetNsName = targetNs->href;
+ }
+ /*
+ * Special case: if #default is used for
+ * the stylesheet-prefix (literal namespace) and there's no default
+ * namespace in scope, we'll use style->defaultAlias for this.
+ */
+ if (literalNsName == NULL) {
+ if (targetNs != NULL) {
+ /*
+ * BUG TODO: Is it not sufficient to have only 1 field for
+ * this, since subsequently alias declarations will
+ * overwrite this.
+ * Example:
+ * <xsl:namespace-alias result-prefix="foo"
+ * stylesheet-prefix="#default"/>
+ * <xsl:namespace-alias result-prefix="bar"
+ * stylesheet-prefix="#default"/>
+ * The mapping for "foo" won't be visible anymore.
+ */
+ style->defaultAlias = targetNs->href;
+ }
+ } else {
+ if (style->nsAliases == NULL)
+ style->nsAliases = xmlHashCreate(10);
+ if (style->nsAliases == NULL) {
+ xsltTransformError(NULL, style, node,
+ "namespace-alias: cannot create hash table\n");
+ goto error;
+ }
+ xmlHashAddEntry((xmlHashTablePtr) style->nsAliases,
+ literalNsName, (void *) targetNsName);
+ }
+#endif /* else of XSLT_REFACTORED */
+
+error:
+ if (stylePrefix != NULL)
+ xmlFree(stylePrefix);
+ if (resultPrefix != NULL)
+ xmlFree(resultPrefix);
+}
+
+/**
+ * xsltGetSpecialNamespace:
+ * @ctxt: the transformation context
+ * @invocNode: the invoking node; e.g. a literal result element/attr;
+ * only used for error reports
+ * @nsName: the namespace name (or NULL)
+ * @nsPrefix: the suggested namespace prefix (or NULL)
+ * @target: the result element on which to anchor a namespace
+ *
+ * Find a matching (prefix and ns-name) ns-declaration
+ * for the requested @nsName and @nsPrefix in the result tree.
+ * If none is found then a new ns-declaration will be
+ * added to @resultElem. If, in this case, the given prefix is
+ * already in use, then a ns-declaration with a modified ns-prefix
+ * be we created. Note that this function's priority is to
+ * preserve ns-prefixes; it will only change a prefix if there's
+ * a namespace clash.
+ * If both @nsName and @nsPrefix are NULL, then this will try to
+ * "undeclare" a default namespace by declaring an xmlns="".
+ *
+ * Returns a namespace declaration or NULL.
+ */
+xmlNsPtr
+xsltGetSpecialNamespace(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
+ const xmlChar *nsName, const xmlChar *nsPrefix,
+ xmlNodePtr target)
+{
+ xmlNsPtr ns;
+ int prefixOccupied = 0;
+
+ if ((ctxt == NULL) || (target == NULL) ||
+ (target->type != XML_ELEMENT_NODE))
+ return(NULL);
+
+ /*
+ * NOTE: Namespace exclusion and ns-aliasing is performed at
+ * compilation-time in the refactored code; so this need not be done
+ * here (it was in the old code).
+ * NOTE: @invocNode was named @cur in the old code and was documented to
+ * be an input node; since it was only used to anchor an error report
+ * somewhere, we can safely change this to @invocNode, which now
+ * will be the XSLT instruction (also a literal result element/attribute),
+ * which was responsible for this call.
+ */
+ /*
+ * OPTIMIZE TODO: This all could be optimized by keeping track of
+ * the ns-decls currently in-scope via a specialized context.
+ */
+ if ((nsPrefix == NULL) && ((nsName == NULL) || (nsName[0] == 0))) {
+ /*
+ * NOTE: the "undeclaration" of the default namespace was
+ * part of the logic of the old xsltGetSpecialNamespace() code,
+ * so we'll keep that mechanism.
+ * Related to the old code: bug #302020:
+ */
+ /*
+ * OPTIMIZE TODO: This all could be optimized by keeping track of
+ * the ns-decls currently in-scope via a specialized context.
+ */
+ /*
+ * Search on the result element itself.
+ */
+ if (target->nsDef != NULL) {
+ ns = target->nsDef;
+ do {
+ if (ns->prefix == NULL) {
+ if ((ns->href != NULL) && (ns->href[0] != 0)) {
+ /*
+ * Raise a namespace normalization error.
+ */
+ xsltTransformError(ctxt, NULL, invocNode,
+ "Namespace normalization error: Cannot undeclare "
+ "the default namespace, since the default namespace "
+ "'%s' is already declared on the result element "
+ "'%s'.\n", ns->href, target->name);
+ return(NULL);
+ } else {
+ /*
+ * The default namespace was undeclared on the
+ * result element.
+ */
+ return(NULL);
+ }
+ break;
+ }
+ ns = ns->next;
+ } while (ns != NULL);
+ }
+ if ((target->parent != NULL) &&
+ (target->parent->type == XML_ELEMENT_NODE))
+ {
+ /*
+ * The parent element is in no namespace, so assume
+ * that there is no default namespace in scope.
+ */
+ if (target->parent->ns == NULL)
+ return(NULL);
+
+ ns = xmlSearchNs(target->doc, target->parent,
+ NULL);
+ /*
+ * Fine if there's no default ns is scope, or if the
+ * default ns was undeclared.
+ */
+ if ((ns == NULL) || (ns->href == NULL) || (ns->href[0] == 0))
+ return(NULL);
+
+ /*
+ * Undeclare the default namespace.
+ */
+ xmlNewNs(target, BAD_CAST "", NULL);
+ /* TODO: Check result */
+ return(NULL);
+ }
+ return(NULL);
+ }
+ /*
+ * Handle the XML namespace.
+ * QUESTION: Is this faster than using xmlStrEqual() anyway?
+ */
+ if ((nsPrefix != NULL) &&
+ (nsPrefix[0] == 'x') && (nsPrefix[1] == 'm') &&
+ (nsPrefix[2] == 'l') && (nsPrefix[3] == 0))
+ {
+ return(xmlSearchNs(target->doc, target, nsPrefix));
+ }
+ /*
+ * First: search on the result element itself.
+ */
+ if (target->nsDef != NULL) {
+ ns = target->nsDef;
+ do {
+ if ((ns->prefix == NULL) == (nsPrefix == NULL)) {
+ if (ns->prefix == nsPrefix) {
+ if (xmlStrEqual(ns->href, nsName))
+ return(ns);
+ prefixOccupied = 1;
+ break;
+ } else if (xmlStrEqual(ns->prefix, nsPrefix)) {
+ if (xmlStrEqual(ns->href, nsName))
+ return(ns);
+ prefixOccupied = 1;
+ break;
+ }
+ }
+ ns = ns->next;
+ } while (ns != NULL);
+ }
+ if (prefixOccupied) {
+ /*
+ * If the ns-prefix is occupied by an other ns-decl on the
+ * result element, then this means:
+ * 1) The desired prefix is shadowed
+ * 2) There's no way around changing the prefix
+ *
+ * Try a desperate search for an in-scope ns-decl
+ * with a matching ns-name before we use the last option,
+ * which is to recreate the ns-decl with a modified prefix.
+ */
+ ns = xmlSearchNsByHref(target->doc, target, nsName);
+ if (ns != NULL)
+ return(ns);
+
+ /*
+ * Fallback to changing the prefix.
+ */
+ } else if ((target->parent != NULL) &&
+ (target->parent->type == XML_ELEMENT_NODE))
+ {
+ /*
+ * Try to find a matching ns-decl in the ancestor-axis.
+ *
+ * Check the common case: The parent element of the current
+ * result element is in the same namespace (with an equal ns-prefix).
+ */
+ if ((target->parent->ns != NULL) &&
+ ((target->parent->ns->prefix != NULL) == (nsPrefix != NULL)))
+ {
+ ns = target->parent->ns;
+
+ if (nsPrefix == NULL) {
+ if (xmlStrEqual(ns->href, nsName))
+ return(ns);
+ } else if (xmlStrEqual(ns->prefix, nsPrefix) &&
+ xmlStrEqual(ns->href, nsName))
+ {
+ return(ns);
+ }
+ }
+ /*
+ * Lookup the remaining in-scope namespaces.
+ */
+ ns = xmlSearchNs(target->doc, target->parent, nsPrefix);
+ if (ns != NULL) {
+ if (xmlStrEqual(ns->href, nsName))
+ return(ns);
+ /*
+ * Now check for a nasty case: We need to ensure that the new
+ * ns-decl won't shadow a prefix in-use by an existing attribute.
+ * <foo xmlns:a="urn:test:a">
+ * <bar a:a="val-a">
+ * <xsl:attribute xmlns:a="urn:test:b" name="a:b">
+ * val-b</xsl:attribute>
+ * </bar>
+ * </foo>
+ */
+ if (target->properties) {
+ xmlAttrPtr attr = target->properties;
+ do {
+ if ((attr->ns) &&
+ xmlStrEqual(attr->ns->prefix, nsPrefix))
+ {
+ /*
+ * Bad, this prefix is already in use.
+ * Since we'll change the prefix anyway, try
+ * a search for a matching ns-decl based on the
+ * namespace name.
+ */
+ ns = xmlSearchNsByHref(target->doc, target, nsName);
+ if (ns != NULL)
+ return(ns);
+ goto declare_new_prefix;
+ }
+ attr = attr->next;
+ } while (attr != NULL);
+ }
+ } else {
+ /*
+ * Either no matching ns-prefix was found or the namespace is
+ * shadowed.
+ * Create a new ns-decl on the current result element.
+ *
+ * Hmm, we could also try to reuse an in-scope
+ * namespace with a matching ns-name but a different
+ * ns-prefix.
+ * What has higher priority?
+ * 1) If keeping the prefix: create a new ns-decl.
+ * 2) If reusal: first lookup ns-names; then fallback
+ * to creation of a new ns-decl.
+ * REVISIT: this currently uses case 1) although
+ * the old way was use xmlSearchNsByHref() and to let change
+ * the prefix.
+ */
+#if 0
+ ns = xmlSearchNsByHref(target->doc, target, nsName);
+ if (ns != NULL)
+ return(ns);
+#endif
+ }
+ /*
+ * Create the ns-decl on the current result element.
+ */
+ ns = xmlNewNs(target, nsName, nsPrefix);
+ /* TODO: check errors */
+ return(ns);
+ } else {
+ /*
+ * This is either the root of the tree or something weird is going on.
+ */
+ ns = xmlNewNs(target, nsName, nsPrefix);
+ /* TODO: Check result */
+ return(ns);
+ }
+
+declare_new_prefix:
+ /*
+ * Fallback: we need to generate a new prefix and declare the namespace
+ * on the result element.
+ */
+ {
+ xmlChar pref[30];
+ int counter = 1;
+
+ if (nsPrefix == NULL) {
+ nsPrefix = BAD_CAST "ns";
+ }
+
+ do {
+ snprintf((char *) pref, 30, "%s_%d", nsPrefix, counter++);
+ ns = xmlSearchNs(target->doc, target, BAD_CAST pref);
+ if (counter > 1000) {
+ xsltTransformError(ctxt, NULL, invocNode,
+ "Internal error in xsltAcquireResultInScopeNs(): "
+ "Failed to compute a unique ns-prefix for the "
+ "generated element");
+ return(NULL);
+ }
+ } while (ns != NULL);
+ ns = xmlNewNs(target, nsName, BAD_CAST pref);
+ /* TODO: Check result */
+ return(ns);
+ }
+ return(NULL);
+}
+
+/**
+ * xsltGetNamespace:
+ * @ctxt: a transformation context
+ * @cur: the input node
+ * @ns: the namespace
+ * @out: the output node (or its parent)
+ *
+ * Find a matching (prefix and ns-name) ns-declaration
+ * for the requested @ns->prefix and @ns->href in the result tree.
+ * If none is found then a new ns-declaration will be
+ * added to @resultElem. If, in this case, the given prefix is
+ * already in use, then a ns-declaration with a modified ns-prefix
+ * be we created.
+ *
+ * Called by:
+ * - xsltCopyPropList() (*not* anymore)
+ * - xsltShallowCopyElement()
+ * - xsltCopyTreeInternal() (*not* anymore)
+ * - xsltApplySequenceConstructor() (*not* in the refactored code),
+ * - xsltElement() (*not* anymore)
+ *
+ * Returns a namespace declaration or NULL in case of
+ * namespace fixup failures or API or internal errors.
+ */
+xmlNsPtr
+xsltGetNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, xmlNsPtr ns,
+ xmlNodePtr out)
+{
+
+ if (ns == NULL)
+ return(NULL);
+
+#ifdef XSLT_REFACTORED
+ /*
+ * Namespace exclusion and ns-aliasing is performed at
+ * compilation-time in the refactored code.
+ * Additionally, aliasing is not intended for non Literal
+ * Result Elements.
+ */
+ return(xsltGetSpecialNamespace(ctxt, cur, ns->href, ns->prefix, out));
+#else
+ {
+ xsltStylesheetPtr style;
+ const xmlChar *URI = NULL; /* the replacement URI */
+
+ if ((ctxt == NULL) || (cur == NULL) || (out == NULL))
+ return(NULL);
+
+ style = ctxt->style;
+ while (style != NULL) {
+ if (style->nsAliases != NULL)
+ URI = (const xmlChar *)
+ xmlHashLookup(style->nsAliases, ns->href);
+ if (URI != NULL)
+ break;
+
+ style = xsltNextImport(style);
+ }
+
+
+ if (URI == UNDEFINED_DEFAULT_NS) {
+ return(xsltGetSpecialNamespace(ctxt, cur, NULL, NULL, out));
+#if 0
+ /*
+ * TODO: Removed, since wrong. If there was no default
+ * namespace in the stylesheet then this must resolve to
+ * the NULL namespace.
+ */
+ xmlNsPtr dflt;
+ dflt = xmlSearchNs(cur->doc, cur, NULL);
+ if (dflt != NULL)
+ URI = dflt->href;
+ else
+ return NULL;
+#endif
+ } else if (URI == NULL)
+ URI = ns->href;
+
+ return(xsltGetSpecialNamespace(ctxt, cur, URI, ns->prefix, out));
+ }
+#endif
+}
+
+/**
+ * xsltGetPlainNamespace:
+ * @ctxt: a transformation context
+ * @cur: the input node
+ * @ns: the namespace
+ * @out: the result element
+ *
+ * Obsolete.
+ * *Not* called by any Libxslt/Libexslt function.
+ * Exaclty the same as xsltGetNamespace().
+ *
+ * Returns a namespace declaration or NULL in case of
+ * namespace fixup failures or API or internal errors.
+ */
+xmlNsPtr
+xsltGetPlainNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur,
+ xmlNsPtr ns, xmlNodePtr out)
+{
+ return(xsltGetNamespace(ctxt, cur, ns, out));
+}
+
+/**
+ * xsltCopyNamespaceList:
+ * @ctxt: a transformation context
+ * @node: the target node
+ * @cur: the first namespace
+ *
+ * Do a copy of an namespace list. If @node is non-NULL the
+ * new namespaces are added automatically. This handles namespaces
+ * aliases.
+ * This function is intended only for *internal* use at
+ * transformation-time for copying ns-declarations of Literal
+ * Result Elements.
+ *
+ * Called by:
+ * xsltCopyTreeInternal() (transform.c)
+ * xsltShallowCopyElem() (transform.c)
+ *
+ * REVISIT: This function won't be used in the refactored code.
+ *
+ * Returns: a new xmlNsPtr, or NULL in case of error.
+ */
+xmlNsPtr
+xsltCopyNamespaceList(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNsPtr cur) {
+ xmlNsPtr ret = NULL, tmp;
+ xmlNsPtr p = NULL,q;
+
+ if (cur == NULL)
+ return(NULL);
+ if (cur->type != XML_NAMESPACE_DECL)
+ return(NULL);
+
+ /*
+ * One can add namespaces only on element nodes
+ */
+ if ((node != NULL) && (node->type != XML_ELEMENT_NODE))
+ node = NULL;
+
+ while (cur != NULL) {
+ if (cur->type != XML_NAMESPACE_DECL)
+ break;
+
+ /*
+ * Avoid duplicating namespace declarations in the tree if
+ * a matching declaration is in scope.
+ */
+ if (node != NULL) {
+ if ((node->ns != NULL) &&
+ (xmlStrEqual(node->ns->prefix, cur->prefix)) &&
+ (xmlStrEqual(node->ns->href, cur->href))) {
+ cur = cur->next;
+ continue;
+ }
+ tmp = xmlSearchNs(node->doc, node, cur->prefix);
+ if ((tmp != NULL) && (xmlStrEqual(tmp->href, cur->href))) {
+ cur = cur->next;
+ continue;
+ }
+ }
+#ifdef XSLT_REFACTORED
+ /*
+ * Namespace exclusion and ns-aliasing is performed at
+ * compilation-time in the refactored code.
+ */
+ q = xmlNewNs(node, cur->href, cur->prefix);
+ if (p == NULL) {
+ ret = p = q;
+ } else {
+ p->next = q;
+ p = q;
+ }
+#else
+ /*
+ * TODO: Remove this if the refactored code gets enabled.
+ */
+ if (!xmlStrEqual(cur->href, XSLT_NAMESPACE)) {
+ const xmlChar *URI;
+ /* TODO apply cascading */
+ URI = (const xmlChar *) xmlHashLookup(ctxt->style->nsAliases,
+ cur->href);
+ if (URI == UNDEFINED_DEFAULT_NS) {
+ cur = cur->next;
+ continue;
+ }
+ if (URI != NULL) {
+ q = xmlNewNs(node, URI, cur->prefix);
+ } else {
+ q = xmlNewNs(node, cur->href, cur->prefix);
+ }
+ if (p == NULL) {
+ ret = p = q;
+ } else {
+ p->next = q;
+ p = q;
+ }
+ }
+#endif
+ cur = cur->next;
+ }
+ return(ret);
+}
+
+/**
+ * xsltCopyNamespace:
+ * @ctxt: a transformation context
+ * @elem: the target element node
+ * @ns: the namespace node
+ *
+ * Copies a namespace node (declaration). If @elem is not NULL,
+ * then the new namespace will be declared on @elem.
+ *
+ * Returns: a new xmlNsPtr, or NULL in case of an error.
+ */
+xmlNsPtr
+xsltCopyNamespace(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
+ xmlNodePtr elem, xmlNsPtr ns)
+{
+ if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL))
+ return(NULL);
+ /*
+ * One can add namespaces only on element nodes
+ */
+ if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE))
+ return(xmlNewNs(NULL, ns->href, ns->prefix));
+ else
+ return(xmlNewNs(elem, ns->href, ns->prefix));
+}
+
+
+/**
+ * xsltFreeNamespaceAliasHashes:
+ * @style: an XSLT stylesheet
+ *
+ * Free up the memory used by namespaces aliases
+ */
+void
+xsltFreeNamespaceAliasHashes(xsltStylesheetPtr style) {
+ if (style->nsAliases != NULL)
+ xmlHashFree((xmlHashTablePtr) style->nsAliases, NULL);
+ style->nsAliases = NULL;
+}
diff --git a/libxslt/namespaces.h b/libxslt/namespaces.h
new file mode 100644
index 0000000..fa2d3b4
--- /dev/null
+++ b/libxslt/namespaces.h
@@ -0,0 +1,68 @@
+/*
+ * Summary: interface for the XSLT namespace handling
+ * Description: set of function easing the processing and generation
+ * of namespace nodes in XSLT.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_NAMESPACES_H__
+#define __XML_XSLT_NAMESPACES_H__
+
+#include <libxml/tree.h>
+#include "xsltexports.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Used within nsAliases hashtable when the default namespace is required
+ * but it's not been explicitly defined
+ */
+/**
+ * UNDEFINED_DEFAULT_NS:
+ *
+ * Special value for undefined namespace, internal
+ */
+#define UNDEFINED_DEFAULT_NS (const xmlChar *) -1L
+
+XSLTPUBFUN void XSLTCALL
+ xsltNamespaceAlias (xsltStylesheetPtr style,
+ xmlNodePtr node);
+XSLTPUBFUN xmlNsPtr XSLTCALL
+ xsltGetNamespace (xsltTransformContextPtr ctxt,
+ xmlNodePtr cur,
+ xmlNsPtr ns,
+ xmlNodePtr out);
+XSLTPUBFUN xmlNsPtr XSLTCALL
+ xsltGetPlainNamespace (xsltTransformContextPtr ctxt,
+ xmlNodePtr cur,
+ xmlNsPtr ns,
+ xmlNodePtr out);
+XSLTPUBFUN xmlNsPtr XSLTCALL
+ xsltGetSpecialNamespace (xsltTransformContextPtr ctxt,
+ xmlNodePtr cur,
+ const xmlChar *URI,
+ const xmlChar *prefix,
+ xmlNodePtr out);
+XSLTPUBFUN xmlNsPtr XSLTCALL
+ xsltCopyNamespace (xsltTransformContextPtr ctxt,
+ xmlNodePtr elem,
+ xmlNsPtr ns);
+XSLTPUBFUN xmlNsPtr XSLTCALL
+ xsltCopyNamespaceList (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNsPtr cur);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeNamespaceAliasHashes
+ (xsltStylesheetPtr style);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_NAMESPACES_H__ */
+
diff --git a/libxslt/numbers.c b/libxslt/numbers.c
new file mode 100644
index 0000000..e78c46b
--- /dev/null
+++ b/libxslt/numbers.c
@@ -0,0 +1,1361 @@
+/*
+ * numbers.c: Implementation of the XSLT number functions
+ *
+ * Reference:
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ * Bjorn Reese <breese@users.sourceforge.net>
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <math.h>
+#include <limits.h>
+#include <float.h>
+#include <string.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parserInternals.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include <libxml/encoding.h>
+#include "xsltutils.h"
+#include "pattern.h"
+#include "templates.h"
+#include "transform.h"
+#include "numbersInternals.h"
+
+#ifndef FALSE
+# define FALSE (0 == 1)
+# define TRUE (1 == 1)
+#endif
+
+#define SYMBOL_QUOTE ((xmlChar)'\'')
+
+#define DEFAULT_TOKEN (xmlChar)'0'
+#define DEFAULT_SEPARATOR "."
+
+#define MAX_TOKENS 1024
+
+typedef struct _xsltFormatToken xsltFormatToken;
+typedef xsltFormatToken *xsltFormatTokenPtr;
+struct _xsltFormatToken {
+ xmlChar *separator;
+ xmlChar token;
+ int width;
+};
+
+typedef struct _xsltFormat xsltFormat;
+typedef xsltFormat *xsltFormatPtr;
+struct _xsltFormat {
+ xmlChar *start;
+ xsltFormatToken tokens[MAX_TOKENS];
+ int nTokens;
+ xmlChar *end;
+};
+
+static char alpha_upper_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static char alpha_lower_list[] = "abcdefghijklmnopqrstuvwxyz";
+static xsltFormatToken default_token;
+
+/*
+ * **** Start temp insert ****
+ *
+ * The following two routines (xsltUTF8Size and xsltUTF8Charcmp)
+ * will be replaced with calls to the corresponding libxml routines
+ * at a later date (when other inter-library dependencies require it)
+ */
+
+/**
+ * xsltUTF8Size:
+ * @utf: pointer to the UTF8 character
+ *
+ * returns the numbers of bytes in the character, -1 on format error
+ */
+static int
+xsltUTF8Size(xmlChar *utf) {
+ xmlChar mask;
+ int len;
+
+ if (utf == NULL)
+ return -1;
+ if (*utf < 0x80)
+ return 1;
+ /* check valid UTF8 character */
+ if (!(*utf & 0x40))
+ return -1;
+ /* determine number of bytes in char */
+ len = 2;
+ for (mask=0x20; mask != 0; mask>>=1) {
+ if (!(*utf & mask))
+ return len;
+ len++;
+ }
+ return -1;
+}
+
+/**
+ * xsltUTF8Charcmp
+ * @utf1: pointer to first UTF8 char
+ * @utf2: pointer to second UTF8 char
+ *
+ * returns result of comparing the two UCS4 values
+ * as with xmlStrncmp
+ */
+static int
+xsltUTF8Charcmp(xmlChar *utf1, xmlChar *utf2) {
+
+ if (utf1 == NULL ) {
+ if (utf2 == NULL)
+ return 0;
+ return -1;
+ }
+ return xmlStrncmp(utf1, utf2, xsltUTF8Size(utf1));
+}
+
+/***** Stop temp insert *****/
+/************************************************************************
+ * *
+ * Utility functions *
+ * *
+ ************************************************************************/
+
+#define IS_SPECIAL(self,letter) \
+ ((xsltUTF8Charcmp((letter), (self)->zeroDigit) == 0) || \
+ (xsltUTF8Charcmp((letter), (self)->digit) == 0) || \
+ (xsltUTF8Charcmp((letter), (self)->decimalPoint) == 0) || \
+ (xsltUTF8Charcmp((letter), (self)->grouping) == 0) || \
+ (xsltUTF8Charcmp((letter), (self)->patternSeparator) == 0))
+
+#define IS_DIGIT_ZERO(x) xsltIsDigitZero(x)
+#define IS_DIGIT_ONE(x) xsltIsDigitZero((xmlChar)(x)-1)
+
+static int
+xsltIsDigitZero(unsigned int ch)
+{
+ /*
+ * Reference: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt
+ */
+ switch (ch) {
+ case 0x0030: case 0x0660: case 0x06F0: case 0x0966:
+ case 0x09E6: case 0x0A66: case 0x0AE6: case 0x0B66:
+ case 0x0C66: case 0x0CE6: case 0x0D66: case 0x0E50:
+ case 0x0E60: case 0x0F20: case 0x1040: case 0x17E0:
+ case 0x1810: case 0xFF10:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static void
+xsltNumberFormatDecimal(xmlBufferPtr buffer,
+ double number,
+ int digit_zero,
+ int width,
+ int digitsPerGroup,
+ int groupingCharacter,
+ int groupingCharacterLen)
+{
+ /*
+ * This used to be
+ * xmlChar temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 4];
+ * which would be length 68 on x86 arch. It was changed to be a longer,
+ * fixed length in order to try to cater for (reasonable) UTF8
+ * separators and numeric characters. The max UTF8 char size will be
+ * 6 or less, so the value used [500] should be *much* larger than needed
+ */
+ xmlChar temp_string[500];
+ xmlChar *pointer;
+ xmlChar temp_char[6];
+ int i;
+ int val;
+ int len;
+
+ /* Build buffer from back */
+ pointer = &temp_string[sizeof(temp_string)] - 1; /* last char */
+ *pointer = 0;
+ i = 0;
+ while (pointer > temp_string) {
+ if ((i >= width) && (fabs(number) < 1.0))
+ break; /* for */
+ if ((i > 0) && (groupingCharacter != 0) &&
+ (digitsPerGroup > 0) &&
+ ((i % digitsPerGroup) == 0)) {
+ if (pointer - groupingCharacterLen < temp_string) {
+ i = -1; /* flag error */
+ break;
+ }
+ pointer -= groupingCharacterLen;
+ xmlCopyCharMultiByte(pointer, groupingCharacter);
+ }
+
+ val = digit_zero + (int)fmod(number, 10.0);
+ if (val < 0x80) { /* shortcut if ASCII */
+ if (pointer <= temp_string) { /* Check enough room */
+ i = -1;
+ break;
+ }
+ *(--pointer) = val;
+ }
+ else {
+ /*
+ * Here we have a multibyte character. It's a little messy,
+ * because until we generate the char we don't know how long
+ * it is. So, we generate it into the buffer temp_char, then
+ * copy from there into temp_string.
+ */
+ len = xmlCopyCharMultiByte(temp_char, val);
+ if ( (pointer - len) < temp_string ) {
+ i = -1;
+ break;
+ }
+ pointer -= len;
+ memcpy(pointer, temp_char, len);
+ }
+ number /= 10.0;
+ ++i;
+ }
+ if (i < 0)
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltNumberFormatDecimal: Internal buffer size exceeded");
+ xmlBufferCat(buffer, pointer);
+}
+
+static void
+xsltNumberFormatAlpha(xsltNumberDataPtr data,
+ xmlBufferPtr buffer,
+ double number,
+ int is_upper)
+{
+ char temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 1];
+ char *pointer;
+ int i;
+ char *alpha_list;
+ double alpha_size = (double)(sizeof(alpha_upper_list) - 1);
+
+ /*
+ * XSLT 1.0 isn't clear on how to handle zero, but XSLT 2.0 says:
+ *
+ * For all format tokens other than the first kind above (one that
+ * consists of decimal digits), there may be implementation-defined
+ * lower and upper bounds on the range of numbers that can be
+ * formatted using this format token; indeed, for some numbering
+ * sequences there may be intrinsic limits. [...] Numbers that fall
+ * outside this range must be formatted using the format token 1.
+ *
+ * The "a" token has an intrinsic lower limit of 1.
+ */
+ if (number < 1.0) {
+ xsltNumberFormatDecimal(buffer, number, '0', 1,
+ data->digitsPerGroup,
+ data->groupingCharacter,
+ data->groupingCharacterLen);
+ return;
+ }
+
+ /* Build buffer from back */
+ pointer = &temp_string[sizeof(temp_string)];
+ *(--pointer) = 0;
+ alpha_list = (is_upper) ? alpha_upper_list : alpha_lower_list;
+
+ for (i = 1; i < (int)sizeof(temp_string); i++) {
+ number--;
+ *(--pointer) = alpha_list[((int)fmod(number, alpha_size))];
+ number /= alpha_size;
+ if (number < 1.0)
+ break; /* for */
+ }
+ xmlBufferCCat(buffer, pointer);
+}
+
+static void
+xsltNumberFormatRoman(xsltNumberDataPtr data,
+ xmlBufferPtr buffer,
+ double number,
+ int is_upper)
+{
+ /*
+ * See discussion in xsltNumberFormatAlpha. Also use a reasonable upper
+ * bound to avoid denial of service.
+ */
+ if (number < 1.0 || number > 5000.0) {
+ xsltNumberFormatDecimal(buffer, number, '0', 1,
+ data->digitsPerGroup,
+ data->groupingCharacter,
+ data->groupingCharacterLen);
+ return;
+ }
+
+ /*
+ * Based on an example by Jim Walsh
+ */
+ while (number >= 1000.0) {
+ xmlBufferCCat(buffer, (is_upper) ? "M" : "m");
+ number -= 1000.0;
+ }
+ if (number >= 900.0) {
+ xmlBufferCCat(buffer, (is_upper) ? "CM" : "cm");
+ number -= 900.0;
+ }
+ while (number >= 500.0) {
+ xmlBufferCCat(buffer, (is_upper) ? "D" : "d");
+ number -= 500.0;
+ }
+ if (number >= 400.0) {
+ xmlBufferCCat(buffer, (is_upper) ? "CD" : "cd");
+ number -= 400.0;
+ }
+ while (number >= 100.0) {
+ xmlBufferCCat(buffer, (is_upper) ? "C" : "c");
+ number -= 100.0;
+ }
+ if (number >= 90.0) {
+ xmlBufferCCat(buffer, (is_upper) ? "XC" : "xc");
+ number -= 90.0;
+ }
+ while (number >= 50.0) {
+ xmlBufferCCat(buffer, (is_upper) ? "L" : "l");
+ number -= 50.0;
+ }
+ if (number >= 40.0) {
+ xmlBufferCCat(buffer, (is_upper) ? "XL" : "xl");
+ number -= 40.0;
+ }
+ while (number >= 10.0) {
+ xmlBufferCCat(buffer, (is_upper) ? "X" : "x");
+ number -= 10.0;
+ }
+ if (number >= 9.0) {
+ xmlBufferCCat(buffer, (is_upper) ? "IX" : "ix");
+ number -= 9.0;
+ }
+ while (number >= 5.0) {
+ xmlBufferCCat(buffer, (is_upper) ? "V" : "v");
+ number -= 5.0;
+ }
+ if (number >= 4.0) {
+ xmlBufferCCat(buffer, (is_upper) ? "IV" : "iv");
+ number -= 4.0;
+ }
+ while (number >= 1.0) {
+ xmlBufferCCat(buffer, (is_upper) ? "I" : "i");
+ number--;
+ }
+}
+
+static void
+xsltNumberFormatTokenize(const xmlChar *format,
+ xsltFormatPtr tokens)
+{
+ int ix = 0;
+ int j;
+ int val;
+ int len;
+
+ default_token.token = DEFAULT_TOKEN;
+ default_token.width = 1;
+ default_token.separator = BAD_CAST(DEFAULT_SEPARATOR);
+
+
+ tokens->start = NULL;
+ tokens->tokens[0].separator = NULL;
+ tokens->end = NULL;
+
+ /*
+ * Insert initial non-alphanumeric token.
+ * There is always such a token in the list, even if NULL
+ */
+ while (! (IS_LETTER(val=xmlStringCurrentChar(NULL, format+ix, &len)) ||
+ IS_DIGIT(val)) ) {
+ if (format[ix] == 0) /* if end of format string */
+ break; /* while */
+ ix += len;
+ }
+ if (ix > 0)
+ tokens->start = xmlStrndup(format, ix);
+
+
+ for (tokens->nTokens = 0; tokens->nTokens < MAX_TOKENS;
+ tokens->nTokens++) {
+ if (format[ix] == 0)
+ break; /* for */
+
+ /*
+ * separator has already been parsed (except for the first
+ * number) in tokens->end, recover it.
+ */
+ if (tokens->nTokens > 0) {
+ tokens->tokens[tokens->nTokens].separator = tokens->end;
+ tokens->end = NULL;
+ }
+
+ val = xmlStringCurrentChar(NULL, format+ix, &len);
+ if (IS_DIGIT_ONE(val) ||
+ IS_DIGIT_ZERO(val)) {
+ tokens->tokens[tokens->nTokens].width = 1;
+ while (IS_DIGIT_ZERO(val)) {
+ tokens->tokens[tokens->nTokens].width++;
+ ix += len;
+ val = xmlStringCurrentChar(NULL, format+ix, &len);
+ }
+ if (IS_DIGIT_ONE(val)) {
+ tokens->tokens[tokens->nTokens].token = val - 1;
+ ix += len;
+ val = xmlStringCurrentChar(NULL, format+ix, &len);
+ }
+ } else if ( (val == (xmlChar)'A') ||
+ (val == (xmlChar)'a') ||
+ (val == (xmlChar)'I') ||
+ (val == (xmlChar)'i') ) {
+ tokens->tokens[tokens->nTokens].token = val;
+ ix += len;
+ val = xmlStringCurrentChar(NULL, format+ix, &len);
+ } else {
+ /* XSLT section 7.7
+ * "Any other format token indicates a numbering sequence
+ * that starts with that token. If an implementation does
+ * not support a numbering sequence that starts with that
+ * token, it must use a format token of 1."
+ */
+ tokens->tokens[tokens->nTokens].token = (xmlChar)'0';
+ tokens->tokens[tokens->nTokens].width = 1;
+ }
+ /*
+ * Skip over remaining alphanumeric characters from the Nd
+ * (Number, decimal digit), Nl (Number, letter), No (Number,
+ * other), Lu (Letter, uppercase), Ll (Letter, lowercase), Lt
+ * (Letters, titlecase), Lm (Letters, modifiers), and Lo
+ * (Letters, other (uncased)) Unicode categories. This happens
+ * to correspond to the Letter and Digit classes from XML (and
+ * one wonders why XSLT doesn't refer to these instead).
+ */
+ while (IS_LETTER(val) || IS_DIGIT(val)) {
+ ix += len;
+ val = xmlStringCurrentChar(NULL, format+ix, &len);
+ }
+
+ /*
+ * Insert temporary non-alphanumeric final tooken.
+ */
+ j = ix;
+ while (! (IS_LETTER(val) || IS_DIGIT(val))) {
+ if (val == 0)
+ break; /* while */
+ ix += len;
+ val = xmlStringCurrentChar(NULL, format+ix, &len);
+ }
+ if (ix > j)
+ tokens->end = xmlStrndup(&format[j], ix - j);
+ }
+}
+
+static void
+xsltNumberFormatInsertNumbers(xsltNumberDataPtr data,
+ double *numbers,
+ int numbers_max,
+ xsltFormatPtr tokens,
+ xmlBufferPtr buffer)
+{
+ int i = 0;
+ double number;
+ xsltFormatTokenPtr token;
+
+ /*
+ * Handle initial non-alphanumeric token
+ */
+ if (tokens->start != NULL)
+ xmlBufferCat(buffer, tokens->start);
+
+ for (i = 0; i < numbers_max; i++) {
+ /* Insert number */
+ number = numbers[(numbers_max - 1) - i];
+ /* Round to nearest like XSLT 2.0 */
+ number = floor(number + 0.5);
+ /*
+ * XSLT 1.0 isn't clear on how to handle negative numbers, but XSLT
+ * 2.0 says:
+ *
+ * It is a non-recoverable dynamic error if any undiscarded item
+ * in the atomized sequence supplied as the value of the value
+ * attribute of xsl:number cannot be converted to an integer, or
+ * if the resulting integer is less than 0 (zero).
+ */
+ if (number < 0.0) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsl-number : negative value\n");
+ /* Recover by treating negative values as zero. */
+ number = 0.0;
+ }
+ if (i < tokens->nTokens) {
+ /*
+ * The "n"th format token will be used to format the "n"th
+ * number in the list
+ */
+ token = &(tokens->tokens[i]);
+ } else if (tokens->nTokens > 0) {
+ /*
+ * If there are more numbers than format tokens, then the
+ * last format token will be used to format the remaining
+ * numbers.
+ */
+ token = &(tokens->tokens[tokens->nTokens - 1]);
+ } else {
+ /*
+ * If there are no format tokens, then a format token of
+ * 1 is used to format all numbers.
+ */
+ token = &default_token;
+ }
+
+ /* Print separator, except for the first number */
+ if (i > 0) {
+ if (token->separator != NULL)
+ xmlBufferCat(buffer, token->separator);
+ else
+ xmlBufferCCat(buffer, DEFAULT_SEPARATOR);
+ }
+
+ switch (xmlXPathIsInf(number)) {
+ case -1:
+ xmlBufferCCat(buffer, "-Infinity");
+ break;
+ case 1:
+ xmlBufferCCat(buffer, "Infinity");
+ break;
+ default:
+ if (xmlXPathIsNaN(number)) {
+ xmlBufferCCat(buffer, "NaN");
+ } else {
+
+ switch (token->token) {
+ case 'A':
+ xsltNumberFormatAlpha(data, buffer, number, TRUE);
+ break;
+ case 'a':
+ xsltNumberFormatAlpha(data, buffer, number, FALSE);
+ break;
+ case 'I':
+ xsltNumberFormatRoman(data, buffer, number, TRUE);
+ break;
+ case 'i':
+ xsltNumberFormatRoman(data, buffer, number, FALSE);
+ break;
+ default:
+ if (IS_DIGIT_ZERO(token->token)) {
+ xsltNumberFormatDecimal(buffer,
+ number,
+ token->token,
+ token->width,
+ data->digitsPerGroup,
+ data->groupingCharacter,
+ data->groupingCharacterLen);
+ }
+ break;
+ }
+ }
+
+ }
+ }
+
+ /*
+ * Handle final non-alphanumeric token
+ */
+ if (tokens->end != NULL)
+ xmlBufferCat(buffer, tokens->end);
+
+}
+
+static int
+xsltTestCompMatchCount(xsltTransformContextPtr context,
+ xmlNodePtr node,
+ xsltCompMatchPtr countPat,
+ xmlNodePtr cur)
+{
+ if (countPat != NULL) {
+ return xsltTestCompMatchList(context, node, countPat);
+ }
+ else {
+ /*
+ * 7.7 Numbering
+ *
+ * If count attribute is not specified, then it defaults to the
+ * pattern that matches any node with the same node type as the
+ * current node and, if the current node has an expanded-name, with
+ * the same expanded-name as the current node.
+ */
+ if (node->type != cur->type)
+ return 0;
+ if (node->type == XML_NAMESPACE_DECL)
+ /*
+ * Namespace nodes have no preceding siblings and no parents
+ * that are namespace nodes. This means that node == cur.
+ */
+ return 1;
+ /* TODO: Skip node types without expanded names like text nodes. */
+ if (!xmlStrEqual(node->name, cur->name))
+ return 0;
+ if (node->ns == cur->ns)
+ return 1;
+ if ((node->ns == NULL) || (cur->ns == NULL))
+ return 0;
+ return (xmlStrEqual(node->ns->href, cur->ns->href));
+ }
+}
+
+static int
+xsltNumberFormatGetAnyLevel(xsltTransformContextPtr context,
+ xmlNodePtr node,
+ xsltCompMatchPtr countPat,
+ xsltCompMatchPtr fromPat,
+ double *array)
+{
+ int amount = 0;
+ int cnt = 0;
+ xmlNodePtr cur = node;
+
+ while (cur != NULL) {
+ /* process current node */
+ if (xsltTestCompMatchCount(context, cur, countPat, node))
+ cnt++;
+ if ((fromPat != NULL) &&
+ xsltTestCompMatchList(context, cur, fromPat)) {
+ break; /* while */
+ }
+
+ /* Skip to next preceding or ancestor */
+ if ((cur->type == XML_DOCUMENT_NODE) ||
+#ifdef LIBXML_DOCB_ENABLED
+ (cur->type == XML_DOCB_DOCUMENT_NODE) ||
+#endif
+ (cur->type == XML_HTML_DOCUMENT_NODE))
+ break; /* while */
+
+ if (cur->type == XML_NAMESPACE_DECL) {
+ /*
+ * The XPath module stores the parent of a namespace node in
+ * the ns->next field.
+ */
+ cur = (xmlNodePtr) ((xmlNsPtr) cur)->next;
+ } else if (cur->type == XML_ATTRIBUTE_NODE) {
+ cur = cur->parent;
+ } else {
+ while ((cur->prev != NULL) && ((cur->prev->type == XML_DTD_NODE) ||
+ (cur->prev->type == XML_XINCLUDE_START) ||
+ (cur->prev->type == XML_XINCLUDE_END)))
+ cur = cur->prev;
+ if (cur->prev != NULL) {
+ for (cur = cur->prev; cur->last != NULL; cur = cur->last);
+ } else {
+ cur = cur->parent;
+ }
+ }
+ }
+
+ array[amount++] = (double) cnt;
+
+ return(amount);
+}
+
+static int
+xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context,
+ xmlNodePtr node,
+ xsltCompMatchPtr countPat,
+ xsltCompMatchPtr fromPat,
+ double *array,
+ int max)
+{
+ int amount = 0;
+ int cnt;
+ xmlNodePtr ancestor;
+ xmlNodePtr preceding;
+ xmlXPathParserContextPtr parser;
+
+ context->xpathCtxt->node = node;
+ parser = xmlXPathNewParserContext(NULL, context->xpathCtxt);
+ if (parser) {
+ /* ancestor-or-self::*[count] */
+ for (ancestor = node;
+ (ancestor != NULL) && (ancestor->type != XML_DOCUMENT_NODE);
+ ancestor = xmlXPathNextAncestor(parser, ancestor)) {
+
+ if ((fromPat != NULL) &&
+ xsltTestCompMatchList(context, ancestor, fromPat))
+ break; /* for */
+
+ if (xsltTestCompMatchCount(context, ancestor, countPat, node)) {
+ /* count(preceding-sibling::*) */
+ cnt = 1;
+ for (preceding =
+ xmlXPathNextPrecedingSibling(parser, ancestor);
+ preceding != NULL;
+ preceding =
+ xmlXPathNextPrecedingSibling(parser, preceding)) {
+
+ if (xsltTestCompMatchCount(context, preceding, countPat,
+ node))
+ cnt++;
+ }
+ array[amount++] = (double)cnt;
+ if (amount >= max)
+ break; /* for */
+ }
+ }
+ xmlXPathFreeParserContext(parser);
+ }
+ return amount;
+}
+
+static int
+xsltNumberFormatGetValue(xmlXPathContextPtr context,
+ xmlNodePtr node,
+ const xmlChar *value,
+ double *number)
+{
+ int amount = 0;
+ xmlBufferPtr pattern;
+ xmlXPathObjectPtr obj;
+
+ pattern = xmlBufferCreate();
+ if (pattern != NULL) {
+ xmlBufferCCat(pattern, "number(");
+ xmlBufferCat(pattern, value);
+ xmlBufferCCat(pattern, ")");
+ context->node = node;
+ obj = xmlXPathEvalExpression(xmlBufferContent(pattern),
+ context);
+ if (obj != NULL) {
+ *number = obj->floatval;
+ amount++;
+ xmlXPathFreeObject(obj);
+ }
+ xmlBufferFree(pattern);
+ }
+ return amount;
+}
+
+/**
+ * xsltNumberFormat:
+ * @ctxt: the XSLT transformation context
+ * @data: the formatting informations
+ * @node: the data to format
+ *
+ * Convert one number.
+ */
+void
+xsltNumberFormat(xsltTransformContextPtr ctxt,
+ xsltNumberDataPtr data,
+ xmlNodePtr node)
+{
+ xmlBufferPtr output = NULL;
+ int amount, i;
+ double number;
+ xsltFormat tokens;
+
+ if (data->format != NULL) {
+ xsltNumberFormatTokenize(data->format, &tokens);
+ }
+ else {
+ xmlChar *format;
+
+ /* The format needs to be recomputed each time */
+ if (data->has_format == 0)
+ return;
+ format = xsltEvalAttrValueTemplate(ctxt, data->node,
+ (const xmlChar *) "format",
+ XSLT_NAMESPACE);
+ if (format == NULL)
+ return;
+ xsltNumberFormatTokenize(format, &tokens);
+ xmlFree(format);
+ }
+
+ output = xmlBufferCreate();
+ if (output == NULL)
+ goto XSLT_NUMBER_FORMAT_END;
+
+ /*
+ * Evaluate the XPath expression to find the value(s)
+ */
+ if (data->value) {
+ amount = xsltNumberFormatGetValue(ctxt->xpathCtxt,
+ node,
+ data->value,
+ &number);
+ if (amount == 1) {
+ xsltNumberFormatInsertNumbers(data,
+ &number,
+ 1,
+ &tokens,
+ output);
+ }
+
+ } else if (data->level) {
+
+ if (xmlStrEqual(data->level, (const xmlChar *) "single")) {
+ amount = xsltNumberFormatGetMultipleLevel(ctxt,
+ node,
+ data->countPat,
+ data->fromPat,
+ &number,
+ 1);
+ if (amount == 1) {
+ xsltNumberFormatInsertNumbers(data,
+ &number,
+ 1,
+ &tokens,
+ output);
+ }
+ } else if (xmlStrEqual(data->level, (const xmlChar *) "multiple")) {
+ double numarray[1024];
+ int max = sizeof(numarray)/sizeof(numarray[0]);
+ amount = xsltNumberFormatGetMultipleLevel(ctxt,
+ node,
+ data->countPat,
+ data->fromPat,
+ numarray,
+ max);
+ if (amount > 0) {
+ xsltNumberFormatInsertNumbers(data,
+ numarray,
+ amount,
+ &tokens,
+ output);
+ }
+ } else if (xmlStrEqual(data->level, (const xmlChar *) "any")) {
+ amount = xsltNumberFormatGetAnyLevel(ctxt,
+ node,
+ data->countPat,
+ data->fromPat,
+ &number);
+ if (amount > 0) {
+ xsltNumberFormatInsertNumbers(data,
+ &number,
+ 1,
+ &tokens,
+ output);
+ }
+ }
+ }
+ /* Insert number as text node */
+ xsltCopyTextString(ctxt, ctxt->insert, xmlBufferContent(output), 0);
+
+ xmlBufferFree(output);
+
+XSLT_NUMBER_FORMAT_END:
+ if (tokens.start != NULL)
+ xmlFree(tokens.start);
+ if (tokens.end != NULL)
+ xmlFree(tokens.end);
+ for (i = 0;i < tokens.nTokens;i++) {
+ if (tokens.tokens[i].separator != NULL)
+ xmlFree(tokens.tokens[i].separator);
+ }
+}
+
+static int
+xsltFormatNumberPreSuffix(xsltDecimalFormatPtr self, xmlChar **format, xsltFormatNumberInfoPtr info)
+{
+ int count=0; /* will hold total length of prefix/suffix */
+ int len;
+
+ while (1) {
+ /*
+ * prefix / suffix ends at end of string or at
+ * first 'special' character
+ */
+ if (**format == 0)
+ return count;
+ /* if next character 'escaped' just count it */
+ if (**format == SYMBOL_QUOTE) {
+ if (*++(*format) == 0)
+ return -1;
+ }
+ else if (IS_SPECIAL(self, *format))
+ return count;
+ /*
+ * else treat percent/per-mille as special cases,
+ * depending on whether +ve or -ve
+ */
+ else {
+ /*
+ * for +ve prefix/suffix, allow only a
+ * single occurence of either
+ */
+ if (xsltUTF8Charcmp(*format, self->percent) == 0) {
+ if (info->is_multiplier_set)
+ return -1;
+ info->multiplier = 100;
+ info->is_multiplier_set = TRUE;
+ } else if (xsltUTF8Charcmp(*format, self->permille) == 0) {
+ if (info->is_multiplier_set)
+ return -1;
+ info->multiplier = 1000;
+ info->is_multiplier_set = TRUE;
+ }
+ }
+
+ if ((len=xsltUTF8Size(*format)) < 1)
+ return -1;
+ count += len;
+ *format += len;
+ }
+}
+
+/**
+ * xsltFormatNumberConversion:
+ * @self: the decimal format
+ * @format: the format requested
+ * @number: the value to format
+ * @result: the place to ouput the result
+ *
+ * format-number() uses the JDK 1.1 DecimalFormat class:
+ *
+ * http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html
+ *
+ * Structure:
+ *
+ * pattern := subpattern{;subpattern}
+ * subpattern := {prefix}integer{.fraction}{suffix}
+ * prefix := '\\u0000'..'\\uFFFD' - specialCharacters
+ * suffix := '\\u0000'..'\\uFFFD' - specialCharacters
+ * integer := '#'* '0'* '0'
+ * fraction := '0'* '#'*
+ *
+ * Notation:
+ * X* 0 or more instances of X
+ * (X | Y) either X or Y.
+ * X..Y any character from X up to Y, inclusive.
+ * S - T characters in S, except those in T
+ *
+ * Special Characters:
+ *
+ * Symbol Meaning
+ * 0 a digit
+ * # a digit, zero shows as absent
+ * . placeholder for decimal separator
+ * , placeholder for grouping separator.
+ * ; separates formats.
+ * - default negative prefix.
+ * % multiply by 100 and show as percentage
+ * ? multiply by 1000 and show as per mille
+ * X any other characters can be used in the prefix or suffix
+ * ' used to quote special characters in a prefix or suffix.
+ *
+ * Returns a possible XPath error
+ */
+xmlXPathError
+xsltFormatNumberConversion(xsltDecimalFormatPtr self,
+ xmlChar *format,
+ double number,
+ xmlChar **result)
+{
+ xmlXPathError status = XPATH_EXPRESSION_OK;
+ xmlBufferPtr buffer;
+ xmlChar *the_format, *prefix = NULL, *suffix = NULL;
+ xmlChar *nprefix, *nsuffix = NULL;
+ xmlChar pchar;
+ int prefix_length, suffix_length = 0, nprefix_length, nsuffix_length;
+ double scale;
+ int j, len;
+ int self_grouping_len;
+ xsltFormatNumberInfo format_info;
+ /*
+ * delayed_multiplier allows a 'trailing' percent or
+ * permille to be treated as suffix
+ */
+ int delayed_multiplier = 0;
+ /* flag to show no -ve format present for -ve number */
+ char default_sign = 0;
+ /* flag to show error found, should use default format */
+ char found_error = 0;
+
+ if (xmlStrlen(format) <= 0) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltFormatNumberConversion : "
+ "Invalid format (0-length)\n");
+ }
+ *result = NULL;
+ switch (xmlXPathIsInf(number)) {
+ case -1:
+ if (self->minusSign == NULL)
+ *result = xmlStrdup(BAD_CAST "-");
+ else
+ *result = xmlStrdup(self->minusSign);
+ /* no-break on purpose */
+ case 1:
+ if ((self == NULL) || (self->infinity == NULL))
+ *result = xmlStrcat(*result, BAD_CAST "Infinity");
+ else
+ *result = xmlStrcat(*result, self->infinity);
+ return(status);
+ default:
+ if (xmlXPathIsNaN(number)) {
+ if ((self == NULL) || (self->noNumber == NULL))
+ *result = xmlStrdup(BAD_CAST "NaN");
+ else
+ *result = xmlStrdup(self->noNumber);
+ return(status);
+ }
+ }
+
+ buffer = xmlBufferCreate();
+ if (buffer == NULL) {
+ return XPATH_MEMORY_ERROR;
+ }
+
+ format_info.integer_hash = 0;
+ format_info.integer_digits = 0;
+ format_info.frac_digits = 0;
+ format_info.frac_hash = 0;
+ format_info.group = -1;
+ format_info.multiplier = 1;
+ format_info.add_decimal = FALSE;
+ format_info.is_multiplier_set = FALSE;
+ format_info.is_negative_pattern = FALSE;
+
+ the_format = format;
+
+ /*
+ * First we process the +ve pattern to get percent / permille,
+ * as well as main format
+ */
+ prefix = the_format;
+ prefix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info);
+ if (prefix_length < 0) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+
+ /*
+ * Here we process the "number" part of the format. It gets
+ * a little messy because of the percent/per-mille - if that
+ * appears at the end, it may be part of the suffix instead
+ * of part of the number, so the variable delayed_multiplier
+ * is used to handle it
+ */
+ self_grouping_len = xmlStrlen(self->grouping);
+ while ((*the_format != 0) &&
+ (xsltUTF8Charcmp(the_format, self->decimalPoint) != 0) &&
+ (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) {
+
+ if (delayed_multiplier != 0) {
+ format_info.multiplier = delayed_multiplier;
+ format_info.is_multiplier_set = TRUE;
+ delayed_multiplier = 0;
+ }
+ if (xsltUTF8Charcmp(the_format, self->digit) == 0) {
+ if (format_info.integer_digits > 0) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+ format_info.integer_hash++;
+ if (format_info.group >= 0)
+ format_info.group++;
+ } else if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) {
+ format_info.integer_digits++;
+ if (format_info.group >= 0)
+ format_info.group++;
+ } else if ((self_grouping_len > 0) &&
+ (!xmlStrncmp(the_format, self->grouping, self_grouping_len))) {
+ /* Reset group count */
+ format_info.group = 0;
+ the_format += self_grouping_len;
+ continue;
+ } else if (xsltUTF8Charcmp(the_format, self->percent) == 0) {
+ if (format_info.is_multiplier_set) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+ delayed_multiplier = 100;
+ } else if (xsltUTF8Charcmp(the_format, self->permille) == 0) {
+ if (format_info.is_multiplier_set) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+ delayed_multiplier = 1000;
+ } else
+ break; /* while */
+
+ if ((len=xsltUTF8Size(the_format)) < 1) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+ the_format += len;
+
+ }
+
+ /* We have finished the integer part, now work on fraction */
+ if ( (*the_format != 0) &&
+ (xsltUTF8Charcmp(the_format, self->decimalPoint) == 0) ) {
+ format_info.add_decimal = TRUE;
+ the_format += xsltUTF8Size(the_format); /* Skip over the decimal */
+ }
+
+ while (*the_format != 0) {
+
+ if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) {
+ if (format_info.frac_hash != 0) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+ format_info.frac_digits++;
+ } else if (xsltUTF8Charcmp(the_format, self->digit) == 0) {
+ format_info.frac_hash++;
+ } else if (xsltUTF8Charcmp(the_format, self->percent) == 0) {
+ if (format_info.is_multiplier_set) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+ delayed_multiplier = 100;
+ if ((len = xsltUTF8Size(the_format)) < 1) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+ the_format += len;
+ continue; /* while */
+ } else if (xsltUTF8Charcmp(the_format, self->permille) == 0) {
+ if (format_info.is_multiplier_set) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+ delayed_multiplier = 1000;
+ if ((len = xsltUTF8Size(the_format)) < 1) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+ the_format += len;
+ continue; /* while */
+ } else if (xsltUTF8Charcmp(the_format, self->grouping) != 0) {
+ break; /* while */
+ }
+ if ((len = xsltUTF8Size(the_format)) < 1) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+ the_format += len;
+ if (delayed_multiplier != 0) {
+ format_info.multiplier = delayed_multiplier;
+ delayed_multiplier = 0;
+ format_info.is_multiplier_set = TRUE;
+ }
+ }
+
+ /*
+ * If delayed_multiplier is set after processing the
+ * "number" part, should be in suffix
+ */
+ if (delayed_multiplier != 0) {
+ the_format -= len;
+ delayed_multiplier = 0;
+ }
+
+ suffix = the_format;
+ suffix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info);
+ if ( (suffix_length < 0) ||
+ ((*the_format != 0) &&
+ (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) ) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+
+ /*
+ * We have processed the +ve prefix, number part and +ve suffix.
+ * If the number is -ve, we must substitute the -ve prefix / suffix
+ */
+ if (number < 0) {
+ /*
+ * Note that j is the number of UTF8 chars before the separator,
+ * not the number of bytes! (bug 151975)
+ */
+ j = xmlUTF8Strloc(format, self->patternSeparator);
+ if (j < 0) {
+ /* No -ve pattern present, so use default signing */
+ default_sign = 1;
+ }
+ else {
+ /* Skip over pattern separator (accounting for UTF8) */
+ the_format = (xmlChar *)xmlUTF8Strpos(format, j + 1);
+ /*
+ * Flag changes interpretation of percent/permille
+ * in -ve pattern
+ */
+ format_info.is_negative_pattern = TRUE;
+ format_info.is_multiplier_set = FALSE;
+
+ /* First do the -ve prefix */
+ nprefix = the_format;
+ nprefix_length = xsltFormatNumberPreSuffix(self,
+ &the_format, &format_info);
+ if (nprefix_length<0) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+
+ while (*the_format != 0) {
+ if ( (xsltUTF8Charcmp(the_format, (self)->percent) == 0) ||
+ (xsltUTF8Charcmp(the_format, (self)->permille)== 0) ) {
+ if (format_info.is_multiplier_set) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+ format_info.is_multiplier_set = TRUE;
+ delayed_multiplier = 1;
+ }
+ else if (IS_SPECIAL(self, the_format))
+ delayed_multiplier = 0;
+ else
+ break; /* while */
+ if ((len = xsltUTF8Size(the_format)) < 1) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+ the_format += len;
+ }
+ if (delayed_multiplier != 0) {
+ format_info.is_multiplier_set = FALSE;
+ the_format -= len;
+ }
+
+ /* Finally do the -ve suffix */
+ if (*the_format != 0) {
+ nsuffix = the_format;
+ nsuffix_length = xsltFormatNumberPreSuffix(self,
+ &the_format, &format_info);
+ if (nsuffix_length < 0) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+ }
+ else
+ nsuffix_length = 0;
+ if (*the_format != 0) {
+ found_error = 1;
+ goto OUTPUT_NUMBER;
+ }
+ /*
+ * Here's another Java peculiarity:
+ * if -ve prefix/suffix == +ve ones, discard & use default
+ */
+ if ((nprefix_length != prefix_length) ||
+ (nsuffix_length != suffix_length) ||
+ ((nprefix_length > 0) &&
+ (xmlStrncmp(nprefix, prefix, prefix_length) !=0 )) ||
+ ((nsuffix_length > 0) &&
+ (xmlStrncmp(nsuffix, suffix, suffix_length) !=0 ))) {
+ prefix = nprefix;
+ prefix_length = nprefix_length;
+ suffix = nsuffix;
+ suffix_length = nsuffix_length;
+ } /* else {
+ default_sign = 1;
+ }
+ */
+ }
+ }
+
+OUTPUT_NUMBER:
+ if (found_error != 0) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltFormatNumberConversion : "
+ "error in format string '%s', using default\n", format);
+ default_sign = (number < 0.0) ? 1 : 0;
+ prefix_length = suffix_length = 0;
+ format_info.integer_hash = 0;
+ format_info.integer_digits = 1;
+ format_info.frac_digits = 1;
+ format_info.frac_hash = 4;
+ format_info.group = -1;
+ format_info.multiplier = 1;
+ format_info.add_decimal = TRUE;
+ }
+
+ /* Ready to output our number. First see if "default sign" is required */
+ if (default_sign != 0)
+ xmlBufferAdd(buffer, self->minusSign, xsltUTF8Size(self->minusSign));
+
+ /* Put the prefix into the buffer */
+ for (j = 0; j < prefix_length; j++) {
+ if ((pchar = *prefix++) == SYMBOL_QUOTE) {
+ len = xsltUTF8Size(prefix);
+ xmlBufferAdd(buffer, prefix, len);
+ prefix += len;
+ j += len - 1; /* length of symbol less length of quote */
+ } else
+ xmlBufferAdd(buffer, &pchar, 1);
+ }
+
+ /* Next do the integer part of the number */
+ number = fabs(number) * (double)format_info.multiplier;
+ scale = pow(10.0, (double)(format_info.frac_digits + format_info.frac_hash));
+ number = floor((scale * number + 0.5)) / scale;
+ if ((self->grouping != NULL) &&
+ (self->grouping[0] != 0)) {
+
+ len = xmlStrlen(self->grouping);
+ pchar = xsltGetUTF8Char(self->grouping, &len);
+ xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
+ format_info.integer_digits,
+ format_info.group,
+ pchar, len);
+ } else
+ xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
+ format_info.integer_digits,
+ format_info.group,
+ ',', 1);
+
+ /* Special case: java treats '.#' like '.0', '.##' like '.0#', etc. */
+ if ((format_info.integer_digits + format_info.integer_hash +
+ format_info.frac_digits == 0) && (format_info.frac_hash > 0)) {
+ ++format_info.frac_digits;
+ --format_info.frac_hash;
+ }
+
+ /* Add leading zero, if required */
+ if ((floor(number) == 0) &&
+ (format_info.integer_digits + format_info.frac_digits == 0)) {
+ xmlBufferAdd(buffer, self->zeroDigit, xsltUTF8Size(self->zeroDigit));
+ }
+
+ /* Next the fractional part, if required */
+ if (format_info.frac_digits + format_info.frac_hash == 0) {
+ if (format_info.add_decimal)
+ xmlBufferAdd(buffer, self->decimalPoint,
+ xsltUTF8Size(self->decimalPoint));
+ }
+ else {
+ number -= floor(number);
+ if ((number != 0) || (format_info.frac_digits != 0)) {
+ xmlBufferAdd(buffer, self->decimalPoint,
+ xsltUTF8Size(self->decimalPoint));
+ number = floor(scale * number + 0.5);
+ for (j = format_info.frac_hash; j > 0; j--) {
+ if (fmod(number, 10.0) >= 1.0)
+ break; /* for */
+ number /= 10.0;
+ }
+ xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0],
+ format_info.frac_digits + j,
+ 0, 0, 0);
+ }
+ }
+ /* Put the suffix into the buffer */
+ for (j = 0; j < suffix_length; j++) {
+ if ((pchar = *suffix++) == SYMBOL_QUOTE) {
+ len = xsltUTF8Size(suffix);
+ xmlBufferAdd(buffer, suffix, len);
+ suffix += len;
+ j += len - 1; /* length of symbol less length of escape */
+ } else
+ xmlBufferAdd(buffer, &pchar, 1);
+ }
+
+ *result = xmlStrdup(xmlBufferContent(buffer));
+ xmlBufferFree(buffer);
+ return status;
+}
+
diff --git a/libxslt/numbersInternals.h b/libxslt/numbersInternals.h
new file mode 100644
index 0000000..8524592
--- /dev/null
+++ b/libxslt/numbersInternals.h
@@ -0,0 +1,73 @@
+/*
+ * Summary: Implementation of the XSLT number functions
+ * Description: Implementation of the XSLT number functions
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Bjorn Reese <breese@users.sourceforge.net> and Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_NUMBERSINTERNALS_H__
+#define __XML_XSLT_NUMBERSINTERNALS_H__
+
+#include <libxml/tree.h>
+#include "xsltexports.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _xsltCompMatch;
+
+/**
+ * xsltNumberData:
+ *
+ * This data structure is just a wrapper to pass xsl:number data in.
+ */
+typedef struct _xsltNumberData xsltNumberData;
+typedef xsltNumberData *xsltNumberDataPtr;
+
+struct _xsltNumberData {
+ const xmlChar *level;
+ const xmlChar *count;
+ const xmlChar *from;
+ const xmlChar *value;
+ const xmlChar *format;
+ int has_format;
+ int digitsPerGroup;
+ int groupingCharacter;
+ int groupingCharacterLen;
+ xmlDocPtr doc;
+ xmlNodePtr node;
+ struct _xsltCompMatch *countPat;
+ struct _xsltCompMatch *fromPat;
+
+ /*
+ * accelerators
+ */
+};
+
+/**
+ * xsltFormatNumberInfo,:
+ *
+ * This data structure lists the various parameters needed to format numbers.
+ */
+typedef struct _xsltFormatNumberInfo xsltFormatNumberInfo;
+typedef xsltFormatNumberInfo *xsltFormatNumberInfoPtr;
+
+struct _xsltFormatNumberInfo {
+ int integer_hash; /* Number of '#' in integer part */
+ int integer_digits; /* Number of '0' in integer part */
+ int frac_digits; /* Number of '0' in fractional part */
+ int frac_hash; /* Number of '#' in fractional part */
+ int group; /* Number of chars per display 'group' */
+ int multiplier; /* Scaling for percent or permille */
+ char add_decimal; /* Flag for whether decimal point appears in pattern */
+ char is_multiplier_set; /* Flag to catch multiple occurences of percent/permille */
+ char is_negative_pattern;/* Flag for processing -ve prefix/suffix */
+};
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __XML_XSLT_NUMBERSINTERNALS_H__ */
diff --git a/libxslt/pattern.c b/libxslt/pattern.c
new file mode 100644
index 0000000..e211a01
--- /dev/null
+++ b/libxslt/pattern.c
@@ -0,0 +1,2593 @@
+/*
+ * pattern.c: Implemetation of the template match compilation and lookup
+ *
+ * Reference:
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+/*
+ * TODO: handle pathological cases like *[*[@a="b"]]
+ * TODO: detect [number] at compilation, optimize accordingly
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/valid.h>
+#include <libxml/hash.h>
+#include <libxml/xmlerror.h>
+#include <libxml/parserInternals.h>
+#include <libxml/xpath.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "xsltutils.h"
+#include "imports.h"
+#include "templates.h"
+#include "keys.h"
+#include "pattern.h"
+#include "documents.h"
+
+#ifdef WITH_XSLT_DEBUG
+#define WITH_XSLT_DEBUG_PATTERN
+#endif
+
+/*
+ * Types are private:
+ */
+
+typedef enum {
+ XSLT_OP_END=0,
+ XSLT_OP_ROOT,
+ XSLT_OP_ELEM,
+ XSLT_OP_ATTR,
+ XSLT_OP_PARENT,
+ XSLT_OP_ANCESTOR,
+ XSLT_OP_ID,
+ XSLT_OP_KEY,
+ XSLT_OP_NS,
+ XSLT_OP_ALL,
+ XSLT_OP_PI,
+ XSLT_OP_COMMENT,
+ XSLT_OP_TEXT,
+ XSLT_OP_NODE,
+ XSLT_OP_PREDICATE
+} xsltOp;
+
+typedef enum {
+ AXIS_CHILD=1,
+ AXIS_ATTRIBUTE
+} xsltAxis;
+
+typedef struct _xsltStepState xsltStepState;
+typedef xsltStepState *xsltStepStatePtr;
+struct _xsltStepState {
+ int step;
+ xmlNodePtr node;
+};
+
+typedef struct _xsltStepStates xsltStepStates;
+typedef xsltStepStates *xsltStepStatesPtr;
+struct _xsltStepStates {
+ int nbstates;
+ int maxstates;
+ xsltStepStatePtr states;
+};
+
+typedef struct _xsltStepOp xsltStepOp;
+typedef xsltStepOp *xsltStepOpPtr;
+struct _xsltStepOp {
+ xsltOp op;
+ xmlChar *value;
+ xmlChar *value2;
+ xmlChar *value3;
+ xmlXPathCompExprPtr comp;
+ /*
+ * Optimisations for count
+ */
+ int previousExtra;
+ int indexExtra;
+ int lenExtra;
+};
+
+struct _xsltCompMatch {
+ struct _xsltCompMatch *next; /* siblings in the name hash */
+ float priority; /* the priority */
+ const xmlChar *pattern; /* the pattern */
+ const xmlChar *mode; /* the mode */
+ const xmlChar *modeURI; /* the mode URI */
+ xsltTemplatePtr template; /* the associated template */
+
+ int direct;
+ /* TODO fix the statically allocated size steps[] */
+ int nbStep;
+ int maxStep;
+ xmlNsPtr *nsList; /* the namespaces in scope */
+ int nsNr; /* the number of namespaces in scope */
+ xsltStepOpPtr steps; /* ops for computation */
+};
+
+typedef struct _xsltParserContext xsltParserContext;
+typedef xsltParserContext *xsltParserContextPtr;
+struct _xsltParserContext {
+ xsltStylesheetPtr style; /* the stylesheet */
+ xsltTransformContextPtr ctxt; /* the transformation or NULL */
+ const xmlChar *cur; /* the current char being parsed */
+ const xmlChar *base; /* the full expression */
+ xmlDocPtr doc; /* the source document */
+ xmlNodePtr elem; /* the source element */
+ int error; /* error code */
+ xsltCompMatchPtr comp; /* the result */
+};
+
+/************************************************************************
+ * *
+ * Type functions *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltNewCompMatch:
+ *
+ * Create a new XSLT CompMatch
+ *
+ * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
+ */
+static xsltCompMatchPtr
+xsltNewCompMatch(void) {
+ xsltCompMatchPtr cur;
+
+ cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
+ if (cur == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltNewCompMatch : out of memory error\n");
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltCompMatch));
+ cur->maxStep = 10;
+ cur->nbStep = 0;
+ cur-> steps = (xsltStepOpPtr) xmlMalloc(sizeof(xsltStepOp) *
+ cur->maxStep);
+ if (cur->steps == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltNewCompMatch : out of memory error\n");
+ xmlFree(cur);
+ return(NULL);
+ }
+ cur->nsNr = 0;
+ cur->nsList = NULL;
+ cur->direct = 0;
+ return(cur);
+}
+
+/**
+ * xsltFreeCompMatch:
+ * @comp: an XSLT comp
+ *
+ * Free up the memory allocated by @comp
+ */
+static void
+xsltFreeCompMatch(xsltCompMatchPtr comp) {
+ xsltStepOpPtr op;
+ int i;
+
+ if (comp == NULL)
+ return;
+ if (comp->pattern != NULL)
+ xmlFree((xmlChar *)comp->pattern);
+ if (comp->nsList != NULL)
+ xmlFree(comp->nsList);
+ for (i = 0;i < comp->nbStep;i++) {
+ op = &comp->steps[i];
+ if (op->value != NULL)
+ xmlFree(op->value);
+ if (op->value2 != NULL)
+ xmlFree(op->value2);
+ if (op->value3 != NULL)
+ xmlFree(op->value3);
+ if (op->comp != NULL)
+ xmlXPathFreeCompExpr(op->comp);
+ }
+ xmlFree(comp->steps);
+ memset(comp, -1, sizeof(xsltCompMatch));
+ xmlFree(comp);
+}
+
+/**
+ * xsltFreeCompMatchList:
+ * @comp: an XSLT comp list
+ *
+ * Free up the memory allocated by all the elements of @comp
+ */
+void
+xsltFreeCompMatchList(xsltCompMatchPtr comp) {
+ xsltCompMatchPtr cur;
+
+ while (comp != NULL) {
+ cur = comp;
+ comp = comp->next;
+ xsltFreeCompMatch(cur);
+ }
+}
+
+/**
+ * xsltNormalizeCompSteps:
+ * @payload: pointer to template hash table entry
+ * @data: pointer to the stylesheet
+ * @name: template match name
+ *
+ * This is a hashtable scanner function to normalize the compiled
+ * steps of an imported stylesheet.
+ */
+void xsltNormalizeCompSteps(void *payload,
+ void *data, const xmlChar *name ATTRIBUTE_UNUSED) {
+ xsltCompMatchPtr comp = payload;
+ xsltStylesheetPtr style = data;
+ int ix;
+
+ for (ix = 0; ix < comp->nbStep; ix++) {
+ comp->steps[ix].previousExtra += style->extrasNr;
+ comp->steps[ix].indexExtra += style->extrasNr;
+ comp->steps[ix].lenExtra += style->extrasNr;
+ }
+}
+
+/**
+ * xsltNewParserContext:
+ * @style: the stylesheet
+ * @ctxt: the transformation context, if done at run-time
+ *
+ * Create a new XSLT ParserContext
+ *
+ * Returns the newly allocated xsltParserContextPtr or NULL in case of error
+ */
+static xsltParserContextPtr
+xsltNewParserContext(xsltStylesheetPtr style, xsltTransformContextPtr ctxt) {
+ xsltParserContextPtr cur;
+
+ cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
+ if (cur == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltNewParserContext : malloc failed\n");
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltParserContext));
+ cur->style = style;
+ cur->ctxt = ctxt;
+ return(cur);
+}
+
+/**
+ * xsltFreeParserContext:
+ * @ctxt: an XSLT parser context
+ *
+ * Free up the memory allocated by @ctxt
+ */
+static void
+xsltFreeParserContext(xsltParserContextPtr ctxt) {
+ if (ctxt == NULL)
+ return;
+ memset(ctxt, -1, sizeof(xsltParserContext));
+ xmlFree(ctxt);
+}
+
+/**
+ * xsltCompMatchAdd:
+ * @comp: the compiled match expression
+ * @op: an op
+ * @value: the first value
+ * @value2: the second value
+ * @novar: flag to set XML_XPATH_NOVAR
+ *
+ * Add an step to an XSLT Compiled Match
+ *
+ * Returns -1 in case of failure, 0 otherwise.
+ */
+static int
+xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp,
+ xsltOp op, xmlChar * value, xmlChar * value2, int novar)
+{
+ if (comp->nbStep >= comp->maxStep) {
+ xsltStepOpPtr tmp;
+
+ tmp = (xsltStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
+ sizeof(xsltStepOp));
+ if (tmp == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltCompMatchAdd: memory re-allocation failure.\n");
+ if (ctxt->style != NULL)
+ ctxt->style->errors++;
+ if (value)
+ xmlFree(value);
+ if (value2)
+ xmlFree(value2);
+ return (-1);
+ }
+ comp->maxStep *= 2;
+ comp->steps = tmp;
+ }
+ comp->steps[comp->nbStep].op = op;
+ comp->steps[comp->nbStep].value = value;
+ comp->steps[comp->nbStep].value2 = value2;
+ comp->steps[comp->nbStep].value3 = NULL;
+ comp->steps[comp->nbStep].comp = NULL;
+ if (ctxt->ctxt != NULL) {
+ comp->steps[comp->nbStep].previousExtra =
+ xsltAllocateExtraCtxt(ctxt->ctxt);
+ comp->steps[comp->nbStep].indexExtra =
+ xsltAllocateExtraCtxt(ctxt->ctxt);
+ comp->steps[comp->nbStep].lenExtra =
+ xsltAllocateExtraCtxt(ctxt->ctxt);
+ } else {
+ comp->steps[comp->nbStep].previousExtra =
+ xsltAllocateExtra(ctxt->style);
+ comp->steps[comp->nbStep].indexExtra =
+ xsltAllocateExtra(ctxt->style);
+ comp->steps[comp->nbStep].lenExtra =
+ xsltAllocateExtra(ctxt->style);
+ }
+ if (op == XSLT_OP_PREDICATE) {
+ xmlXPathContextPtr xctxt;
+
+ if (ctxt->style != NULL)
+ xctxt = xmlXPathNewContext(ctxt->style->doc);
+ else
+ xctxt = xmlXPathNewContext(NULL);
+#ifdef XML_XPATH_NOVAR
+ if (novar != 0)
+ xctxt->flags = XML_XPATH_NOVAR;
+#endif
+ if (ctxt->style != NULL)
+ xctxt->dict = ctxt->style->dict;
+ comp->steps[comp->nbStep].comp = xmlXPathCtxtCompile(xctxt, value);
+ xmlXPathFreeContext(xctxt);
+ if (comp->steps[comp->nbStep].comp == NULL) {
+ xsltTransformError(NULL, ctxt->style, ctxt->elem,
+ "Failed to compile predicate\n");
+ if (ctxt->style != NULL)
+ ctxt->style->errors++;
+ }
+ }
+ comp->nbStep++;
+ return (0);
+}
+
+/**
+ * xsltSwapTopCompMatch:
+ * @comp: the compiled match expression
+ *
+ * reverse the two top steps.
+ */
+static void
+xsltSwapTopCompMatch(xsltCompMatchPtr comp) {
+ int i;
+ int j = comp->nbStep - 1;
+
+ if (j > 0) {
+ register xmlChar *tmp;
+ register xsltOp op;
+ register xmlXPathCompExprPtr expr;
+ register int t;
+ i = j - 1;
+ tmp = comp->steps[i].value;
+ comp->steps[i].value = comp->steps[j].value;
+ comp->steps[j].value = tmp;
+ tmp = comp->steps[i].value2;
+ comp->steps[i].value2 = comp->steps[j].value2;
+ comp->steps[j].value2 = tmp;
+ tmp = comp->steps[i].value3;
+ comp->steps[i].value3 = comp->steps[j].value3;
+ comp->steps[j].value3 = tmp;
+ op = comp->steps[i].op;
+ comp->steps[i].op = comp->steps[j].op;
+ comp->steps[j].op = op;
+ expr = comp->steps[i].comp;
+ comp->steps[i].comp = comp->steps[j].comp;
+ comp->steps[j].comp = expr;
+ t = comp->steps[i].previousExtra;
+ comp->steps[i].previousExtra = comp->steps[j].previousExtra;
+ comp->steps[j].previousExtra = t;
+ t = comp->steps[i].indexExtra;
+ comp->steps[i].indexExtra = comp->steps[j].indexExtra;
+ comp->steps[j].indexExtra = t;
+ t = comp->steps[i].lenExtra;
+ comp->steps[i].lenExtra = comp->steps[j].lenExtra;
+ comp->steps[j].lenExtra = t;
+ }
+}
+
+/**
+ * xsltReverseCompMatch:
+ * @ctxt: the parser context
+ * @comp: the compiled match expression
+ *
+ * reverse all the stack of expressions
+ */
+static void
+xsltReverseCompMatch(xsltParserContextPtr ctxt, xsltCompMatchPtr comp) {
+ int i = 0;
+ int j = comp->nbStep - 1;
+
+ while (j > i) {
+ register xmlChar *tmp;
+ register xsltOp op;
+ register xmlXPathCompExprPtr expr;
+ register int t;
+
+ tmp = comp->steps[i].value;
+ comp->steps[i].value = comp->steps[j].value;
+ comp->steps[j].value = tmp;
+ tmp = comp->steps[i].value2;
+ comp->steps[i].value2 = comp->steps[j].value2;
+ comp->steps[j].value2 = tmp;
+ tmp = comp->steps[i].value3;
+ comp->steps[i].value3 = comp->steps[j].value3;
+ comp->steps[j].value3 = tmp;
+ op = comp->steps[i].op;
+ comp->steps[i].op = comp->steps[j].op;
+ comp->steps[j].op = op;
+ expr = comp->steps[i].comp;
+ comp->steps[i].comp = comp->steps[j].comp;
+ comp->steps[j].comp = expr;
+ t = comp->steps[i].previousExtra;
+ comp->steps[i].previousExtra = comp->steps[j].previousExtra;
+ comp->steps[j].previousExtra = t;
+ t = comp->steps[i].indexExtra;
+ comp->steps[i].indexExtra = comp->steps[j].indexExtra;
+ comp->steps[j].indexExtra = t;
+ t = comp->steps[i].lenExtra;
+ comp->steps[i].lenExtra = comp->steps[j].lenExtra;
+ comp->steps[j].lenExtra = t;
+ j--;
+ i++;
+ }
+ xsltCompMatchAdd(ctxt, comp, XSLT_OP_END, NULL, NULL, 0);
+
+ /*
+ * Detect consecutive XSLT_OP_PREDICATE and predicates on ops which
+ * haven't been optimized yet indicating a direct matching should be done.
+ */
+ for (i = 0;i < comp->nbStep - 1;i++) {
+ xsltOp op = comp->steps[i].op;
+
+ if ((op != XSLT_OP_ELEM) &&
+ (op != XSLT_OP_ALL) &&
+ (comp->steps[i + 1].op == XSLT_OP_PREDICATE)) {
+
+ comp->direct = 1;
+ if (comp->pattern[0] != '/') {
+ xmlChar *query;
+
+ query = xmlStrdup((const xmlChar *)"//");
+ query = xmlStrcat(query, comp->pattern);
+
+ xmlFree((xmlChar *) comp->pattern);
+ comp->pattern = query;
+ }
+ break;
+ }
+ }
+}
+
+/************************************************************************
+ * *
+ * The interpreter for the precompiled patterns *
+ * *
+ ************************************************************************/
+
+static int
+xsltPatPushState(xsltTransformContextPtr ctxt, xsltStepStates *states,
+ int step, xmlNodePtr node) {
+ if ((states->states == NULL) || (states->maxstates <= 0)) {
+ states->maxstates = 4;
+ states->nbstates = 0;
+ states->states = xmlMalloc(4 * sizeof(xsltStepState));
+ }
+ else if (states->maxstates <= states->nbstates) {
+ xsltStepState *tmp;
+
+ tmp = (xsltStepStatePtr) xmlRealloc(states->states,
+ 2 * states->maxstates * sizeof(xsltStepState));
+ if (tmp == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltPatPushState: memory re-allocation failure.\n");
+ ctxt->state = XSLT_STATE_STOPPED;
+ return(-1);
+ }
+ states->states = tmp;
+ states->maxstates *= 2;
+ }
+ states->states[states->nbstates].step = step;
+ states->states[states->nbstates++].node = node;
+#if 0
+ fprintf(stderr, "Push: %d, %s\n", step, node->name);
+#endif
+ return(0);
+}
+
+/**
+ * xsltTestCompMatchDirect:
+ * @ctxt: a XSLT process context
+ * @comp: the precompiled pattern
+ * @node: a node
+ * @nsList: the namespaces in scope
+ * @nsNr: the number of namespaces in scope
+ *
+ * Test whether the node matches the pattern, do a direct evalutation
+ * and not a step by step evaluation.
+ *
+ * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
+ */
+static int
+xsltTestCompMatchDirect(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
+ xmlNodePtr node, xmlNsPtr *nsList, int nsNr) {
+ xsltStepOpPtr sel = NULL;
+ xmlDocPtr prevdoc;
+ xmlDocPtr doc;
+ xmlXPathObjectPtr list;
+ int ix, j;
+ int nocache = 0;
+ int isRVT;
+
+ doc = node->doc;
+ if (XSLT_IS_RES_TREE_FRAG(doc))
+ isRVT = 1;
+ else
+ isRVT = 0;
+ sel = &comp->steps[0]; /* store extra in first step arbitrarily */
+
+ prevdoc = (xmlDocPtr)
+ XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
+ ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
+ list = (xmlXPathObjectPtr)
+ XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra);
+
+ if ((list == NULL) || (prevdoc != doc)) {
+ xmlXPathObjectPtr newlist;
+ xmlNodePtr parent = node->parent;
+ xmlDocPtr olddoc;
+ xmlNodePtr oldnode;
+ int oldNsNr, oldContextSize, oldProximityPosition;
+ xmlNsPtr *oldNamespaces;
+
+ oldnode = ctxt->xpathCtxt->node;
+ olddoc = ctxt->xpathCtxt->doc;
+ oldNsNr = ctxt->xpathCtxt->nsNr;
+ oldNamespaces = ctxt->xpathCtxt->namespaces;
+ oldContextSize = ctxt->xpathCtxt->contextSize;
+ oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
+ ctxt->xpathCtxt->node = node;
+ ctxt->xpathCtxt->doc = doc;
+ ctxt->xpathCtxt->namespaces = nsList;
+ ctxt->xpathCtxt->nsNr = nsNr;
+ newlist = xmlXPathEval(comp->pattern, ctxt->xpathCtxt);
+ ctxt->xpathCtxt->node = oldnode;
+ ctxt->xpathCtxt->doc = olddoc;
+ ctxt->xpathCtxt->namespaces = oldNamespaces;
+ ctxt->xpathCtxt->nsNr = oldNsNr;
+ ctxt->xpathCtxt->contextSize = oldContextSize;
+ ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
+ if (newlist == NULL)
+ return(-1);
+ if (newlist->type != XPATH_NODESET) {
+ xmlXPathFreeObject(newlist);
+ return(-1);
+ }
+ ix = 0;
+
+ if ((parent == NULL) || (node->doc == NULL) || isRVT)
+ nocache = 1;
+
+ if (nocache == 0) {
+ if (list != NULL)
+ xmlXPathFreeObject(list);
+ list = newlist;
+
+ XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) =
+ (void *) list;
+ XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
+ (void *) doc;
+ XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
+ 0;
+ XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) =
+ (xmlFreeFunc) xmlXPathFreeObject;
+ } else
+ list = newlist;
+ }
+ if ((list->nodesetval == NULL) ||
+ (list->nodesetval->nodeNr <= 0)) {
+ if (nocache == 1)
+ xmlXPathFreeObject(list);
+ return(0);
+ }
+ /* TODO: store the index and use it for the scan */
+ if (ix == 0) {
+ for (j = 0;j < list->nodesetval->nodeNr;j++) {
+ if (list->nodesetval->nodeTab[j] == node) {
+ if (nocache == 1)
+ xmlXPathFreeObject(list);
+ return(1);
+ }
+ }
+ } else {
+ }
+ if (nocache == 1)
+ xmlXPathFreeObject(list);
+ return(0);
+}
+
+/**
+ * xsltTestPredicateMatch:
+ * @ctxt: a XSLT process context
+ * @comp: the precompiled pattern
+ * @node: a node
+ * @step: the predicate step
+ * @sel: the previous step
+ *
+ * Test whether the node matches the predicate
+ *
+ * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
+ */
+static int
+xsltTestPredicateMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
+ xmlNodePtr node, xsltStepOpPtr step,
+ xsltStepOpPtr sel) {
+ xmlNodePtr oldNode;
+ xmlDocPtr doc;
+ int oldCS, oldCP;
+ int pos = 0, len = 0;
+ int isRVT;
+ int match;
+
+ if (step->value == NULL)
+ return(0);
+ if (step->comp == NULL)
+ return(0);
+
+ doc = node->doc;
+ if (XSLT_IS_RES_TREE_FRAG(doc))
+ isRVT = 1;
+ else
+ isRVT = 0;
+
+ /*
+ * Recompute contextSize and proximityPosition.
+ *
+ * TODO: Make this work for additional ops. Currently, only XSLT_OP_ELEM
+ * and XSLT_OP_ALL are supported.
+ */
+ oldCS = ctxt->xpathCtxt->contextSize;
+ oldCP = ctxt->xpathCtxt->proximityPosition;
+ if ((sel != NULL) &&
+ (sel->op == XSLT_OP_ELEM) &&
+ (sel->value != NULL) &&
+ (node->type == XML_ELEMENT_NODE) &&
+ (node->parent != NULL)) {
+ xmlNodePtr previous;
+ int nocache = 0;
+
+ previous = (xmlNodePtr)
+ XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
+ if ((previous != NULL) &&
+ (previous->parent == node->parent)) {
+ /*
+ * just walk back to adjust the index
+ */
+ int indx = 0;
+ xmlNodePtr sibling = node;
+
+ while (sibling != NULL) {
+ if (sibling == previous)
+ break;
+ if ((sibling->type == XML_ELEMENT_NODE) &&
+ (previous->name != NULL) &&
+ (sibling->name != NULL) &&
+ (previous->name[0] == sibling->name[0]) &&
+ (xmlStrEqual(previous->name, sibling->name)))
+ {
+ if ((sel->value2 == NULL) ||
+ ((sibling->ns != NULL) &&
+ (xmlStrEqual(sel->value2, sibling->ns->href))))
+ indx++;
+ }
+ sibling = sibling->prev;
+ }
+ if (sibling == NULL) {
+ /* hum going backward in document order ... */
+ indx = 0;
+ sibling = node;
+ while (sibling != NULL) {
+ if (sibling == previous)
+ break;
+ if ((sibling->type == XML_ELEMENT_NODE) &&
+ (previous->name != NULL) &&
+ (sibling->name != NULL) &&
+ (previous->name[0] == sibling->name[0]) &&
+ (xmlStrEqual(previous->name, sibling->name)))
+ {
+ if ((sel->value2 == NULL) ||
+ ((sibling->ns != NULL) &&
+ (xmlStrEqual(sel->value2,
+ sibling->ns->href))))
+ {
+ indx--;
+ }
+ }
+ sibling = sibling->next;
+ }
+ }
+ if (sibling != NULL) {
+ pos = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) + indx;
+ /*
+ * If the node is in a Value Tree we need to
+ * save len, but cannot cache the node!
+ * (bugs 153137 and 158840)
+ */
+ if (node->doc != NULL) {
+ len = XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival);
+ if (!isRVT) {
+ XSLT_RUNTIME_EXTRA(ctxt,
+ sel->previousExtra, ptr) = node;
+ XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos;
+ }
+ }
+ } else
+ pos = 0;
+ } else {
+ /*
+ * recompute the index
+ */
+ xmlNodePtr parent = node->parent;
+ xmlNodePtr siblings = NULL;
+
+ if (parent) siblings = parent->children;
+
+ while (siblings != NULL) {
+ if (siblings->type == XML_ELEMENT_NODE) {
+ if (siblings == node) {
+ len++;
+ pos = len;
+ } else if ((node->name != NULL) &&
+ (siblings->name != NULL) &&
+ (node->name[0] == siblings->name[0]) &&
+ (xmlStrEqual(node->name, siblings->name))) {
+ if ((sel->value2 == NULL) ||
+ ((siblings->ns != NULL) &&
+ (xmlStrEqual(sel->value2, siblings->ns->href))))
+ len++;
+ }
+ }
+ siblings = siblings->next;
+ }
+ if ((parent == NULL) || (node->doc == NULL))
+ nocache = 1;
+ else {
+ while (parent->parent != NULL)
+ parent = parent->parent;
+ if (((parent->type != XML_DOCUMENT_NODE) &&
+ (parent->type != XML_HTML_DOCUMENT_NODE)) ||
+ (parent != (xmlNodePtr) node->doc))
+ nocache = 1;
+ }
+ }
+ if (pos != 0) {
+ ctxt->xpathCtxt->contextSize = len;
+ ctxt->xpathCtxt->proximityPosition = pos;
+ /*
+ * If the node is in a Value Tree we cannot
+ * cache it !
+ */
+ if ((!isRVT) && (node->doc != NULL) &&
+ (nocache == 0)) {
+ XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node;
+ XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos;
+ XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = len;
+ }
+ }
+ } else if ((sel != NULL) && (sel->op == XSLT_OP_ALL) &&
+ (node->type == XML_ELEMENT_NODE)) {
+ xmlNodePtr previous;
+ int nocache = 0;
+
+ previous = (xmlNodePtr)
+ XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
+ if ((previous != NULL) &&
+ (previous->parent == node->parent)) {
+ /*
+ * just walk back to adjust the index
+ */
+ int indx = 0;
+ xmlNodePtr sibling = node;
+
+ while (sibling != NULL) {
+ if (sibling == previous)
+ break;
+ if (sibling->type == XML_ELEMENT_NODE)
+ indx++;
+ sibling = sibling->prev;
+ }
+ if (sibling == NULL) {
+ /* hum going backward in document order ... */
+ indx = 0;
+ sibling = node;
+ while (sibling != NULL) {
+ if (sibling == previous)
+ break;
+ if (sibling->type == XML_ELEMENT_NODE)
+ indx--;
+ sibling = sibling->next;
+ }
+ }
+ if (sibling != NULL) {
+ pos = XSLT_RUNTIME_EXTRA(ctxt,
+ sel->indexExtra, ival) + indx;
+ /*
+ * If the node is in a Value Tree we cannot
+ * cache it !
+ */
+ if ((node->doc != NULL) && !isRVT) {
+ len = XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival);
+ XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node;
+ XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos;
+ }
+ } else
+ pos = 0;
+ } else {
+ /*
+ * recompute the index
+ */
+ xmlNodePtr parent = node->parent;
+ xmlNodePtr siblings = NULL;
+
+ if (parent) siblings = parent->children;
+
+ while (siblings != NULL) {
+ if (siblings->type == XML_ELEMENT_NODE) {
+ len++;
+ if (siblings == node) {
+ pos = len;
+ }
+ }
+ siblings = siblings->next;
+ }
+ if ((parent == NULL) || (node->doc == NULL))
+ nocache = 1;
+ else {
+ while (parent->parent != NULL)
+ parent = parent->parent;
+ if (((parent->type != XML_DOCUMENT_NODE) &&
+ (parent->type != XML_HTML_DOCUMENT_NODE)) ||
+ (parent != (xmlNodePtr) node->doc))
+ nocache = 1;
+ }
+ }
+ if (pos != 0) {
+ ctxt->xpathCtxt->contextSize = len;
+ ctxt->xpathCtxt->proximityPosition = pos;
+ /*
+ * If the node is in a Value Tree we cannot
+ * cache it !
+ */
+ if ((node->doc != NULL) && (nocache == 0) && !isRVT) {
+ XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node;
+ XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos;
+ XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = len;
+ }
+ }
+ }
+
+ oldNode = ctxt->node;
+ ctxt->node = node;
+
+ match = xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList, comp->nsNr);
+
+ if (pos != 0) {
+ ctxt->xpathCtxt->contextSize = oldCS;
+ ctxt->xpathCtxt->proximityPosition = oldCP;
+ }
+ ctxt->node = oldNode;
+
+ return match;
+}
+
+/**
+ * xsltTestCompMatch:
+ * @ctxt: a XSLT process context
+ * @comp: the precompiled pattern
+ * @node: a node
+ * @mode: the mode name or NULL
+ * @modeURI: the mode URI or NULL
+ *
+ * Test whether the node matches the pattern
+ *
+ * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
+ */
+static int
+xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
+ xmlNodePtr matchNode, const xmlChar *mode,
+ const xmlChar *modeURI) {
+ int i;
+ xmlNodePtr node = matchNode;
+ xsltStepOpPtr step, sel = NULL;
+ xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */
+
+ if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) {
+ xsltTransformError(ctxt, NULL, node,
+ "xsltTestCompMatch: null arg\n");
+ return(-1);
+ }
+ if (mode != NULL) {
+ if (comp->mode == NULL)
+ return(0);
+ /*
+ * both mode strings must be interned on the stylesheet dictionary
+ */
+ if (comp->mode != mode)
+ return(0);
+ } else {
+ if (comp->mode != NULL)
+ return(0);
+ }
+ if (modeURI != NULL) {
+ if (comp->modeURI == NULL)
+ return(0);
+ /*
+ * both modeURI strings must be interned on the stylesheet dictionary
+ */
+ if (comp->modeURI != modeURI)
+ return(0);
+ } else {
+ if (comp->modeURI != NULL)
+ return(0);
+ }
+
+ i = 0;
+restart:
+ for (;i < comp->nbStep;i++) {
+ step = &comp->steps[i];
+ if (step->op != XSLT_OP_PREDICATE)
+ sel = step;
+ switch (step->op) {
+ case XSLT_OP_END:
+ goto found;
+ case XSLT_OP_ROOT:
+ if ((node->type == XML_DOCUMENT_NODE) ||
+#ifdef LIBXML_DOCB_ENABLED
+ (node->type == XML_DOCB_DOCUMENT_NODE) ||
+#endif
+ (node->type == XML_HTML_DOCUMENT_NODE))
+ continue;
+ if ((node->type == XML_ELEMENT_NODE) && (node->name[0] == ' '))
+ continue;
+ goto rollback;
+ case XSLT_OP_ELEM:
+ if (node->type != XML_ELEMENT_NODE)
+ goto rollback;
+ if (step->value == NULL)
+ continue;
+ if (step->value[0] != node->name[0])
+ goto rollback;
+ if (!xmlStrEqual(step->value, node->name))
+ goto rollback;
+
+ /* Namespace test */
+ if (node->ns == NULL) {
+ if (step->value2 != NULL)
+ goto rollback;
+ } else if (node->ns->href != NULL) {
+ if (step->value2 == NULL)
+ goto rollback;
+ if (!xmlStrEqual(step->value2, node->ns->href))
+ goto rollback;
+ }
+ continue;
+ case XSLT_OP_ATTR:
+ if (node->type != XML_ATTRIBUTE_NODE)
+ goto rollback;
+ if (step->value != NULL) {
+ if (step->value[0] != node->name[0])
+ goto rollback;
+ if (!xmlStrEqual(step->value, node->name))
+ goto rollback;
+ }
+ /* Namespace test */
+ if (node->ns == NULL) {
+ if (step->value2 != NULL)
+ goto rollback;
+ } else if (step->value2 != NULL) {
+ if (!xmlStrEqual(step->value2, node->ns->href))
+ goto rollback;
+ }
+ continue;
+ case XSLT_OP_PARENT:
+ if ((node->type == XML_DOCUMENT_NODE) ||
+ (node->type == XML_HTML_DOCUMENT_NODE) ||
+#ifdef LIBXML_DOCB_ENABLED
+ (node->type == XML_DOCB_DOCUMENT_NODE) ||
+#endif
+ (node->type == XML_NAMESPACE_DECL))
+ goto rollback;
+ node = node->parent;
+ if (node == NULL)
+ goto rollback;
+ if (step->value == NULL)
+ continue;
+ if (step->value[0] != node->name[0])
+ goto rollback;
+ if (!xmlStrEqual(step->value, node->name))
+ goto rollback;
+ /* Namespace test */
+ if (node->ns == NULL) {
+ if (step->value2 != NULL)
+ goto rollback;
+ } else if (node->ns->href != NULL) {
+ if (step->value2 == NULL)
+ goto rollback;
+ if (!xmlStrEqual(step->value2, node->ns->href))
+ goto rollback;
+ }
+ continue;
+ case XSLT_OP_ANCESTOR:
+ /* TODO: implement coalescing of ANCESTOR/NODE ops */
+ if (step->value == NULL) {
+ step = &comp->steps[i+1];
+ if (step->op == XSLT_OP_ROOT)
+ goto found;
+ /* added NS, ID and KEY as a result of bug 168208 */
+ if ((step->op != XSLT_OP_ELEM) &&
+ (step->op != XSLT_OP_ALL) &&
+ (step->op != XSLT_OP_NS) &&
+ (step->op != XSLT_OP_ID) &&
+ (step->op != XSLT_OP_KEY))
+ goto rollback;
+ }
+ if (node == NULL)
+ goto rollback;
+ if ((node->type == XML_DOCUMENT_NODE) ||
+ (node->type == XML_HTML_DOCUMENT_NODE) ||
+#ifdef LIBXML_DOCB_ENABLED
+ (node->type == XML_DOCB_DOCUMENT_NODE) ||
+#endif
+ (node->type == XML_NAMESPACE_DECL))
+ goto rollback;
+ node = node->parent;
+ if ((step->op != XSLT_OP_ELEM) && step->op != XSLT_OP_ALL) {
+ xsltPatPushState(ctxt, &states, i, node);
+ continue;
+ }
+ i++;
+ if (step->value == NULL) {
+ xsltPatPushState(ctxt, &states, i - 1, node);
+ continue;
+ }
+ while (node != NULL) {
+ if ((node->type == XML_ELEMENT_NODE) &&
+ (step->value[0] == node->name[0]) &&
+ (xmlStrEqual(step->value, node->name))) {
+ /* Namespace test */
+ if (node->ns == NULL) {
+ if (step->value2 == NULL)
+ break;
+ } else if (node->ns->href != NULL) {
+ if ((step->value2 != NULL) &&
+ (xmlStrEqual(step->value2, node->ns->href)))
+ break;
+ }
+ }
+ node = node->parent;
+ }
+ if (node == NULL)
+ goto rollback;
+ xsltPatPushState(ctxt, &states, i - 1, node);
+ continue;
+ case XSLT_OP_ID: {
+ /* TODO Handle IDs decently, must be done differently */
+ xmlAttrPtr id;
+
+ if (node->type != XML_ELEMENT_NODE)
+ goto rollback;
+
+ id = xmlGetID(node->doc, step->value);
+ if ((id == NULL) || (id->parent != node))
+ goto rollback;
+ break;
+ }
+ case XSLT_OP_KEY: {
+ xmlNodeSetPtr list;
+ int indx;
+
+ list = xsltGetKey(ctxt, step->value,
+ step->value3, step->value2);
+ if (list == NULL)
+ goto rollback;
+ for (indx = 0;indx < list->nodeNr;indx++)
+ if (list->nodeTab[indx] == node)
+ break;
+ if (indx >= list->nodeNr)
+ goto rollback;
+ break;
+ }
+ case XSLT_OP_NS:
+ if (node->type != XML_ELEMENT_NODE)
+ goto rollback;
+ if (node->ns == NULL) {
+ if (step->value != NULL)
+ goto rollback;
+ } else if (node->ns->href != NULL) {
+ if (step->value == NULL)
+ goto rollback;
+ if (!xmlStrEqual(step->value, node->ns->href))
+ goto rollback;
+ }
+ break;
+ case XSLT_OP_ALL:
+ if (node->type != XML_ELEMENT_NODE)
+ goto rollback;
+ break;
+ case XSLT_OP_PREDICATE: {
+ /*
+ * When there is cascading XSLT_OP_PREDICATE or a predicate
+ * after an op which hasn't been optimized yet, then use a
+ * direct computation approach. It's not done directly
+ * at the beginning of the routine to filter out as much
+ * as possible this costly computation.
+ */
+ if (comp->direct) {
+ if (states.states != NULL) {
+ /* Free the rollback states */
+ xmlFree(states.states);
+ }
+ return(xsltTestCompMatchDirect(ctxt, comp, matchNode,
+ comp->nsList, comp->nsNr));
+ }
+
+ if (!xsltTestPredicateMatch(ctxt, comp, node, step, sel))
+ goto rollback;
+
+ break;
+ }
+ case XSLT_OP_PI:
+ if (node->type != XML_PI_NODE)
+ goto rollback;
+ if (step->value != NULL) {
+ if (!xmlStrEqual(step->value, node->name))
+ goto rollback;
+ }
+ break;
+ case XSLT_OP_COMMENT:
+ if (node->type != XML_COMMENT_NODE)
+ goto rollback;
+ break;
+ case XSLT_OP_TEXT:
+ if ((node->type != XML_TEXT_NODE) &&
+ (node->type != XML_CDATA_SECTION_NODE))
+ goto rollback;
+ break;
+ case XSLT_OP_NODE:
+ switch (node->type) {
+ case XML_ELEMENT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_PI_NODE:
+ case XML_COMMENT_NODE:
+ case XML_TEXT_NODE:
+ break;
+ default:
+ goto rollback;
+ }
+ break;
+ }
+ }
+found:
+ if (states.states != NULL) {
+ /* Free the rollback states */
+ xmlFree(states.states);
+ }
+ return(1);
+rollback:
+ /* got an error try to rollback */
+ if (states.states == NULL)
+ return(0);
+ if (states.nbstates <= 0) {
+ xmlFree(states.states);
+ return(0);
+ }
+ states.nbstates--;
+ i = states.states[states.nbstates].step;
+ node = states.states[states.nbstates].node;
+#if 0
+ fprintf(stderr, "Pop: %d, %s\n", i, node->name);
+#endif
+ goto restart;
+}
+
+/**
+ * xsltTestCompMatchList:
+ * @ctxt: a XSLT process context
+ * @node: a node
+ * @comp: the precompiled pattern list
+ *
+ * Test whether the node matches one of the patterns in the list
+ *
+ * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
+ */
+int
+xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xsltCompMatchPtr comp) {
+ int ret;
+
+ if ((ctxt == NULL) || (node == NULL))
+ return(-1);
+ while (comp != NULL) {
+ ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL);
+ if (ret == 1)
+ return(1);
+ comp = comp->next;
+ }
+ return(0);
+}
+
+/************************************************************************
+ * *
+ * Dedicated parser for templates *
+ * *
+ ************************************************************************/
+
+#define CUR (*ctxt->cur)
+#define SKIP(val) ctxt->cur += (val)
+#define NXT(val) ctxt->cur[(val)]
+#define CUR_PTR ctxt->cur
+
+#define SKIP_BLANKS \
+ while (IS_BLANK_CH(CUR)) NEXT
+
+#define CURRENT (*ctxt->cur)
+#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
+
+
+#define PUSH(op, val, val2, novar) \
+ if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto error;
+
+#define SWAP() \
+ xsltSwapTopCompMatch(ctxt->comp);
+
+#define XSLT_ERROR(X) \
+ { xsltError(ctxt, __FILE__, __LINE__, X); \
+ ctxt->error = (X); return; }
+
+#define XSLT_ERROR0(X) \
+ { xsltError(ctxt, __FILE__, __LINE__, X); \
+ ctxt->error = (X); return(0); }
+
+/**
+ * xsltScanLiteral:
+ * @ctxt: the XPath Parser context
+ *
+ * Parse an XPath Litteral:
+ *
+ * [29] Literal ::= '"' [^"]* '"'
+ * | "'" [^']* "'"
+ *
+ * Returns the Literal parsed or NULL
+ */
+
+static xmlChar *
+xsltScanLiteral(xsltParserContextPtr ctxt) {
+ const xmlChar *q, *cur;
+ xmlChar *ret = NULL;
+ int val, len;
+
+ SKIP_BLANKS;
+ if (CUR == '"') {
+ NEXT;
+ cur = q = CUR_PTR;
+ val = xmlStringCurrentChar(NULL, cur, &len);
+ while ((IS_CHAR(val)) && (val != '"')) {
+ cur += len;
+ val = xmlStringCurrentChar(NULL, cur, &len);
+ }
+ if (!IS_CHAR(val)) {
+ ctxt->error = 1;
+ return(NULL);
+ } else {
+ ret = xmlStrndup(q, cur - q);
+ }
+ cur += len;
+ CUR_PTR = cur;
+ } else if (CUR == '\'') {
+ NEXT;
+ cur = q = CUR_PTR;
+ val = xmlStringCurrentChar(NULL, cur, &len);
+ while ((IS_CHAR(val)) && (val != '\'')) {
+ cur += len;
+ val = xmlStringCurrentChar(NULL, cur, &len);
+ }
+ if (!IS_CHAR(val)) {
+ ctxt->error = 1;
+ return(NULL);
+ } else {
+ ret = xmlStrndup(q, cur - q);
+ }
+ cur += len;
+ CUR_PTR = cur;
+ } else {
+ /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
+ ctxt->error = 1;
+ return(NULL);
+ }
+ return(ret);
+}
+
+/**
+ * xsltScanNCName:
+ * @ctxt: the XPath Parser context
+ *
+ * Parses a non qualified name
+ *
+ * Returns the Name parsed or NULL
+ */
+
+static xmlChar *
+xsltScanNCName(xsltParserContextPtr ctxt) {
+ const xmlChar *q, *cur;
+ xmlChar *ret = NULL;
+ int val, len;
+
+ SKIP_BLANKS;
+
+ cur = q = CUR_PTR;
+ val = xmlStringCurrentChar(NULL, cur, &len);
+ if (!IS_LETTER(val) && (val != '_'))
+ return(NULL);
+
+ while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
+ (val == '.') || (val == '-') ||
+ (val == '_') ||
+ (IS_COMBINING(val)) ||
+ (IS_EXTENDER(val))) {
+ cur += len;
+ val = xmlStringCurrentChar(NULL, cur, &len);
+ }
+ ret = xmlStrndup(q, cur - q);
+ CUR_PTR = cur;
+ return(ret);
+}
+
+/*
+ * xsltCompileIdKeyPattern:
+ * @ctxt: the compilation context
+ * @name: a preparsed name
+ * @aid: whether id/key are allowed there
+ * @novar: flag to prohibit xslt var
+ *
+ * Compile the XSLT LocationIdKeyPattern
+ * [3] IdKeyPattern ::= 'id' '(' Literal ')'
+ * | 'key' '(' Literal ',' Literal ')'
+ *
+ * also handle NodeType and PI from:
+ *
+ * [7] NodeTest ::= NameTest
+ * | NodeType '(' ')'
+ * | 'processing-instruction' '(' Literal ')'
+ */
+static void
+xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name,
+ int aid, int novar, xsltAxis axis) {
+ xmlChar *lit = NULL;
+ xmlChar *lit2 = NULL;
+
+ if (CUR != '(') {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : ( expected\n");
+ ctxt->error = 1;
+ return;
+ }
+ if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
+ if (axis != 0) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : NodeTest expected\n");
+ ctxt->error = 1;
+ return;
+ }
+ NEXT;
+ SKIP_BLANKS;
+ lit = xsltScanLiteral(ctxt);
+ if (ctxt->error) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : Literal expected\n");
+ return;
+ }
+ SKIP_BLANKS;
+ if (CUR != ')') {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : ) expected\n");
+ xmlFree(lit);
+ ctxt->error = 1;
+ return;
+ }
+ NEXT;
+ PUSH(XSLT_OP_ID, lit, NULL, novar);
+ lit = NULL;
+ } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
+ if (axis != 0) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : NodeTest expected\n");
+ ctxt->error = 1;
+ return;
+ }
+ NEXT;
+ SKIP_BLANKS;
+ lit = xsltScanLiteral(ctxt);
+ if (ctxt->error) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : Literal expected\n");
+ return;
+ }
+ SKIP_BLANKS;
+ if (CUR != ',') {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : , expected\n");
+ xmlFree(lit);
+ ctxt->error = 1;
+ return;
+ }
+ NEXT;
+ SKIP_BLANKS;
+ lit2 = xsltScanLiteral(ctxt);
+ if (ctxt->error) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : Literal expected\n");
+ xmlFree(lit);
+ return;
+ }
+ SKIP_BLANKS;
+ if (CUR != ')') {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : ) expected\n");
+ xmlFree(lit);
+ xmlFree(lit2);
+ ctxt->error = 1;
+ return;
+ }
+ NEXT;
+ /* URGENT TODO: support namespace in keys */
+ PUSH(XSLT_OP_KEY, lit, lit2, novar);
+ lit = NULL;
+ lit2 = NULL;
+ } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
+ NEXT;
+ SKIP_BLANKS;
+ if (CUR != ')') {
+ lit = xsltScanLiteral(ctxt);
+ if (ctxt->error) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : Literal expected\n");
+ return;
+ }
+ SKIP_BLANKS;
+ if (CUR != ')') {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : ) expected\n");
+ ctxt->error = 1;
+ return;
+ }
+ }
+ NEXT;
+ PUSH(XSLT_OP_PI, lit, NULL, novar);
+ lit = NULL;
+ } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
+ NEXT;
+ SKIP_BLANKS;
+ if (CUR != ')') {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : ) expected\n");
+ ctxt->error = 1;
+ return;
+ }
+ NEXT;
+ PUSH(XSLT_OP_TEXT, NULL, NULL, novar);
+ } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
+ NEXT;
+ SKIP_BLANKS;
+ if (CUR != ')') {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : ) expected\n");
+ ctxt->error = 1;
+ return;
+ }
+ NEXT;
+ PUSH(XSLT_OP_COMMENT, NULL, NULL, novar);
+ } else if (xmlStrEqual(name, (const xmlChar *)"node")) {
+ NEXT;
+ SKIP_BLANKS;
+ if (CUR != ')') {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : ) expected\n");
+ ctxt->error = 1;
+ return;
+ }
+ NEXT;
+ if (axis == AXIS_ATTRIBUTE) {
+ PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
+ }
+ else {
+ PUSH(XSLT_OP_NODE, NULL, NULL, novar);
+ }
+ } else if (aid) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
+ ctxt->error = 1;
+ return;
+ } else {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : node type\n");
+ ctxt->error = 1;
+ return;
+ }
+error:
+ return;
+}
+
+/**
+ * xsltCompileStepPattern:
+ * @ctxt: the compilation context
+ * @token: a posible precompiled name
+ * @novar: flag to prohibit xslt variables from pattern
+ *
+ * Compile the XSLT StepPattern and generates a precompiled
+ * form suitable for fast matching.
+ *
+ * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
+ * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
+ * | ('child' | 'attribute') '::'
+ * from XPath
+ * [7] NodeTest ::= NameTest
+ * | NodeType '(' ')'
+ * | 'processing-instruction' '(' Literal ')'
+ * [8] Predicate ::= '[' PredicateExpr ']'
+ * [9] PredicateExpr ::= Expr
+ * [13] AbbreviatedAxisSpecifier ::= '@'?
+ * [37] NameTest ::= '*' | NCName ':' '*' | QName
+ */
+
+static void
+xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
+ xmlChar *name = NULL;
+ const xmlChar *URI = NULL;
+ xmlChar *URL = NULL;
+ int level;
+ xsltAxis axis = 0;
+
+ SKIP_BLANKS;
+ if ((token == NULL) && (CUR == '@')) {
+ NEXT;
+ axis = AXIS_ATTRIBUTE;
+ }
+parse_node_test:
+ if (token == NULL)
+ token = xsltScanNCName(ctxt);
+ if (token == NULL) {
+ if (CUR == '*') {
+ NEXT;
+ if (axis == AXIS_ATTRIBUTE) {
+ PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
+ }
+ else {
+ PUSH(XSLT_OP_ALL, NULL, NULL, novar);
+ }
+ goto parse_predicate;
+ } else {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileStepPattern : Name expected\n");
+ ctxt->error = 1;
+ goto error;
+ }
+ }
+
+
+ SKIP_BLANKS;
+ if (CUR == '(') {
+ xsltCompileIdKeyPattern(ctxt, token, 0, novar, axis);
+ xmlFree(token);
+ token = NULL;
+ if (ctxt->error)
+ goto error;
+ } else if (CUR == ':') {
+ NEXT;
+ if (CUR != ':') {
+ xmlChar *prefix = token;
+ xmlNsPtr ns;
+
+ /*
+ * This is a namespace match
+ */
+ token = xsltScanNCName(ctxt);
+ ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
+ if (ns == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileStepPattern : no namespace bound to prefix %s\n",
+ prefix);
+ xmlFree(prefix);
+ prefix=NULL;
+ ctxt->error = 1;
+ goto error;
+ } else {
+ URL = xmlStrdup(ns->href);
+ }
+ xmlFree(prefix);
+ prefix=NULL;
+ if (token == NULL) {
+ if (CUR == '*') {
+ NEXT;
+ if (axis == AXIS_ATTRIBUTE) {
+ PUSH(XSLT_OP_ATTR, NULL, URL, novar);
+ URL = NULL;
+ }
+ else {
+ PUSH(XSLT_OP_NS, URL, NULL, novar);
+ URL = NULL;
+ }
+ } else {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileStepPattern : Name expected\n");
+ ctxt->error = 1;
+ goto error;
+ }
+ } else {
+ if (axis == AXIS_ATTRIBUTE) {
+ PUSH(XSLT_OP_ATTR, token, URL, novar);
+ token = NULL;
+ URL = NULL;
+ }
+ else {
+ PUSH(XSLT_OP_ELEM, token, URL, novar);
+ token = NULL;
+ URL = NULL;
+ }
+ }
+ } else {
+ if (axis != 0) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileStepPattern : NodeTest expected\n");
+ ctxt->error = 1;
+ goto error;
+ }
+ NEXT;
+ if (xmlStrEqual(token, (const xmlChar *) "child")) {
+ axis = AXIS_CHILD;
+ } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
+ axis = AXIS_ATTRIBUTE;
+ } else {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
+ ctxt->error = 1;
+ goto error;
+ }
+ xmlFree(token);
+ token = NULL;
+ SKIP_BLANKS;
+ token = xsltScanNCName(ctxt);
+ goto parse_node_test;
+ }
+ } else {
+ URI = xsltGetQNameURI(ctxt->elem, &token);
+ if (token == NULL) {
+ ctxt->error = 1;
+ goto error;
+ }
+ if (URI != NULL)
+ URL = xmlStrdup(URI);
+ if (axis == AXIS_ATTRIBUTE) {
+ PUSH(XSLT_OP_ATTR, token, URL, novar);
+ token = NULL;
+ URL = NULL;
+ }
+ else {
+ PUSH(XSLT_OP_ELEM, token, URL, novar);
+ token = NULL;
+ URL = NULL;
+ }
+ }
+parse_predicate:
+ SKIP_BLANKS;
+ level = 0;
+ while (CUR == '[') {
+ const xmlChar *q;
+ xmlChar *ret = NULL;
+
+ level++;
+ NEXT;
+ q = CUR_PTR;
+ while (CUR != 0) {
+ /* Skip over nested predicates */
+ if (CUR == '[')
+ level++;
+ else if (CUR == ']') {
+ level--;
+ if (level == 0)
+ break;
+ } else if (CUR == '"') {
+ NEXT;
+ while ((CUR != 0) && (CUR != '"'))
+ NEXT;
+ } else if (CUR == '\'') {
+ NEXT;
+ while ((CUR != 0) && (CUR != '\''))
+ NEXT;
+ }
+ NEXT;
+ }
+ if (CUR == 0) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileStepPattern : ']' expected\n");
+ ctxt->error = 1;
+ return;
+ }
+ ret = xmlStrndup(q, CUR_PTR - q);
+ PUSH(XSLT_OP_PREDICATE, ret, NULL, novar);
+ ret = NULL;
+ /* push the predicate lower than local test */
+ SWAP();
+ NEXT;
+ SKIP_BLANKS;
+ }
+ return;
+error:
+ if (token != NULL)
+ xmlFree(token);
+ if (name != NULL)
+ xmlFree(name);
+}
+
+/**
+ * xsltCompileRelativePathPattern:
+ * @comp: the compilation context
+ * @token: a posible precompiled name
+ * @novar: flag to prohibit xslt variables
+ *
+ * Compile the XSLT RelativePathPattern and generates a precompiled
+ * form suitable for fast matching.
+ *
+ * [4] RelativePathPattern ::= StepPattern
+ * | RelativePathPattern '/' StepPattern
+ * | RelativePathPattern '//' StepPattern
+ */
+static void
+xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
+ xsltCompileStepPattern(ctxt, token, novar);
+ if (ctxt->error)
+ goto error;
+ SKIP_BLANKS;
+ while ((CUR != 0) && (CUR != '|')) {
+ if ((CUR == '/') && (NXT(1) == '/')) {
+ PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
+ NEXT;
+ NEXT;
+ SKIP_BLANKS;
+ xsltCompileStepPattern(ctxt, NULL, novar);
+ } else if (CUR == '/') {
+ PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
+ NEXT;
+ SKIP_BLANKS;
+ if ((CUR != 0) && (CUR != '|')) {
+ xsltCompileRelativePathPattern(ctxt, NULL, novar);
+ }
+ } else {
+ ctxt->error = 1;
+ }
+ if (ctxt->error)
+ goto error;
+ SKIP_BLANKS;
+ }
+error:
+ return;
+}
+
+/**
+ * xsltCompileLocationPathPattern:
+ * @ctxt: the compilation context
+ * @novar: flag to prohibit xslt variables
+ *
+ * Compile the XSLT LocationPathPattern and generates a precompiled
+ * form suitable for fast matching.
+ *
+ * [2] LocationPathPattern ::= '/' RelativePathPattern?
+ * | IdKeyPattern (('/' | '//') RelativePathPattern)?
+ * | '//'? RelativePathPattern
+ */
+static void
+xsltCompileLocationPathPattern(xsltParserContextPtr ctxt, int novar) {
+ SKIP_BLANKS;
+ if ((CUR == '/') && (NXT(1) == '/')) {
+ /*
+ * since we reverse the query
+ * a leading // can be safely ignored
+ */
+ NEXT;
+ NEXT;
+ ctxt->comp->priority = 0.5; /* '//' means not 0 priority */
+ xsltCompileRelativePathPattern(ctxt, NULL, novar);
+ } else if (CUR == '/') {
+ /*
+ * We need to find root as the parent
+ */
+ NEXT;
+ SKIP_BLANKS;
+ PUSH(XSLT_OP_ROOT, NULL, NULL, novar);
+ if ((CUR != 0) && (CUR != '|')) {
+ PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
+ xsltCompileRelativePathPattern(ctxt, NULL, novar);
+ }
+ } else if (CUR == '*') {
+ xsltCompileRelativePathPattern(ctxt, NULL, novar);
+ } else if (CUR == '@') {
+ xsltCompileRelativePathPattern(ctxt, NULL, novar);
+ } else {
+ xmlChar *name;
+ name = xsltScanNCName(ctxt);
+ if (name == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileLocationPathPattern : Name expected\n");
+ ctxt->error = 1;
+ return;
+ }
+ SKIP_BLANKS;
+ if ((CUR == '(') && !xmlXPathIsNodeType(name)) {
+ xsltCompileIdKeyPattern(ctxt, name, 1, novar, 0);
+ xmlFree(name);
+ name = NULL;
+ if ((CUR == '/') && (NXT(1) == '/')) {
+ PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
+ NEXT;
+ NEXT;
+ SKIP_BLANKS;
+ xsltCompileRelativePathPattern(ctxt, NULL, novar);
+ } else if (CUR == '/') {
+ PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
+ NEXT;
+ SKIP_BLANKS;
+ xsltCompileRelativePathPattern(ctxt, NULL, novar);
+ }
+ return;
+ }
+ xsltCompileRelativePathPattern(ctxt, name, novar);
+ }
+error:
+ return;
+}
+
+/**
+ * xsltCompilePatternInternal:
+ * @pattern: an XSLT pattern
+ * @doc: the containing document
+ * @node: the containing element
+ * @style: the stylesheet
+ * @runtime: the transformation context, if done at run-time
+ * @novar: flag to prohibit xslt variables
+ *
+ * Compile the XSLT pattern and generates a list of precompiled form suitable
+ * for fast matching.
+ *
+ * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
+ *
+ * Returns the generated pattern list or NULL in case of failure
+ */
+
+static xsltCompMatchPtr
+xsltCompilePatternInternal(const xmlChar *pattern, xmlDocPtr doc,
+ xmlNodePtr node, xsltStylesheetPtr style,
+ xsltTransformContextPtr runtime, int novar) {
+ xsltParserContextPtr ctxt = NULL;
+ xsltCompMatchPtr element, first = NULL, previous = NULL;
+ int current, start, end, level, j;
+
+ if (pattern == NULL) {
+ xsltTransformError(NULL, NULL, node,
+ "xsltCompilePattern : NULL pattern\n");
+ return(NULL);
+ }
+
+ ctxt = xsltNewParserContext(style, runtime);
+ if (ctxt == NULL)
+ return(NULL);
+ ctxt->doc = doc;
+ ctxt->elem = node;
+ current = end = 0;
+ while (pattern[current] != 0) {
+ start = current;
+ while (IS_BLANK_CH(pattern[current]))
+ current++;
+ end = current;
+ level = 0;
+ while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) {
+ if (pattern[end] == '[')
+ level++;
+ else if (pattern[end] == ']')
+ level--;
+ else if (pattern[end] == '\'') {
+ end++;
+ while ((pattern[end] != 0) && (pattern[end] != '\''))
+ end++;
+ } else if (pattern[end] == '"') {
+ end++;
+ while ((pattern[end] != 0) && (pattern[end] != '"'))
+ end++;
+ }
+ if (pattern[end] == 0)
+ break;
+ end++;
+ }
+ if (current == end) {
+ xsltTransformError(NULL, NULL, node,
+ "xsltCompilePattern : NULL pattern\n");
+ goto error;
+ }
+ element = xsltNewCompMatch();
+ if (element == NULL) {
+ goto error;
+ }
+ if (first == NULL)
+ first = element;
+ else if (previous != NULL)
+ previous->next = element;
+ previous = element;
+
+ ctxt->comp = element;
+ ctxt->base = xmlStrndup(&pattern[start], end - start);
+ if (ctxt->base == NULL)
+ goto error;
+ ctxt->cur = &(ctxt->base)[current - start];
+ element->pattern = ctxt->base;
+ element->nsList = xmlGetNsList(doc, node);
+ j = 0;
+ if (element->nsList != NULL) {
+ while (element->nsList[j] != NULL)
+ j++;
+ }
+ element->nsNr = j;
+
+
+#ifdef WITH_XSLT_DEBUG_PATTERN
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCompilePattern : parsing '%s'\n",
+ element->pattern);
+#endif
+ /*
+ Preset default priority to be zero.
+ This may be changed by xsltCompileLocationPathPattern.
+ */
+ element->priority = 0;
+ xsltCompileLocationPathPattern(ctxt, novar);
+ if (ctxt->error) {
+ xsltTransformError(NULL, style, node,
+ "xsltCompilePattern : failed to compile '%s'\n",
+ element->pattern);
+ if (style != NULL) style->errors++;
+ goto error;
+ }
+
+ /*
+ * Reverse for faster interpretation.
+ */
+ xsltReverseCompMatch(ctxt, element);
+
+ /*
+ * Set-up the priority
+ */
+ if (element->priority == 0) { /* if not yet determined */
+ if (((element->steps[0].op == XSLT_OP_ELEM) ||
+ (element->steps[0].op == XSLT_OP_ATTR) ||
+ (element->steps[0].op == XSLT_OP_PI)) &&
+ (element->steps[0].value != NULL) &&
+ (element->steps[1].op == XSLT_OP_END)) {
+ ; /* previously preset */
+ } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
+ (element->steps[0].value2 != NULL) &&
+ (element->steps[1].op == XSLT_OP_END)) {
+ element->priority = -0.25;
+ } else if ((element->steps[0].op == XSLT_OP_NS) &&
+ (element->steps[0].value != NULL) &&
+ (element->steps[1].op == XSLT_OP_END)) {
+ element->priority = -0.25;
+ } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
+ (element->steps[0].value == NULL) &&
+ (element->steps[0].value2 == NULL) &&
+ (element->steps[1].op == XSLT_OP_END)) {
+ element->priority = -0.5;
+ } else if (((element->steps[0].op == XSLT_OP_PI) ||
+ (element->steps[0].op == XSLT_OP_TEXT) ||
+ (element->steps[0].op == XSLT_OP_ALL) ||
+ (element->steps[0].op == XSLT_OP_NODE) ||
+ (element->steps[0].op == XSLT_OP_COMMENT)) &&
+ (element->steps[1].op == XSLT_OP_END)) {
+ element->priority = -0.5;
+ } else {
+ element->priority = 0.5;
+ }
+ }
+#ifdef WITH_XSLT_DEBUG_PATTERN
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCompilePattern : parsed %s, default priority %f\n",
+ element->pattern, element->priority);
+#endif
+ if (pattern[end] == '|')
+ end++;
+ current = end;
+ }
+ if (end == 0) {
+ xsltTransformError(NULL, style, node,
+ "xsltCompilePattern : NULL pattern\n");
+ if (style != NULL) style->errors++;
+ goto error;
+ }
+
+ xsltFreeParserContext(ctxt);
+ return(first);
+
+error:
+ if (ctxt != NULL)
+ xsltFreeParserContext(ctxt);
+ if (first != NULL)
+ xsltFreeCompMatchList(first);
+ return(NULL);
+}
+
+/**
+ * xsltCompilePattern:
+ * @pattern: an XSLT pattern
+ * @doc: the containing document
+ * @node: the containing element
+ * @style: the stylesheet
+ * @runtime: the transformation context, if done at run-time
+ *
+ * Compile the XSLT pattern and generates a list of precompiled form suitable
+ * for fast matching.
+ *
+ * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
+ *
+ * Returns the generated pattern list or NULL in case of failure
+ */
+
+xsltCompMatchPtr
+xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc,
+ xmlNodePtr node, xsltStylesheetPtr style,
+ xsltTransformContextPtr runtime) {
+ return (xsltCompilePatternInternal(pattern, doc, node, style, runtime, 0));
+}
+
+/************************************************************************
+ * *
+ * Module interfaces *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltAddTemplate:
+ * @style: an XSLT stylesheet
+ * @cur: an XSLT template
+ * @mode: the mode name or NULL
+ * @modeURI: the mode URI or NULL
+ *
+ * Register the XSLT pattern associated to @cur
+ *
+ * Returns -1 in case of error, 0 otherwise
+ */
+int
+xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
+ const xmlChar *mode, const xmlChar *modeURI) {
+ xsltCompMatchPtr pat, list, next;
+ /*
+ * 'top' will point to style->xxxMatch ptr - declaring as 'void'
+ * avoids gcc 'type-punned pointer' warning.
+ */
+ void **top = NULL;
+ const xmlChar *name = NULL;
+ float priority; /* the priority */
+
+ if ((style == NULL) || (cur == NULL))
+ return(-1);
+
+ /* Register named template */
+ if (cur->name != NULL) {
+ if (style->namedTemplates == NULL) {
+ style->namedTemplates = xmlHashCreate(10);
+ if (style->namedTemplates == NULL)
+ return(-1);
+ }
+ else {
+ void *dup = xmlHashLookup2(style->namedTemplates, cur->name,
+ cur->nameURI);
+ if (dup != NULL) {
+ xsltTransformError(NULL, style, cur->elem,
+ "xsl:template: error duplicate name '%s'\n",
+ cur->name);
+ style->errors++;
+ return(-1);
+ }
+ }
+
+ xmlHashAddEntry2(style->namedTemplates, cur->name, cur->nameURI, cur);
+ }
+
+ if (cur->match == NULL)
+ return(0);
+
+ priority = cur->priority;
+ pat = xsltCompilePatternInternal(cur->match, style->doc, cur->elem,
+ style, NULL, 1);
+ if (pat == NULL)
+ return(-1);
+ while (pat) {
+ next = pat->next;
+ pat->next = NULL;
+ name = NULL;
+
+ pat->template = cur;
+ if (mode != NULL)
+ pat->mode = xmlDictLookup(style->dict, mode, -1);
+ if (modeURI != NULL)
+ pat->modeURI = xmlDictLookup(style->dict, modeURI, -1);
+ if (priority != XSLT_PAT_NO_PRIORITY)
+ pat->priority = priority;
+
+ /*
+ * insert it in the hash table list corresponding to its lookup name
+ */
+ switch (pat->steps[0].op) {
+ case XSLT_OP_ATTR:
+ if (pat->steps[0].value != NULL)
+ name = pat->steps[0].value;
+ else
+ top = &(style->attrMatch);
+ break;
+ case XSLT_OP_PARENT:
+ case XSLT_OP_ANCESTOR:
+ top = &(style->elemMatch);
+ break;
+ case XSLT_OP_ROOT:
+ top = &(style->rootMatch);
+ break;
+ case XSLT_OP_KEY:
+ top = &(style->keyMatch);
+ break;
+ case XSLT_OP_ID:
+ /* TODO optimize ID !!! */
+ case XSLT_OP_NS:
+ case XSLT_OP_ALL:
+ top = &(style->elemMatch);
+ break;
+ case XSLT_OP_END:
+ case XSLT_OP_PREDICATE:
+ xsltTransformError(NULL, style, NULL,
+ "xsltAddTemplate: invalid compiled pattern\n");
+ xsltFreeCompMatch(pat);
+ return(-1);
+ /*
+ * TODO: some flags at the top level about type based patterns
+ * would be faster than inclusion in the hash table.
+ */
+ case XSLT_OP_PI:
+ if (pat->steps[0].value != NULL)
+ name = pat->steps[0].value;
+ else
+ top = &(style->piMatch);
+ break;
+ case XSLT_OP_COMMENT:
+ top = &(style->commentMatch);
+ break;
+ case XSLT_OP_TEXT:
+ top = &(style->textMatch);
+ break;
+ case XSLT_OP_ELEM:
+ case XSLT_OP_NODE:
+ if (pat->steps[0].value != NULL)
+ name = pat->steps[0].value;
+ else
+ top = &(style->elemMatch);
+ break;
+ }
+ if (name != NULL) {
+ if (style->templatesHash == NULL) {
+ style->templatesHash = xmlHashCreate(1024);
+ if (style->templatesHash == NULL) {
+ xsltFreeCompMatch(pat);
+ return(-1);
+ }
+ xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat);
+ } else {
+ list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
+ name, mode, modeURI);
+ if (list == NULL) {
+ xmlHashAddEntry3(style->templatesHash, name,
+ mode, modeURI, pat);
+ } else {
+ /*
+ * Note '<=' since one must choose among the matching
+ * template rules that are left, the one that occurs
+ * last in the stylesheet
+ */
+ if (list->priority <= pat->priority) {
+ pat->next = list;
+ xmlHashUpdateEntry3(style->templatesHash, name,
+ mode, modeURI, pat, NULL);
+ } else {
+ while (list->next != NULL) {
+ if (list->next->priority <= pat->priority)
+ break;
+ list = list->next;
+ }
+ pat->next = list->next;
+ list->next = pat;
+ }
+ }
+ }
+ } else if (top != NULL) {
+ list = *top;
+ if (list == NULL) {
+ *top = pat;
+ pat->next = NULL;
+ } else if (list->priority <= pat->priority) {
+ pat->next = list;
+ *top = pat;
+ } else {
+ while (list->next != NULL) {
+ if (list->next->priority <= pat->priority)
+ break;
+ list = list->next;
+ }
+ pat->next = list->next;
+ list->next = pat;
+ }
+ } else {
+ xsltTransformError(NULL, style, NULL,
+ "xsltAddTemplate: invalid compiled pattern\n");
+ xsltFreeCompMatch(pat);
+ return(-1);
+ }
+#ifdef WITH_XSLT_DEBUG_PATTERN
+ if (mode)
+ xsltGenericDebug(xsltGenericDebugContext,
+ "added pattern : '%s' mode '%s' priority %f\n",
+ pat->pattern, pat->mode, pat->priority);
+ else
+ xsltGenericDebug(xsltGenericDebugContext,
+ "added pattern : '%s' priority %f\n",
+ pat->pattern, pat->priority);
+#endif
+
+ pat = next;
+ }
+ return(0);
+}
+
+static int
+xsltComputeAllKeys(xsltTransformContextPtr ctxt, xmlNodePtr contextNode)
+{
+ if ((ctxt == NULL) || (contextNode == NULL)) {
+ xsltTransformError(ctxt, NULL, ctxt->inst,
+ "Internal error in xsltComputeAllKeys(): "
+ "Bad arguments.\n");
+ return(-1);
+ }
+
+ if (ctxt->document == NULL) {
+ /*
+ * The document info will only be NULL if we have a RTF.
+ */
+ if (contextNode->doc->_private != NULL)
+ goto doc_info_mismatch;
+ /*
+ * On-demand creation of the document info (needed for keys).
+ */
+ ctxt->document = xsltNewDocument(ctxt, contextNode->doc);
+ if (ctxt->document == NULL)
+ return(-1);
+ }
+ return xsltInitAllDocKeys(ctxt);
+
+doc_info_mismatch:
+ xsltTransformError(ctxt, NULL, ctxt->inst,
+ "Internal error in xsltComputeAllKeys(): "
+ "The context's document info doesn't match the "
+ "document info of the current result tree.\n");
+ ctxt->state = XSLT_STATE_STOPPED;
+ return(-1);
+}
+
+/**
+ * xsltGetTemplate:
+ * @ctxt: a XSLT process context
+ * @node: the node being processed
+ * @style: the current style
+ *
+ * Finds the template applying to this node, if @style is non-NULL
+ * it means one needs to look for the next imported template in scope.
+ *
+ * Returns the xsltTemplatePtr or NULL if not found
+ */
+xsltTemplatePtr
+xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xsltStylesheetPtr style)
+{
+ xsltStylesheetPtr curstyle;
+ xsltTemplatePtr ret = NULL;
+ const xmlChar *name = NULL;
+ xsltCompMatchPtr list = NULL;
+ float priority;
+ int keyed = 0;
+
+ if ((ctxt == NULL) || (node == NULL))
+ return(NULL);
+
+ if (style == NULL) {
+ curstyle = ctxt->style;
+ } else {
+ curstyle = xsltNextImport(style);
+ }
+
+ while ((curstyle != NULL) && (curstyle != style)) {
+ priority = XSLT_PAT_NO_PRIORITY;
+ /* TODO : handle IDs/keys here ! */
+ if (curstyle->templatesHash != NULL) {
+ /*
+ * Use the top name as selector
+ */
+ switch (node->type) {
+ case XML_ELEMENT_NODE:
+ if (node->name[0] == ' ')
+ break;
+ case XML_ATTRIBUTE_NODE:
+ case XML_PI_NODE:
+ name = node->name;
+ break;
+ case XML_DOCUMENT_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+ case XML_TEXT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_COMMENT_NODE:
+ case XML_ENTITY_REF_NODE:
+ case XML_ENTITY_NODE:
+ case XML_DOCUMENT_TYPE_NODE:
+ case XML_DOCUMENT_FRAG_NODE:
+ case XML_NOTATION_NODE:
+ case XML_DTD_NODE:
+ case XML_ELEMENT_DECL:
+ case XML_ATTRIBUTE_DECL:
+ case XML_ENTITY_DECL:
+ case XML_NAMESPACE_DECL:
+ case XML_XINCLUDE_START:
+ case XML_XINCLUDE_END:
+ break;
+ default:
+ return(NULL);
+
+ }
+ }
+ if (name != NULL) {
+ /*
+ * find the list of applicable expressions based on the name
+ */
+ list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash,
+ name, ctxt->mode, ctxt->modeURI);
+ } else
+ list = NULL;
+ while (list != NULL) {
+ if (xsltTestCompMatch(ctxt, list, node,
+ ctxt->mode, ctxt->modeURI)) {
+ ret = list->template;
+ priority = list->priority;
+ break;
+ }
+ list = list->next;
+ }
+ list = NULL;
+
+ /*
+ * find alternate generic matches
+ */
+ switch (node->type) {
+ case XML_ELEMENT_NODE:
+ if (node->name[0] == ' ')
+ list = curstyle->rootMatch;
+ else
+ list = curstyle->elemMatch;
+ if (node->psvi != NULL) keyed = 1;
+ break;
+ case XML_ATTRIBUTE_NODE: {
+ xmlAttrPtr attr;
+
+ list = curstyle->attrMatch;
+ attr = (xmlAttrPtr) node;
+ if (attr->psvi != NULL) keyed = 1;
+ break;
+ }
+ case XML_PI_NODE:
+ list = curstyle->piMatch;
+ if (node->psvi != NULL) keyed = 1;
+ break;
+ case XML_DOCUMENT_NODE:
+ case XML_HTML_DOCUMENT_NODE: {
+ xmlDocPtr doc;
+
+ list = curstyle->rootMatch;
+ doc = (xmlDocPtr) node;
+ if (doc->psvi != NULL) keyed = 1;
+ break;
+ }
+ case XML_TEXT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ list = curstyle->textMatch;
+ if (node->psvi != NULL) keyed = 1;
+ break;
+ case XML_COMMENT_NODE:
+ list = curstyle->commentMatch;
+ if (node->psvi != NULL) keyed = 1;
+ break;
+ case XML_ENTITY_REF_NODE:
+ case XML_ENTITY_NODE:
+ case XML_DOCUMENT_TYPE_NODE:
+ case XML_DOCUMENT_FRAG_NODE:
+ case XML_NOTATION_NODE:
+ case XML_DTD_NODE:
+ case XML_ELEMENT_DECL:
+ case XML_ATTRIBUTE_DECL:
+ case XML_ENTITY_DECL:
+ case XML_NAMESPACE_DECL:
+ case XML_XINCLUDE_START:
+ case XML_XINCLUDE_END:
+ break;
+ default:
+ break;
+ }
+ while ((list != NULL) &&
+ ((ret == NULL) || (list->priority > priority))) {
+ if (xsltTestCompMatch(ctxt, list, node,
+ ctxt->mode, ctxt->modeURI)) {
+ ret = list->template;
+ priority = list->priority;
+ break;
+ }
+ list = list->next;
+ }
+ /*
+ * Some of the tests for elements can also apply to documents
+ */
+ if ((node->type == XML_DOCUMENT_NODE) ||
+ (node->type == XML_HTML_DOCUMENT_NODE) ||
+ (node->type == XML_TEXT_NODE)) {
+ list = curstyle->elemMatch;
+ while ((list != NULL) &&
+ ((ret == NULL) || (list->priority > priority))) {
+ if (xsltTestCompMatch(ctxt, list, node,
+ ctxt->mode, ctxt->modeURI)) {
+ ret = list->template;
+ priority = list->priority;
+ break;
+ }
+ list = list->next;
+ }
+ } else if ((node->type == XML_PI_NODE) ||
+ (node->type == XML_COMMENT_NODE)) {
+ list = curstyle->elemMatch;
+ while ((list != NULL) &&
+ ((ret == NULL) || (list->priority > priority))) {
+ if (xsltTestCompMatch(ctxt, list, node,
+ ctxt->mode, ctxt->modeURI)) {
+ ret = list->template;
+ priority = list->priority;
+ break;
+ }
+ list = list->next;
+ }
+ }
+
+keyed_match:
+ if (keyed) {
+ list = curstyle->keyMatch;
+ while ((list != NULL) &&
+ ((ret == NULL) || (list->priority > priority))) {
+ if (xsltTestCompMatch(ctxt, list, node,
+ ctxt->mode, ctxt->modeURI)) {
+ ret = list->template;
+ priority = list->priority;
+ break;
+ }
+ list = list->next;
+ }
+ }
+ else if (ctxt->hasTemplKeyPatterns &&
+ ((ctxt->document == NULL) ||
+ (ctxt->document->nbKeysComputed < ctxt->nbKeys)))
+ {
+ /*
+ * Compute all remaining keys for this document.
+ *
+ * REVISIT TODO: I think this could be further optimized.
+ */
+ if (xsltComputeAllKeys(ctxt, node) == -1)
+ goto error;
+
+ switch (node->type) {
+ case XML_ELEMENT_NODE:
+ if (node->psvi != NULL) keyed = 1;
+ break;
+ case XML_ATTRIBUTE_NODE:
+ if (((xmlAttrPtr) node)->psvi != NULL) keyed = 1;
+ break;
+ case XML_TEXT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_COMMENT_NODE:
+ case XML_PI_NODE:
+ if (node->psvi != NULL) keyed = 1;
+ break;
+ case XML_DOCUMENT_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+ if (((xmlDocPtr) node)->psvi != NULL) keyed = 1;
+ break;
+ default:
+ break;
+ }
+ if (keyed)
+ goto keyed_match;
+ }
+ if (ret != NULL)
+ return(ret);
+
+ /*
+ * Cycle on next curstylesheet import.
+ */
+ curstyle = xsltNextImport(curstyle);
+ }
+
+error:
+ return(NULL);
+}
+
+/**
+ * xsltCleanupTemplates:
+ * @style: an XSLT stylesheet
+ *
+ * Cleanup the state of the templates used by the stylesheet and
+ * the ones it imports.
+ */
+void
+xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED) {
+}
+
+/**
+ * xsltFreeTemplateHashes:
+ * @style: an XSLT stylesheet
+ *
+ * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
+ */
+void
+xsltFreeTemplateHashes(xsltStylesheetPtr style) {
+ if (style->templatesHash != NULL)
+ xmlHashFree((xmlHashTablePtr) style->templatesHash,
+ (xmlHashDeallocator) xsltFreeCompMatchList);
+ if (style->rootMatch != NULL)
+ xsltFreeCompMatchList(style->rootMatch);
+ if (style->keyMatch != NULL)
+ xsltFreeCompMatchList(style->keyMatch);
+ if (style->elemMatch != NULL)
+ xsltFreeCompMatchList(style->elemMatch);
+ if (style->attrMatch != NULL)
+ xsltFreeCompMatchList(style->attrMatch);
+ if (style->parentMatch != NULL)
+ xsltFreeCompMatchList(style->parentMatch);
+ if (style->textMatch != NULL)
+ xsltFreeCompMatchList(style->textMatch);
+ if (style->piMatch != NULL)
+ xsltFreeCompMatchList(style->piMatch);
+ if (style->commentMatch != NULL)
+ xsltFreeCompMatchList(style->commentMatch);
+ if (style->namedTemplates != NULL)
+ xmlHashFree(style->namedTemplates, NULL);
+}
+
diff --git a/libxslt/pattern.h b/libxslt/pattern.h
new file mode 100644
index 0000000..eb21be3
--- /dev/null
+++ b/libxslt/pattern.h
@@ -0,0 +1,81 @@
+/*
+ * Summary: interface for the pattern matching used in template matches.
+ * Description: the implementation of the lookup of the right template
+ * for a given node must be really fast in order to keep
+ * decent performances.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_PATTERN_H__
+#define __XML_XSLT_PATTERN_H__
+
+#include "xsltInternals.h"
+#include "xsltexports.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * xsltCompMatch:
+ *
+ * Data structure used for the implementation of patterns.
+ * It is kept private (in pattern.c).
+ */
+typedef struct _xsltCompMatch xsltCompMatch;
+typedef xsltCompMatch *xsltCompMatchPtr;
+
+/*
+ * Pattern related interfaces.
+ */
+
+XSLTPUBFUN xsltCompMatchPtr XSLTCALL
+ xsltCompilePattern (const xmlChar *pattern,
+ xmlDocPtr doc,
+ xmlNodePtr node,
+ xsltStylesheetPtr style,
+ xsltTransformContextPtr runtime);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeCompMatchList (xsltCompMatchPtr comp);
+XSLTPUBFUN int XSLTCALL
+ xsltTestCompMatchList (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xsltCompMatchPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltNormalizeCompSteps (void *payload,
+ void *data,
+ const xmlChar *name);
+
+/*
+ * Template related interfaces.
+ */
+XSLTPUBFUN int XSLTCALL
+ xsltAddTemplate (xsltStylesheetPtr style,
+ xsltTemplatePtr cur,
+ const xmlChar *mode,
+ const xmlChar *modeURI);
+XSLTPUBFUN xsltTemplatePtr XSLTCALL
+ xsltGetTemplate (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xsltStylesheetPtr style);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeTemplateHashes (xsltStylesheetPtr style);
+XSLTPUBFUN void XSLTCALL
+ xsltCleanupTemplates (xsltStylesheetPtr style);
+
+#if 0
+int xsltMatchPattern (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ const xmlChar *pattern,
+ xmlDocPtr ctxtdoc,
+ xmlNodePtr ctxtnode);
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_PATTERN_H__ */
+
diff --git a/libxslt/preproc.c b/libxslt/preproc.c
new file mode 100644
index 0000000..9a7de53
--- /dev/null
+++ b/libxslt/preproc.c
@@ -0,0 +1,2376 @@
+/*
+ * preproc.c: Preprocessing of style operations
+ *
+ * References:
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * Michael Kay "XSLT Programmer's Reference" pp 637-643
+ * Writing Multiple Output Files
+ *
+ * XSLT-1.1 Working Draft
+ * http://www.w3.org/TR/xslt11#multiple-output
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/valid.h>
+#include <libxml/hash.h>
+#include <libxml/uri.h>
+#include <libxml/encoding.h>
+#include <libxml/xmlerror.h>
+#include "xslt.h"
+#include "xsltutils.h"
+#include "xsltInternals.h"
+#include "transform.h"
+#include "templates.h"
+#include "variables.h"
+#include "numbersInternals.h"
+#include "preproc.h"
+#include "extra.h"
+#include "imports.h"
+#include "extensions.h"
+#include "pattern.h"
+
+#ifdef WITH_XSLT_DEBUG
+#define WITH_XSLT_DEBUG_PREPROC
+#endif
+
+const xmlChar *xsltExtMarker = (const xmlChar *) "Extension Element";
+
+/************************************************************************
+ * *
+ * Grammar checks *
+ * *
+ ************************************************************************/
+
+#ifdef XSLT_REFACTORED
+ /*
+ * Grammar checks are now performed in xslt.c.
+ */
+#else
+/**
+ * xsltCheckTopLevelElement:
+ * @style: the XSLT stylesheet
+ * @inst: the XSLT instruction
+ * @err: raise an error or not
+ *
+ * Check that the instruction is instanciated as a top level element.
+ *
+ * Returns -1 in case of error, 0 if failed and 1 in case of success
+ */
+static int
+xsltCheckTopLevelElement(xsltStylesheetPtr style, xmlNodePtr inst, int err) {
+ xmlNodePtr parent;
+ if ((style == NULL) || (inst == NULL) || (inst->ns == NULL))
+ return(-1);
+
+ parent = inst->parent;
+ if (parent == NULL) {
+ if (err) {
+ xsltTransformError(NULL, style, inst,
+ "internal problem: element has no parent\n");
+ style->errors++;
+ }
+ return(0);
+ }
+ if ((parent->ns == NULL) || (parent->type != XML_ELEMENT_NODE) ||
+ ((parent->ns != inst->ns) &&
+ (!xmlStrEqual(parent->ns->href, inst->ns->href))) ||
+ ((!xmlStrEqual(parent->name, BAD_CAST "stylesheet")) &&
+ (!xmlStrEqual(parent->name, BAD_CAST "transform")))) {
+ if (err) {
+ xsltTransformError(NULL, style, inst,
+ "element %s only allowed as child of stylesheet\n",
+ inst->name);
+ style->errors++;
+ }
+ return(0);
+ }
+ return(1);
+}
+
+/**
+ * xsltCheckInstructionElement:
+ * @style: the XSLT stylesheet
+ * @inst: the XSLT instruction
+ *
+ * Check that the instruction is instanciated as an instruction element.
+ */
+static void
+xsltCheckInstructionElement(xsltStylesheetPtr style, xmlNodePtr inst) {
+ xmlNodePtr parent;
+ int has_ext;
+
+ if ((style == NULL) || (inst == NULL) || (inst->ns == NULL) ||
+ (style->literal_result))
+ return;
+
+ has_ext = (style->extInfos != NULL);
+
+ parent = inst->parent;
+ if (parent == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "internal problem: element has no parent\n");
+ style->errors++;
+ return;
+ }
+ while ((parent != NULL) && (parent->type != XML_DOCUMENT_NODE)) {
+ if (((parent->ns == inst->ns) ||
+ ((parent->ns != NULL) &&
+ (xmlStrEqual(parent->ns->href, inst->ns->href)))) &&
+ ((xmlStrEqual(parent->name, BAD_CAST "template")) ||
+ (xmlStrEqual(parent->name, BAD_CAST "param")) ||
+ (xmlStrEqual(parent->name, BAD_CAST "attribute")) ||
+ (xmlStrEqual(parent->name, BAD_CAST "variable")))) {
+ return;
+ }
+
+ /*
+ * if we are within an extension element all bets are off
+ * about the semantic there e.g. xsl:param within func:function
+ */
+ if ((has_ext) && (parent->ns != NULL) &&
+ (xmlHashLookup(style->extInfos, parent->ns->href) != NULL))
+ return;
+
+ parent = parent->parent;
+ }
+ xsltTransformError(NULL, style, inst,
+ "element %s only allowed within a template, variable or param\n",
+ inst->name);
+ style->errors++;
+}
+
+/**
+ * xsltCheckParentElement:
+ * @style: the XSLT stylesheet
+ * @inst: the XSLT instruction
+ * @allow1: allowed parent1
+ * @allow2: allowed parent2
+ *
+ * Check that the instruction is instanciated as the childre of one of the
+ * possible parents.
+ */
+static void
+xsltCheckParentElement(xsltStylesheetPtr style, xmlNodePtr inst,
+ const xmlChar *allow1, const xmlChar *allow2) {
+ xmlNodePtr parent;
+
+ if ((style == NULL) || (inst == NULL) || (inst->ns == NULL) ||
+ (style->literal_result))
+ return;
+
+ parent = inst->parent;
+ if (parent == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "internal problem: element has no parent\n");
+ style->errors++;
+ return;
+ }
+ if (((parent->ns == inst->ns) ||
+ ((parent->ns != NULL) &&
+ (xmlStrEqual(parent->ns->href, inst->ns->href)))) &&
+ ((xmlStrEqual(parent->name, allow1)) ||
+ (xmlStrEqual(parent->name, allow2)))) {
+ return;
+ }
+
+ if (style->extInfos != NULL) {
+ while ((parent != NULL) && (parent->type != XML_DOCUMENT_NODE)) {
+ /*
+ * if we are within an extension element all bets are off
+ * about the semantic there e.g. xsl:param within func:function
+ */
+ if ((parent->ns != NULL) &&
+ (xmlHashLookup(style->extInfos, parent->ns->href) != NULL))
+ return;
+
+ parent = parent->parent;
+ }
+ }
+ xsltTransformError(NULL, style, inst,
+ "element %s is not allowed within that context\n",
+ inst->name);
+ style->errors++;
+}
+#endif
+
+/************************************************************************
+ * *
+ * handling of precomputed data *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltNewStylePreComp:
+ * @style: the XSLT stylesheet
+ * @type: the construct type
+ *
+ * Create a new XSLT Style precomputed block
+ *
+ * Returns the newly allocated specialized structure
+ * or NULL in case of error
+ */
+static xsltStylePreCompPtr
+xsltNewStylePreComp(xsltStylesheetPtr style, xsltStyleType type) {
+ xsltStylePreCompPtr cur;
+#ifdef XSLT_REFACTORED
+ size_t size;
+#endif
+
+ if (style == NULL)
+ return(NULL);
+
+#ifdef XSLT_REFACTORED
+ /*
+ * URGENT TODO: Use specialized factory functions in order
+ * to avoid this ugliness.
+ */
+ switch (type) {
+ case XSLT_FUNC_COPY:
+ size = sizeof(xsltStyleItemCopy); break;
+ case XSLT_FUNC_SORT:
+ size = sizeof(xsltStyleItemSort); break;
+ case XSLT_FUNC_TEXT:
+ size = sizeof(xsltStyleItemText); break;
+ case XSLT_FUNC_ELEMENT:
+ size = sizeof(xsltStyleItemElement); break;
+ case XSLT_FUNC_ATTRIBUTE:
+ size = sizeof(xsltStyleItemAttribute); break;
+ case XSLT_FUNC_COMMENT:
+ size = sizeof(xsltStyleItemComment); break;
+ case XSLT_FUNC_PI:
+ size = sizeof(xsltStyleItemPI); break;
+ case XSLT_FUNC_COPYOF:
+ size = sizeof(xsltStyleItemCopyOf); break;
+ case XSLT_FUNC_VALUEOF:
+ size = sizeof(xsltStyleItemValueOf); break;;
+ case XSLT_FUNC_NUMBER:
+ size = sizeof(xsltStyleItemNumber); break;
+ case XSLT_FUNC_APPLYIMPORTS:
+ size = sizeof(xsltStyleItemApplyImports); break;
+ case XSLT_FUNC_CALLTEMPLATE:
+ size = sizeof(xsltStyleItemCallTemplate); break;
+ case XSLT_FUNC_APPLYTEMPLATES:
+ size = sizeof(xsltStyleItemApplyTemplates); break;
+ case XSLT_FUNC_CHOOSE:
+ size = sizeof(xsltStyleItemChoose); break;
+ case XSLT_FUNC_IF:
+ size = sizeof(xsltStyleItemIf); break;
+ case XSLT_FUNC_FOREACH:
+ size = sizeof(xsltStyleItemForEach); break;
+ case XSLT_FUNC_DOCUMENT:
+ size = sizeof(xsltStyleItemDocument); break;
+ case XSLT_FUNC_WITHPARAM:
+ size = sizeof(xsltStyleItemWithParam); break;
+ case XSLT_FUNC_PARAM:
+ size = sizeof(xsltStyleItemParam); break;
+ case XSLT_FUNC_VARIABLE:
+ size = sizeof(xsltStyleItemVariable); break;
+ case XSLT_FUNC_WHEN:
+ size = sizeof(xsltStyleItemWhen); break;
+ case XSLT_FUNC_OTHERWISE:
+ size = sizeof(xsltStyleItemOtherwise); break;
+ default:
+ xsltTransformError(NULL, style, NULL,
+ "xsltNewStylePreComp : invalid type %d\n", type);
+ style->errors++;
+ return(NULL);
+ }
+ /*
+ * Create the structure.
+ */
+ cur = (xsltStylePreCompPtr) xmlMalloc(size);
+ if (cur == NULL) {
+ xsltTransformError(NULL, style, NULL,
+ "xsltNewStylePreComp : malloc failed\n");
+ style->errors++;
+ return(NULL);
+ }
+ memset(cur, 0, size);
+
+#else /* XSLT_REFACTORED */
+ /*
+ * Old behaviour.
+ */
+ cur = (xsltStylePreCompPtr) xmlMalloc(sizeof(xsltStylePreComp));
+ if (cur == NULL) {
+ xsltTransformError(NULL, style, NULL,
+ "xsltNewStylePreComp : malloc failed\n");
+ style->errors++;
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltStylePreComp));
+#endif /* XSLT_REFACTORED */
+
+ /*
+ * URGENT TODO: Better to move this to spezialized factory functions.
+ */
+ cur->type = type;
+ switch (cur->type) {
+ case XSLT_FUNC_COPY:
+ cur->func = (xsltTransformFunction) xsltCopy;break;
+ case XSLT_FUNC_SORT:
+ cur->func = (xsltTransformFunction) xsltSort;break;
+ case XSLT_FUNC_TEXT:
+ cur->func = (xsltTransformFunction) xsltText;break;
+ case XSLT_FUNC_ELEMENT:
+ cur->func = (xsltTransformFunction) xsltElement;break;
+ case XSLT_FUNC_ATTRIBUTE:
+ cur->func = (xsltTransformFunction) xsltAttribute;break;
+ case XSLT_FUNC_COMMENT:
+ cur->func = (xsltTransformFunction) xsltComment;break;
+ case XSLT_FUNC_PI:
+ cur->func = (xsltTransformFunction) xsltProcessingInstruction;
+ break;
+ case XSLT_FUNC_COPYOF:
+ cur->func = (xsltTransformFunction) xsltCopyOf;break;
+ case XSLT_FUNC_VALUEOF:
+ cur->func = (xsltTransformFunction) xsltValueOf;break;
+ case XSLT_FUNC_NUMBER:
+ cur->func = (xsltTransformFunction) xsltNumber;break;
+ case XSLT_FUNC_APPLYIMPORTS:
+ cur->func = (xsltTransformFunction) xsltApplyImports;break;
+ case XSLT_FUNC_CALLTEMPLATE:
+ cur->func = (xsltTransformFunction) xsltCallTemplate;break;
+ case XSLT_FUNC_APPLYTEMPLATES:
+ cur->func = (xsltTransformFunction) xsltApplyTemplates;break;
+ case XSLT_FUNC_CHOOSE:
+ cur->func = (xsltTransformFunction) xsltChoose;break;
+ case XSLT_FUNC_IF:
+ cur->func = (xsltTransformFunction) xsltIf;break;
+ case XSLT_FUNC_FOREACH:
+ cur->func = (xsltTransformFunction) xsltForEach;break;
+ case XSLT_FUNC_DOCUMENT:
+ cur->func = (xsltTransformFunction) xsltDocumentElem;break;
+ case XSLT_FUNC_WITHPARAM:
+ case XSLT_FUNC_PARAM:
+ case XSLT_FUNC_VARIABLE:
+ case XSLT_FUNC_WHEN:
+ break;
+ default:
+ if (cur->func == NULL) {
+ xsltTransformError(NULL, style, NULL,
+ "xsltNewStylePreComp : no function for type %d\n", type);
+ style->errors++;
+ }
+ }
+ cur->next = style->preComps;
+ style->preComps = (xsltElemPreCompPtr) cur;
+
+ return(cur);
+}
+
+/**
+ * xsltFreeStylePreComp:
+ * @comp: an XSLT Style precomputed block
+ *
+ * Free up the memory allocated by @comp
+ */
+static void
+xsltFreeStylePreComp(xsltStylePreCompPtr comp) {
+ if (comp == NULL)
+ return;
+#ifdef XSLT_REFACTORED
+ /*
+ * URGENT TODO: Implement destructors.
+ */
+ switch (comp->type) {
+ case XSLT_FUNC_LITERAL_RESULT_ELEMENT:
+ break;
+ case XSLT_FUNC_COPY:
+ break;
+ case XSLT_FUNC_SORT: {
+ xsltStyleItemSortPtr item = (xsltStyleItemSortPtr) comp;
+ if (item->locale != (xsltLocale)0)
+ xsltFreeLocale(item->locale);
+ if (item->comp != NULL)
+ xmlXPathFreeCompExpr(item->comp);
+ }
+ break;
+ case XSLT_FUNC_TEXT:
+ break;
+ case XSLT_FUNC_ELEMENT:
+ break;
+ case XSLT_FUNC_ATTRIBUTE:
+ break;
+ case XSLT_FUNC_COMMENT:
+ break;
+ case XSLT_FUNC_PI:
+ break;
+ case XSLT_FUNC_COPYOF: {
+ xsltStyleItemCopyOfPtr item = (xsltStyleItemCopyOfPtr) comp;
+ if (item->comp != NULL)
+ xmlXPathFreeCompExpr(item->comp);
+ }
+ break;
+ case XSLT_FUNC_VALUEOF: {
+ xsltStyleItemValueOfPtr item = (xsltStyleItemValueOfPtr) comp;
+ if (item->comp != NULL)
+ xmlXPathFreeCompExpr(item->comp);
+ }
+ break;
+ case XSLT_FUNC_NUMBER: {
+ xsltStyleItemNumberPtr item = (xsltStyleItemNumberPtr) comp;
+ if (item->numdata.countPat != NULL)
+ xsltFreeCompMatchList(item->numdata.countPat);
+ if (item->numdata.fromPat != NULL)
+ xsltFreeCompMatchList(item->numdata.fromPat);
+ }
+ break;
+ case XSLT_FUNC_APPLYIMPORTS:
+ break;
+ case XSLT_FUNC_CALLTEMPLATE:
+ break;
+ case XSLT_FUNC_APPLYTEMPLATES: {
+ xsltStyleItemApplyTemplatesPtr item =
+ (xsltStyleItemApplyTemplatesPtr) comp;
+ if (item->comp != NULL)
+ xmlXPathFreeCompExpr(item->comp);
+ }
+ break;
+ case XSLT_FUNC_CHOOSE:
+ break;
+ case XSLT_FUNC_IF: {
+ xsltStyleItemIfPtr item = (xsltStyleItemIfPtr) comp;
+ if (item->comp != NULL)
+ xmlXPathFreeCompExpr(item->comp);
+ }
+ break;
+ case XSLT_FUNC_FOREACH: {
+ xsltStyleItemForEachPtr item =
+ (xsltStyleItemForEachPtr) comp;
+ if (item->comp != NULL)
+ xmlXPathFreeCompExpr(item->comp);
+ }
+ break;
+ case XSLT_FUNC_DOCUMENT:
+ break;
+ case XSLT_FUNC_WITHPARAM: {
+ xsltStyleItemWithParamPtr item =
+ (xsltStyleItemWithParamPtr) comp;
+ if (item->comp != NULL)
+ xmlXPathFreeCompExpr(item->comp);
+ }
+ break;
+ case XSLT_FUNC_PARAM: {
+ xsltStyleItemParamPtr item =
+ (xsltStyleItemParamPtr) comp;
+ if (item->comp != NULL)
+ xmlXPathFreeCompExpr(item->comp);
+ }
+ break;
+ case XSLT_FUNC_VARIABLE: {
+ xsltStyleItemVariablePtr item =
+ (xsltStyleItemVariablePtr) comp;
+ if (item->comp != NULL)
+ xmlXPathFreeCompExpr(item->comp);
+ }
+ break;
+ case XSLT_FUNC_WHEN: {
+ xsltStyleItemWhenPtr item =
+ (xsltStyleItemWhenPtr) comp;
+ if (item->comp != NULL)
+ xmlXPathFreeCompExpr(item->comp);
+ }
+ break;
+ case XSLT_FUNC_OTHERWISE:
+ case XSLT_FUNC_FALLBACK:
+ case XSLT_FUNC_MESSAGE:
+ case XSLT_FUNC_INCLUDE:
+ case XSLT_FUNC_ATTRSET:
+
+ break;
+ default:
+ /* TODO: Raise error. */
+ break;
+ }
+#else
+ if (comp->locale != (xsltLocale)0)
+ xsltFreeLocale(comp->locale);
+ if (comp->comp != NULL)
+ xmlXPathFreeCompExpr(comp->comp);
+ if (comp->numdata.countPat != NULL)
+ xsltFreeCompMatchList(comp->numdata.countPat);
+ if (comp->numdata.fromPat != NULL)
+ xsltFreeCompMatchList(comp->numdata.fromPat);
+ if (comp->nsList != NULL)
+ xmlFree(comp->nsList);
+#endif
+
+ xmlFree(comp);
+}
+
+
+/************************************************************************
+ * *
+ * XSLT-1.1 extensions *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltDocumentComp:
+ * @style: the XSLT stylesheet
+ * @inst: the instruction in the stylesheet
+ * @function: unused
+ *
+ * Pre process an XSLT-1.1 document element
+ *
+ * Returns a precompiled data structure for the element
+ */
+xsltElemPreCompPtr
+xsltDocumentComp(xsltStylesheetPtr style, xmlNodePtr inst,
+ xsltTransformFunction function ATTRIBUTE_UNUSED) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemDocumentPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+ const xmlChar *filename = NULL;
+
+ /*
+ * As of 2006-03-30, this function is currently defined in Libxslt
+ * to be used for:
+ * (in libxslt/extra.c)
+ * "output" in XSLT_SAXON_NAMESPACE
+ * "write" XSLT_XALAN_NAMESPACE
+ * "document" XSLT_XT_NAMESPACE
+ * "document" XSLT_NAMESPACE (from the abandoned old working
+ * draft of XSLT 1.1)
+ * (in libexslt/common.c)
+ * "document" in EXSLT_COMMON_NAMESPACE
+ */
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemDocumentPtr)
+ xsltNewStylePreComp(style, XSLT_FUNC_DOCUMENT);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_DOCUMENT);
+#endif
+
+ if (comp == NULL)
+ return (NULL);
+ comp->inst = inst;
+ comp->ver11 = 0;
+
+ if (xmlStrEqual(inst->name, (const xmlChar *) "output")) {
+#ifdef WITH_XSLT_DEBUG_EXTRA
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Found saxon:output extension\n");
+#endif
+ /*
+ * The element "output" is in the namespace XSLT_SAXON_NAMESPACE
+ * (http://icl.com/saxon)
+ * The @file is in no namespace; it is an AVT.
+ * (http://www.computerwizards.com/saxon/doc/extensions.html#saxon:output)
+ *
+ * TODO: Do we need not to check the namespace here?
+ */
+ filename = xsltEvalStaticAttrValueTemplate(style, inst,
+ (const xmlChar *)"file",
+ NULL, &comp->has_filename);
+ } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) {
+#ifdef WITH_XSLT_DEBUG_EXTRA
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Found xalan:write extension\n");
+#endif
+ /* the filename need to be interpreted */
+ /*
+ * TODO: Is "filename need to be interpreted" meant to be a todo?
+ * Where will be the filename of xalan:write be processed?
+ *
+ * TODO: Do we need not to check the namespace here?
+ * The extension ns is "http://xml.apache.org/xalan/redirect".
+ * See http://xml.apache.org/xalan-j/extensionslib.html.
+ */
+ } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) {
+ if (inst->ns != NULL) {
+ if (xmlStrEqual(inst->ns->href, XSLT_NAMESPACE)) {
+ /*
+ * Mark the instruction as being of
+ * XSLT version 1.1 (abandoned).
+ */
+ comp->ver11 = 1;
+#ifdef WITH_XSLT_DEBUG_EXTRA
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Found xslt11:document construct\n");
+#endif
+ } else {
+ if (xmlStrEqual(inst->ns->href,
+ (const xmlChar *)"http://exslt.org/common")) {
+ /* EXSLT. */
+#ifdef WITH_XSLT_DEBUG_EXTRA
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Found exslt:document extension\n");
+#endif
+ } else if (xmlStrEqual(inst->ns->href, XSLT_XT_NAMESPACE)) {
+ /* James Clark's XT. */
+#ifdef WITH_XSLT_DEBUG_EXTRA
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Found xt:document extension\n");
+#endif
+ }
+ }
+ }
+ /*
+ * The element "document" is used in conjunction with the
+ * following namespaces:
+ *
+ * 1) XSLT_NAMESPACE (http://www.w3.org/1999/XSL/Transform version 1.1)
+ * <!ELEMENT xsl:document %template;>
+ * <!ATTLIST xsl:document
+ * href %avt; #REQUIRED
+ * @href is an AVT
+ * IMPORTANT: xsl:document was in the abandoned XSLT 1.1 draft,
+ * it was removed and isn't available in XSLT 1.1 anymore.
+ * In XSLT 2.0 it was renamed to xsl:result-document.
+ *
+ * All other attributes are identical to the attributes
+ * on xsl:output
+ *
+ * 2) EXSLT_COMMON_NAMESPACE (http://exslt.org/common)
+ * <exsl:document
+ * href = { uri-reference }
+ * TODO: is @href is an AVT?
+ *
+ * 3) XSLT_XT_NAMESPACE (http://www.jclark.com/xt)
+ * Example: <xt:document method="xml" href="myFile.xml">
+ * TODO: is @href is an AVT?
+ *
+ * In all cases @href is in no namespace.
+ */
+ filename = xsltEvalStaticAttrValueTemplate(style, inst,
+ (const xmlChar *)"href", NULL, &comp->has_filename);
+ }
+ if (!comp->has_filename) {
+ goto error;
+ }
+ comp->filename = filename;
+
+error:
+ return ((xsltElemPreCompPtr) comp);
+}
+
+/************************************************************************
+ * *
+ * Most of the XSLT-1.0 transformations *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltSortComp:
+ * @style: the XSLT stylesheet
+ * @inst: the xslt sort node
+ *
+ * Process the xslt sort node on the source node
+ */
+static void
+xsltSortComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemSortPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemSortPtr) xsltNewStylePreComp(style, XSLT_FUNC_SORT);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_SORT);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+
+ comp->stype = xsltEvalStaticAttrValueTemplate(style, inst,
+ (const xmlChar *)"data-type",
+ NULL, &comp->has_stype);
+ if (comp->stype != NULL) {
+ if (xmlStrEqual(comp->stype, (const xmlChar *) "text"))
+ comp->number = 0;
+ else if (xmlStrEqual(comp->stype, (const xmlChar *) "number"))
+ comp->number = 1;
+ else {
+ xsltTransformError(NULL, style, inst,
+ "xsltSortComp: no support for data-type = %s\n", comp->stype);
+ comp->number = 0; /* use default */
+ if (style != NULL) style->warnings++;
+ }
+ }
+ comp->order = xsltEvalStaticAttrValueTemplate(style, inst,
+ (const xmlChar *)"order",
+ NULL, &comp->has_order);
+ if (comp->order != NULL) {
+ if (xmlStrEqual(comp->order, (const xmlChar *) "ascending"))
+ comp->descending = 0;
+ else if (xmlStrEqual(comp->order, (const xmlChar *) "descending"))
+ comp->descending = 1;
+ else {
+ xsltTransformError(NULL, style, inst,
+ "xsltSortComp: invalid value %s for order\n", comp->order);
+ comp->descending = 0; /* use default */
+ if (style != NULL) style->warnings++;
+ }
+ }
+ comp->case_order = xsltEvalStaticAttrValueTemplate(style, inst,
+ (const xmlChar *)"case-order",
+ NULL, &comp->has_use);
+ if (comp->case_order != NULL) {
+ if (xmlStrEqual(comp->case_order, (const xmlChar *) "upper-first"))
+ comp->lower_first = 0;
+ else if (xmlStrEqual(comp->case_order, (const xmlChar *) "lower-first"))
+ comp->lower_first = 1;
+ else {
+ xsltTransformError(NULL, style, inst,
+ "xsltSortComp: invalid value %s for order\n", comp->order);
+ comp->lower_first = 0; /* use default */
+ if (style != NULL) style->warnings++;
+ }
+ }
+
+ comp->lang = xsltEvalStaticAttrValueTemplate(style, inst,
+ (const xmlChar *)"lang",
+ NULL, &comp->has_lang);
+ if (comp->lang != NULL) {
+ comp->locale = xsltNewLocale(comp->lang);
+ }
+ else {
+ comp->locale = (xsltLocale)0;
+ }
+
+ comp->select = xsltGetCNsProp(style, inst,(const xmlChar *)"select", XSLT_NAMESPACE);
+ if (comp->select == NULL) {
+ /*
+ * The default value of the select attribute is ., which will
+ * cause the string-value of the current node to be used as
+ * the sort key.
+ */
+ comp->select = xmlDictLookup(style->dict, BAD_CAST ".", 1);
+ }
+ comp->comp = xsltXPathCompile(style, comp->select);
+ if (comp->comp == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsltSortComp: could not compile select expression '%s'\n",
+ comp->select);
+ if (style != NULL) style->errors++;
+ }
+ if (inst->children != NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:sort : is not empty\n");
+ if (style != NULL) style->errors++;
+ }
+}
+
+/**
+ * xsltCopyComp:
+ * @style: the XSLT stylesheet
+ * @inst: the xslt copy node
+ *
+ * Process the xslt copy node on the source node
+ */
+static void
+xsltCopyComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemCopyPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemCopyPtr) xsltNewStylePreComp(style, XSLT_FUNC_COPY);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_COPY);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+
+
+ comp->use = xsltGetCNsProp(style, inst, (const xmlChar *)"use-attribute-sets",
+ XSLT_NAMESPACE);
+ if (comp->use == NULL)
+ comp->has_use = 0;
+ else
+ comp->has_use = 1;
+}
+
+#ifdef XSLT_REFACTORED
+ /* Enable if ever needed for xsl:text. */
+#else
+/**
+ * xsltTextComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the xslt text node
+ *
+ * TODO: This function is obsolete, since xsl:text won't
+ * be compiled, but removed from the tree.
+ *
+ * Process the xslt text node on the source node
+ */
+static void
+xsltTextComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemTextPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+ const xmlChar *prop;
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemTextPtr) xsltNewStylePreComp(style, XSLT_FUNC_TEXT);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_TEXT);
+#endif
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+ comp->noescape = 0;
+
+ prop = xsltGetCNsProp(style, inst,
+ (const xmlChar *)"disable-output-escaping",
+ XSLT_NAMESPACE);
+ if (prop != NULL) {
+ if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
+ comp->noescape = 1;
+ } else if (!xmlStrEqual(prop,
+ (const xmlChar *)"no")){
+ xsltTransformError(NULL, style, inst,
+ "xsl:text: disable-output-escaping allows only yes or no\n");
+ if (style != NULL) style->warnings++;
+ }
+ }
+}
+#endif /* else of XSLT_REFACTORED */
+
+/**
+ * xsltElementComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the xslt element node
+ *
+ * Process the xslt element node on the source node
+ */
+static void
+xsltElementComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemElementPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ /*
+ * <xsl:element
+ * name = { qname }
+ * namespace = { uri-reference }
+ * use-attribute-sets = qnames>
+ * <!-- Content: template -->
+ * </xsl:element>
+ */
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemElementPtr) xsltNewStylePreComp(style, XSLT_FUNC_ELEMENT);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_ELEMENT);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+
+ /*
+ * Attribute "name".
+ */
+ /*
+ * TODO: Precompile the AVT. See bug #344894.
+ */
+ comp->name = xsltEvalStaticAttrValueTemplate(style, inst,
+ (const xmlChar *)"name", NULL, &comp->has_name);
+ if (! comp->has_name) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:element: The attribute 'name' is missing.\n");
+ style->errors++;
+ goto error;
+ }
+ /*
+ * Attribute "namespace".
+ */
+ /*
+ * TODO: Precompile the AVT. See bug #344894.
+ */
+ comp->ns = xsltEvalStaticAttrValueTemplate(style, inst,
+ (const xmlChar *)"namespace", NULL, &comp->has_ns);
+
+ if (comp->name != NULL) {
+ if (xmlValidateQName(comp->name, 0)) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:element: The value '%s' of the attribute 'name' is "
+ "not a valid QName.\n", comp->name);
+ style->errors++;
+ } else {
+ const xmlChar *prefix = NULL, *name;
+
+ name = xsltSplitQName(style->dict, comp->name, &prefix);
+ if (comp->has_ns == 0) {
+ xmlNsPtr ns;
+
+ /*
+ * SPEC XSLT 1.0:
+ * "If the namespace attribute is not present, then the QName is
+ * expanded into an expanded-name using the namespace declarations
+ * in effect for the xsl:element element, including any default
+ * namespace declaration.
+ */
+ ns = xmlSearchNs(inst->doc, inst, prefix);
+ if (ns != NULL) {
+ comp->ns = xmlDictLookup(style->dict, ns->href, -1);
+ comp->has_ns = 1;
+#ifdef XSLT_REFACTORED
+ comp->nsPrefix = prefix;
+ comp->name = name;
+#else
+ (void)name; /* Suppress unused variable warning. */
+#endif
+ } else if (prefix != NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:element: The prefixed QName '%s' "
+ "has no namespace binding in scope in the "
+ "stylesheet; this is an error, since the namespace was "
+ "not specified by the instruction itself.\n", comp->name);
+ style->errors++;
+ }
+ }
+ if ((prefix != NULL) &&
+ (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3)))
+ {
+ /*
+ * Mark is to be skipped.
+ */
+ comp->has_name = 0;
+ }
+ }
+ }
+ /*
+ * Attribute "use-attribute-sets",
+ */
+ comp->use = xsltEvalStaticAttrValueTemplate(style, inst,
+ (const xmlChar *)"use-attribute-sets",
+ NULL, &comp->has_use);
+
+error:
+ return;
+}
+
+/**
+ * xsltAttributeComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the xslt attribute node
+ *
+ * Process the xslt attribute node on the source node
+ */
+static void
+xsltAttributeComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemAttributePtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ /*
+ * <xsl:attribute
+ * name = { qname }
+ * namespace = { uri-reference }>
+ * <!-- Content: template -->
+ * </xsl:attribute>
+ */
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemAttributePtr) xsltNewStylePreComp(style,
+ XSLT_FUNC_ATTRIBUTE);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_ATTRIBUTE);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+
+ /*
+ * Attribute "name".
+ */
+ /*
+ * TODO: Precompile the AVT. See bug #344894.
+ */
+ comp->name = xsltEvalStaticAttrValueTemplate(style, inst,
+ (const xmlChar *)"name",
+ NULL, &comp->has_name);
+ if (! comp->has_name) {
+ xsltTransformError(NULL, style, inst,
+ "XSLT-attribute: The attribute 'name' is missing.\n");
+ style->errors++;
+ return;
+ }
+ /*
+ * Attribute "namespace".
+ */
+ /*
+ * TODO: Precompile the AVT. See bug #344894.
+ */
+ comp->ns = xsltEvalStaticAttrValueTemplate(style, inst,
+ (const xmlChar *)"namespace",
+ NULL, &comp->has_ns);
+
+ if (comp->name != NULL) {
+ if (xmlValidateQName(comp->name, 0)) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:attribute: The value '%s' of the attribute 'name' is "
+ "not a valid QName.\n", comp->name);
+ style->errors++;
+ } else if (xmlStrEqual(comp->name, BAD_CAST "xmlns")) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:attribute: The attribute name 'xmlns' is not allowed.\n");
+ style->errors++;
+ } else {
+ const xmlChar *prefix = NULL, *name;
+
+ name = xsltSplitQName(style->dict, comp->name, &prefix);
+ if (prefix != NULL) {
+ if (comp->has_ns == 0) {
+ xmlNsPtr ns;
+
+ /*
+ * SPEC XSLT 1.0:
+ * "If the namespace attribute is not present, then the
+ * QName is expanded into an expanded-name using the
+ * namespace declarations in effect for the xsl:element
+ * element, including any default namespace declaration.
+ */
+ ns = xmlSearchNs(inst->doc, inst, prefix);
+ if (ns != NULL) {
+ comp->ns = xmlDictLookup(style->dict, ns->href, -1);
+ comp->has_ns = 1;
+#ifdef XSLT_REFACTORED
+ comp->nsPrefix = prefix;
+ comp->name = name;
+#else
+ (void)name; /* Suppress unused variable warning. */
+#endif
+ } else {
+ xsltTransformError(NULL, style, inst,
+ "xsl:attribute: The prefixed QName '%s' "
+ "has no namespace binding in scope in the "
+ "stylesheet; this is an error, since the "
+ "namespace was not specified by the instruction "
+ "itself.\n", comp->name);
+ style->errors++;
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * xsltCommentComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the xslt comment node
+ *
+ * Process the xslt comment node on the source node
+ */
+static void
+xsltCommentComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemCommentPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemCommentPtr) xsltNewStylePreComp(style, XSLT_FUNC_COMMENT);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_COMMENT);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+}
+
+/**
+ * xsltProcessingInstructionComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the xslt processing-instruction node
+ *
+ * Process the xslt processing-instruction node on the source node
+ */
+static void
+xsltProcessingInstructionComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemPIPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemPIPtr) xsltNewStylePreComp(style, XSLT_FUNC_PI);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_PI);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+
+ comp->name = xsltEvalStaticAttrValueTemplate(style, inst,
+ (const xmlChar *)"name",
+ XSLT_NAMESPACE, &comp->has_name);
+}
+
+/**
+ * xsltCopyOfComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the xslt copy-of node
+ *
+ * Process the xslt copy-of node on the source node
+ */
+static void
+xsltCopyOfComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemCopyOfPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemCopyOfPtr) xsltNewStylePreComp(style, XSLT_FUNC_COPYOF);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_COPYOF);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+
+ comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select",
+ XSLT_NAMESPACE);
+ if (comp->select == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:copy-of : select is missing\n");
+ if (style != NULL) style->errors++;
+ return;
+ }
+ comp->comp = xsltXPathCompile(style, comp->select);
+ if (comp->comp == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:copy-of : could not compile select expression '%s'\n",
+ comp->select);
+ if (style != NULL) style->errors++;
+ }
+}
+
+/**
+ * xsltValueOfComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the xslt value-of node
+ *
+ * Process the xslt value-of node on the source node
+ */
+static void
+xsltValueOfComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemValueOfPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+ const xmlChar *prop;
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemValueOfPtr) xsltNewStylePreComp(style, XSLT_FUNC_VALUEOF);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_VALUEOF);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+
+ prop = xsltGetCNsProp(style, inst,
+ (const xmlChar *)"disable-output-escaping",
+ XSLT_NAMESPACE);
+ if (prop != NULL) {
+ if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
+ comp->noescape = 1;
+ } else if (!xmlStrEqual(prop,
+ (const xmlChar *)"no")){
+ xsltTransformError(NULL, style, inst,
+"xsl:value-of : disable-output-escaping allows only yes or no\n");
+ if (style != NULL) style->warnings++;
+ }
+ }
+ comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select",
+ XSLT_NAMESPACE);
+ if (comp->select == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:value-of : select is missing\n");
+ if (style != NULL) style->errors++;
+ return;
+ }
+ comp->comp = xsltXPathCompile(style, comp->select);
+ if (comp->comp == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:value-of : could not compile select expression '%s'\n",
+ comp->select);
+ if (style != NULL) style->errors++;
+ }
+}
+
+static void
+xsltGetQNameProperty(xsltStylesheetPtr style, xmlNodePtr inst,
+ const xmlChar *propName,
+ int mandatory,
+ int *hasProp, const xmlChar **nsName,
+ const xmlChar** localName)
+{
+ const xmlChar *prop;
+
+ if (nsName)
+ *nsName = NULL;
+ if (localName)
+ *localName = NULL;
+ if (hasProp)
+ *hasProp = 0;
+
+ prop = xsltGetCNsProp(style, inst, propName, XSLT_NAMESPACE);
+ if (prop == NULL) {
+ if (mandatory) {
+ xsltTransformError(NULL, style, inst,
+ "The attribute '%s' is missing.\n", propName);
+ style->errors++;
+ return;
+ }
+ } else {
+ const xmlChar *URI;
+
+ if (xmlValidateQName(prop, 0)) {
+ xsltTransformError(NULL, style, inst,
+ "The value '%s' of the attribute "
+ "'%s' is not a valid QName.\n", prop, propName);
+ style->errors++;
+ return;
+ } else {
+ /*
+ * @prop will be in the string dict afterwards, @URI not.
+ */
+ URI = xsltGetQNameURI2(style, inst, &prop);
+ if (prop == NULL) {
+ style->errors++;
+ } else {
+ if (localName)
+ *localName = prop;
+ if (hasProp)
+ *hasProp = 1;
+ if (URI != NULL) {
+ /*
+ * Fixes bug #308441: Put the ns-name in the dict
+ * in order to pointer compare names during XPath's
+ * variable lookup.
+ */
+ if (nsName)
+ *nsName = xmlDictLookup(style->dict, URI, -1);
+ /* comp->has_ns = 1; */
+ }
+ }
+ }
+ }
+ return;
+}
+
+/**
+ * xsltWithParamComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the xslt with-param node
+ *
+ * Process the xslt with-param node on the source node
+ * Allowed parents: xsl:call-template, xsl:apply-templates.
+ * <xsl:with-param
+ * name = qname
+ * select = expression>
+ * <!-- Content: template -->
+ * </xsl:with-param>
+ */
+static void
+xsltWithParamComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemWithParamPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemWithParamPtr) xsltNewStylePreComp(style, XSLT_FUNC_WITHPARAM);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_WITHPARAM);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+
+ /*
+ * Attribute "name".
+ */
+ xsltGetQNameProperty(style, inst, BAD_CAST "name",
+ 1, &(comp->has_name), &(comp->ns), &(comp->name));
+ if (comp->ns)
+ comp->has_ns = 1;
+ /*
+ * Attribute "select".
+ */
+ comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select",
+ XSLT_NAMESPACE);
+ if (comp->select != NULL) {
+ comp->comp = xsltXPathCompile(style, comp->select);
+ if (comp->comp == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "XSLT-with-param: Failed to compile select "
+ "expression '%s'\n", comp->select);
+ style->errors++;
+ }
+ if (inst->children != NULL) {
+ xsltTransformError(NULL, style, inst,
+ "XSLT-with-param: The content should be empty since "
+ "the attribute select is present.\n");
+ style->warnings++;
+ }
+ }
+}
+
+/**
+ * xsltNumberComp:
+ * @style: an XSLT compiled stylesheet
+ * @cur: the xslt number node
+ *
+ * Process the xslt number node on the source node
+ */
+static void
+xsltNumberComp(xsltStylesheetPtr style, xmlNodePtr cur) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemNumberPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+ const xmlChar *prop;
+
+ if ((style == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemNumberPtr) xsltNewStylePreComp(style, XSLT_FUNC_NUMBER);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_NUMBER);
+#endif
+
+ if (comp == NULL)
+ return;
+ cur->psvi = comp;
+
+ comp->numdata.doc = cur->doc;
+ comp->numdata.node = cur;
+ comp->numdata.value = xsltGetCNsProp(style, cur, (const xmlChar *)"value",
+ XSLT_NAMESPACE);
+
+ prop = xsltEvalStaticAttrValueTemplate(style, cur,
+ (const xmlChar *)"format",
+ XSLT_NAMESPACE, &comp->numdata.has_format);
+ if (comp->numdata.has_format == 0) {
+ comp->numdata.format = xmlDictLookup(style->dict, BAD_CAST "" , 0);
+ } else {
+ comp->numdata.format = prop;
+ }
+
+ comp->numdata.count = xsltGetCNsProp(style, cur, (const xmlChar *)"count",
+ XSLT_NAMESPACE);
+ comp->numdata.from = xsltGetCNsProp(style, cur, (const xmlChar *)"from",
+ XSLT_NAMESPACE);
+
+ prop = xsltGetCNsProp(style, cur, (const xmlChar *)"count", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ comp->numdata.countPat = xsltCompilePattern(prop, cur->doc, cur, style,
+ NULL);
+ }
+
+ prop = xsltGetCNsProp(style, cur, (const xmlChar *)"from", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ comp->numdata.fromPat = xsltCompilePattern(prop, cur->doc, cur, style,
+ NULL);
+ }
+
+ prop = xsltGetCNsProp(style, cur, (const xmlChar *)"level", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ if (xmlStrEqual(prop, BAD_CAST("single")) ||
+ xmlStrEqual(prop, BAD_CAST("multiple")) ||
+ xmlStrEqual(prop, BAD_CAST("any"))) {
+ comp->numdata.level = prop;
+ } else {
+ xsltTransformError(NULL, style, cur,
+ "xsl:number : invalid value %s for level\n", prop);
+ if (style != NULL) style->warnings++;
+ }
+ }
+
+ prop = xsltGetCNsProp(style, cur, (const xmlChar *)"lang", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:number : lang attribute not implemented\n");
+ XSLT_TODO; /* xsl:number lang attribute */
+ }
+
+ prop = xsltGetCNsProp(style, cur, (const xmlChar *)"letter-value", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ if (xmlStrEqual(prop, BAD_CAST("alphabetic"))) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:number : letter-value 'alphabetic' not implemented\n");
+ if (style != NULL) style->warnings++;
+ XSLT_TODO; /* xsl:number letter-value attribute alphabetic */
+ } else if (xmlStrEqual(prop, BAD_CAST("traditional"))) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:number : letter-value 'traditional' not implemented\n");
+ if (style != NULL) style->warnings++;
+ XSLT_TODO; /* xsl:number letter-value attribute traditional */
+ } else {
+ xsltTransformError(NULL, style, cur,
+ "xsl:number : invalid value %s for letter-value\n", prop);
+ if (style != NULL) style->warnings++;
+ }
+ }
+
+ prop = xsltGetCNsProp(style, cur, (const xmlChar *)"grouping-separator",
+ XSLT_NAMESPACE);
+ if (prop != NULL) {
+ comp->numdata.groupingCharacterLen = xmlStrlen(prop);
+ comp->numdata.groupingCharacter =
+ xsltGetUTF8Char(prop, &(comp->numdata.groupingCharacterLen));
+ }
+
+ prop = xsltGetCNsProp(style, cur, (const xmlChar *)"grouping-size", XSLT_NAMESPACE);
+ if (prop != NULL) {
+ sscanf((char *)prop, "%d", &comp->numdata.digitsPerGroup);
+ } else {
+ comp->numdata.groupingCharacter = 0;
+ }
+
+ /* Set default values */
+ if (comp->numdata.value == NULL) {
+ if (comp->numdata.level == NULL) {
+ comp->numdata.level = xmlDictLookup(style->dict,
+ BAD_CAST"single", 6);
+ }
+ }
+
+}
+
+/**
+ * xsltApplyImportsComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the xslt apply-imports node
+ *
+ * Process the xslt apply-imports node on the source node
+ */
+static void
+xsltApplyImportsComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemApplyImportsPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemApplyImportsPtr) xsltNewStylePreComp(style, XSLT_FUNC_APPLYIMPORTS);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_APPLYIMPORTS);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+}
+
+/**
+ * xsltCallTemplateComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the xslt call-template node
+ *
+ * Process the xslt call-template node on the source node
+ */
+static void
+xsltCallTemplateComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemCallTemplatePtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemCallTemplatePtr)
+ xsltNewStylePreComp(style, XSLT_FUNC_CALLTEMPLATE);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_CALLTEMPLATE);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+
+ /*
+ * Attribute "name".
+ */
+ xsltGetQNameProperty(style, inst, BAD_CAST "name",
+ 1, &(comp->has_name), &(comp->ns), &(comp->name));
+ if (comp->ns)
+ comp->has_ns = 1;
+}
+
+/**
+ * xsltApplyTemplatesComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the apply-templates node
+ *
+ * Process the apply-templates node on the source node
+ */
+static void
+xsltApplyTemplatesComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemApplyTemplatesPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemApplyTemplatesPtr)
+ xsltNewStylePreComp(style, XSLT_FUNC_APPLYTEMPLATES);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_APPLYTEMPLATES);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+
+ /*
+ * Attribute "mode".
+ */
+ xsltGetQNameProperty(style, inst, BAD_CAST "mode",
+ 0, NULL, &(comp->modeURI), &(comp->mode));
+ /*
+ * Attribute "select".
+ */
+ comp->select = xsltGetCNsProp(style, inst, BAD_CAST "select",
+ XSLT_NAMESPACE);
+ if (comp->select != NULL) {
+ comp->comp = xsltXPathCompile(style, comp->select);
+ if (comp->comp == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "XSLT-apply-templates: could not compile select "
+ "expression '%s'\n", comp->select);
+ style->errors++;
+ }
+ }
+ /* TODO: handle (or skip) the xsl:sort and xsl:with-param */
+}
+
+/**
+ * xsltChooseComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the xslt choose node
+ *
+ * Process the xslt choose node on the source node
+ */
+static void
+xsltChooseComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemChoosePtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemChoosePtr)
+ xsltNewStylePreComp(style, XSLT_FUNC_CHOOSE);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_CHOOSE);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+}
+
+/**
+ * xsltIfComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the xslt if node
+ *
+ * Process the xslt if node on the source node
+ */
+static void
+xsltIfComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemIfPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemIfPtr)
+ xsltNewStylePreComp(style, XSLT_FUNC_IF);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_IF);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+
+ comp->test = xsltGetCNsProp(style, inst, (const xmlChar *)"test", XSLT_NAMESPACE);
+ if (comp->test == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:if : test is not defined\n");
+ if (style != NULL) style->errors++;
+ return;
+ }
+ comp->comp = xsltXPathCompile(style, comp->test);
+ if (comp->comp == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:if : could not compile test expression '%s'\n",
+ comp->test);
+ if (style != NULL) style->errors++;
+ }
+}
+
+/**
+ * xsltWhenComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the xslt if node
+ *
+ * Process the xslt if node on the source node
+ */
+static void
+xsltWhenComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemWhenPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemWhenPtr)
+ xsltNewStylePreComp(style, XSLT_FUNC_WHEN);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_WHEN);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+
+ comp->test = xsltGetCNsProp(style, inst, (const xmlChar *)"test", XSLT_NAMESPACE);
+ if (comp->test == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:when : test is not defined\n");
+ if (style != NULL) style->errors++;
+ return;
+ }
+ comp->comp = xsltXPathCompile(style, comp->test);
+ if (comp->comp == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:when : could not compile test expression '%s'\n",
+ comp->test);
+ if (style != NULL) style->errors++;
+ }
+}
+
+/**
+ * xsltForEachComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the xslt for-each node
+ *
+ * Process the xslt for-each node on the source node
+ */
+static void
+xsltForEachComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemForEachPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemForEachPtr)
+ xsltNewStylePreComp(style, XSLT_FUNC_FOREACH);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_FOREACH);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+
+ comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select",
+ XSLT_NAMESPACE);
+ if (comp->select == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:for-each : select is missing\n");
+ if (style != NULL) style->errors++;
+ } else {
+ comp->comp = xsltXPathCompile(style, comp->select);
+ if (comp->comp == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "xsl:for-each : could not compile select expression '%s'\n",
+ comp->select);
+ if (style != NULL) style->errors++;
+ }
+ }
+ /* TODO: handle and skip the xsl:sort */
+}
+
+/**
+ * xsltVariableComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the xslt variable node
+ *
+ * Process the xslt variable node on the source node
+ */
+static void
+xsltVariableComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemVariablePtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemVariablePtr)
+ xsltNewStylePreComp(style, XSLT_FUNC_VARIABLE);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_VARIABLE);
+#endif
+
+ if (comp == NULL)
+ return;
+
+ inst->psvi = comp;
+ comp->inst = inst;
+ /*
+ * The full template resolution can be done statically
+ */
+
+ /*
+ * Attribute "name".
+ */
+ xsltGetQNameProperty(style, inst, BAD_CAST "name",
+ 1, &(comp->has_name), &(comp->ns), &(comp->name));
+ if (comp->ns)
+ comp->has_ns = 1;
+ /*
+ * Attribute "select".
+ */
+ comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select",
+ XSLT_NAMESPACE);
+ if (comp->select != NULL) {
+#ifndef XSLT_REFACTORED
+ xmlNodePtr cur;
+#endif
+ comp->comp = xsltXPathCompile(style, comp->select);
+ if (comp->comp == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "XSLT-variable: Failed to compile the XPath expression '%s'.\n",
+ comp->select);
+ style->errors++;
+ }
+#ifdef XSLT_REFACTORED
+ if (inst->children != NULL) {
+ xsltTransformError(NULL, style, inst,
+ "XSLT-variable: There must be no child nodes, since the "
+ "attribute 'select' was specified.\n");
+ style->errors++;
+ }
+#else
+ for (cur = inst->children; cur != NULL; cur = cur->next) {
+ if (cur->type != XML_COMMENT_NODE &&
+ (cur->type != XML_TEXT_NODE || !xsltIsBlank(cur->content)))
+ {
+ xsltTransformError(NULL, style, inst,
+ "XSLT-variable: There must be no child nodes, since the "
+ "attribute 'select' was specified.\n");
+ style->errors++;
+ }
+ }
+#endif
+ }
+}
+
+/**
+ * xsltParamComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst: the xslt param node
+ *
+ * Process the xslt param node on the source node
+ */
+static void
+xsltParamComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemParamPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleItemParamPtr)
+ xsltNewStylePreComp(style, XSLT_FUNC_PARAM);
+#else
+ comp = xsltNewStylePreComp(style, XSLT_FUNC_PARAM);
+#endif
+
+ if (comp == NULL)
+ return;
+ inst->psvi = comp;
+ comp->inst = inst;
+
+ /*
+ * Attribute "name".
+ */
+ xsltGetQNameProperty(style, inst, BAD_CAST "name",
+ 1, &(comp->has_name), &(comp->ns), &(comp->name));
+ if (comp->ns)
+ comp->has_ns = 1;
+ /*
+ * Attribute "select".
+ */
+ comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select",
+ XSLT_NAMESPACE);
+ if (comp->select != NULL) {
+ comp->comp = xsltXPathCompile(style, comp->select);
+ if (comp->comp == NULL) {
+ xsltTransformError(NULL, style, inst,
+ "XSLT-param: could not compile select expression '%s'.\n",
+ comp->select);
+ style->errors++;
+ }
+ if (inst->children != NULL) {
+ xsltTransformError(NULL, style, inst,
+ "XSLT-param: The content should be empty since the "
+ "attribute 'select' is present.\n");
+ style->warnings++;
+ }
+ }
+}
+
+/************************************************************************
+ * *
+ * Generic interface *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltFreeStylePreComps:
+ * @style: an XSLT transformation context
+ *
+ * Free up the memory allocated by all precomputed blocks
+ */
+void
+xsltFreeStylePreComps(xsltStylesheetPtr style) {
+ xsltElemPreCompPtr cur, next;
+
+ if (style == NULL)
+ return;
+
+ cur = style->preComps;
+ while (cur != NULL) {
+ next = cur->next;
+ if (cur->type == XSLT_FUNC_EXTENSION)
+ cur->free(cur);
+ else
+ xsltFreeStylePreComp((xsltStylePreCompPtr) cur);
+ cur = next;
+ }
+}
+
+#ifdef XSLT_REFACTORED
+
+/**
+ * xsltStylePreCompute:
+ * @style: the XSLT stylesheet
+ * @node: the element in the XSLT namespace
+ *
+ * Precompute an XSLT element.
+ * This expects the type of the element to be already
+ * set in style->compCtxt->inode->type;
+ */
+void
+xsltStylePreCompute(xsltStylesheetPtr style, xmlNodePtr node) {
+ /*
+ * The xsltXSLTElemMarker marker was set beforehand by
+ * the parsing mechanism for all elements in the XSLT namespace.
+ */
+ if (style == NULL) {
+ if ((node != NULL) && (node->type == XML_ELEMENT_NODE))
+ node->psvi = NULL;
+ return;
+ }
+ if (node == NULL)
+ return;
+ if (! IS_XSLT_ELEM_FAST(node))
+ return;
+
+ node->psvi = NULL;
+ if (XSLT_CCTXT(style)->inode->type != 0) {
+ switch (XSLT_CCTXT(style)->inode->type) {
+ case XSLT_FUNC_APPLYTEMPLATES:
+ xsltApplyTemplatesComp(style, node);
+ break;
+ case XSLT_FUNC_WITHPARAM:
+ xsltWithParamComp(style, node);
+ break;
+ case XSLT_FUNC_VALUEOF:
+ xsltValueOfComp(style, node);
+ break;
+ case XSLT_FUNC_COPY:
+ xsltCopyComp(style, node);
+ break;
+ case XSLT_FUNC_COPYOF:
+ xsltCopyOfComp(style, node);
+ break;
+ case XSLT_FUNC_IF:
+ xsltIfComp(style, node);
+ break;
+ case XSLT_FUNC_CHOOSE:
+ xsltChooseComp(style, node);
+ break;
+ case XSLT_FUNC_WHEN:
+ xsltWhenComp(style, node);
+ break;
+ case XSLT_FUNC_OTHERWISE:
+ /* NOP yet */
+ return;
+ case XSLT_FUNC_FOREACH:
+ xsltForEachComp(style, node);
+ break;
+ case XSLT_FUNC_APPLYIMPORTS:
+ xsltApplyImportsComp(style, node);
+ break;
+ case XSLT_FUNC_ATTRIBUTE:
+ xsltAttributeComp(style, node);
+ break;
+ case XSLT_FUNC_ELEMENT:
+ xsltElementComp(style, node);
+ break;
+ case XSLT_FUNC_SORT:
+ xsltSortComp(style, node);
+ break;
+ case XSLT_FUNC_COMMENT:
+ xsltCommentComp(style, node);
+ break;
+ case XSLT_FUNC_NUMBER:
+ xsltNumberComp(style, node);
+ break;
+ case XSLT_FUNC_PI:
+ xsltProcessingInstructionComp(style, node);
+ break;
+ case XSLT_FUNC_CALLTEMPLATE:
+ xsltCallTemplateComp(style, node);
+ break;
+ case XSLT_FUNC_PARAM:
+ xsltParamComp(style, node);
+ break;
+ case XSLT_FUNC_VARIABLE:
+ xsltVariableComp(style, node);
+ break;
+ case XSLT_FUNC_FALLBACK:
+ /* NOP yet */
+ return;
+ case XSLT_FUNC_DOCUMENT:
+ /* The extra one */
+ node->psvi = (void *) xsltDocumentComp(style, node,
+ (xsltTransformFunction) xsltDocumentElem);
+ break;
+ case XSLT_FUNC_MESSAGE:
+ /* NOP yet */
+ return;
+ default:
+ /*
+ * NOTE that xsl:text, xsl:template, xsl:stylesheet,
+ * xsl:transform, xsl:import, xsl:include are not expected
+ * to be handed over to this function.
+ */
+ xsltTransformError(NULL, style, node,
+ "Internal error: (xsltStylePreCompute) cannot handle "
+ "the XSLT element '%s'.\n", node->name);
+ style->errors++;
+ return;
+ }
+ } else {
+ /*
+ * Fallback to string comparison.
+ */
+ if (IS_XSLT_NAME(node, "apply-templates")) {
+ xsltApplyTemplatesComp(style, node);
+ } else if (IS_XSLT_NAME(node, "with-param")) {
+ xsltWithParamComp(style, node);
+ } else if (IS_XSLT_NAME(node, "value-of")) {
+ xsltValueOfComp(style, node);
+ } else if (IS_XSLT_NAME(node, "copy")) {
+ xsltCopyComp(style, node);
+ } else if (IS_XSLT_NAME(node, "copy-of")) {
+ xsltCopyOfComp(style, node);
+ } else if (IS_XSLT_NAME(node, "if")) {
+ xsltIfComp(style, node);
+ } else if (IS_XSLT_NAME(node, "choose")) {
+ xsltChooseComp(style, node);
+ } else if (IS_XSLT_NAME(node, "when")) {
+ xsltWhenComp(style, node);
+ } else if (IS_XSLT_NAME(node, "otherwise")) {
+ /* NOP yet */
+ return;
+ } else if (IS_XSLT_NAME(node, "for-each")) {
+ xsltForEachComp(style, node);
+ } else if (IS_XSLT_NAME(node, "apply-imports")) {
+ xsltApplyImportsComp(style, node);
+ } else if (IS_XSLT_NAME(node, "attribute")) {
+ xsltAttributeComp(style, node);
+ } else if (IS_XSLT_NAME(node, "element")) {
+ xsltElementComp(style, node);
+ } else if (IS_XSLT_NAME(node, "sort")) {
+ xsltSortComp(style, node);
+ } else if (IS_XSLT_NAME(node, "comment")) {
+ xsltCommentComp(style, node);
+ } else if (IS_XSLT_NAME(node, "number")) {
+ xsltNumberComp(style, node);
+ } else if (IS_XSLT_NAME(node, "processing-instruction")) {
+ xsltProcessingInstructionComp(style, node);
+ } else if (IS_XSLT_NAME(node, "call-template")) {
+ xsltCallTemplateComp(style, node);
+ } else if (IS_XSLT_NAME(node, "param")) {
+ xsltParamComp(style, node);
+ } else if (IS_XSLT_NAME(node, "variable")) {
+ xsltVariableComp(style, node);
+ } else if (IS_XSLT_NAME(node, "fallback")) {
+ /* NOP yet */
+ return;
+ } else if (IS_XSLT_NAME(node, "document")) {
+ /* The extra one */
+ node->psvi = (void *) xsltDocumentComp(style, node,
+ (xsltTransformFunction) xsltDocumentElem);
+ } else if (IS_XSLT_NAME(node, "output")) {
+ /* Top-level */
+ return;
+ } else if (IS_XSLT_NAME(node, "preserve-space")) {
+ /* Top-level */
+ return;
+ } else if (IS_XSLT_NAME(node, "strip-space")) {
+ /* Top-level */
+ return;
+ } else if (IS_XSLT_NAME(node, "key")) {
+ /* Top-level */
+ return;
+ } else if (IS_XSLT_NAME(node, "message")) {
+ return;
+ } else if (IS_XSLT_NAME(node, "attribute-set")) {
+ /* Top-level */
+ return;
+ } else if (IS_XSLT_NAME(node, "namespace-alias")) {
+ /* Top-level */
+ return;
+ } else if (IS_XSLT_NAME(node, "decimal-format")) {
+ /* Top-level */
+ return;
+ } else if (IS_XSLT_NAME(node, "include")) {
+ /* Top-level */
+ } else {
+ /*
+ * NOTE that xsl:text, xsl:template, xsl:stylesheet,
+ * xsl:transform, xsl:import, xsl:include are not expected
+ * to be handed over to this function.
+ */
+ xsltTransformError(NULL, style, node,
+ "Internal error: (xsltStylePreCompute) cannot handle "
+ "the XSLT element '%s'.\n", node->name);
+ style->errors++;
+ return;
+ }
+ }
+ /*
+ * Assign the current list of in-scope namespaces to the
+ * item. This is needed for XPath expressions.
+ */
+ if (node->psvi != NULL) {
+ ((xsltStylePreCompPtr) node->psvi)->inScopeNs =
+ XSLT_CCTXT(style)->inode->inScopeNs;
+ }
+}
+
+#else
+
+/**
+ * xsltStylePreCompute:
+ * @style: the XSLT stylesheet
+ * @inst: the instruction in the stylesheet
+ *
+ * Precompute an XSLT stylesheet element
+ */
+void
+xsltStylePreCompute(xsltStylesheetPtr style, xmlNodePtr inst) {
+ /*
+ * URGENT TODO: Normally inst->psvi Should never be reserved here,
+ * BUT: since if we include the same stylesheet from
+ * multiple imports, then the stylesheet will be parsed
+ * again. We simply must not try to compute the stylesheet again.
+ * TODO: Get to the point where we don't need to query the
+ * namespace- and local-name of the node, but can evaluate this
+ * using cctxt->style->inode->category;
+ */
+ if ((inst == NULL) || (inst->type != XML_ELEMENT_NODE) ||
+ (inst->psvi != NULL))
+ return;
+
+ if (IS_XSLT_ELEM(inst)) {
+ xsltStylePreCompPtr cur;
+
+ if (IS_XSLT_NAME(inst, "apply-templates")) {
+ xsltCheckInstructionElement(style, inst);
+ xsltApplyTemplatesComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "with-param")) {
+ xsltCheckParentElement(style, inst, BAD_CAST "apply-templates",
+ BAD_CAST "call-template");
+ xsltWithParamComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "value-of")) {
+ xsltCheckInstructionElement(style, inst);
+ xsltValueOfComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "copy")) {
+ xsltCheckInstructionElement(style, inst);
+ xsltCopyComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "copy-of")) {
+ xsltCheckInstructionElement(style, inst);
+ xsltCopyOfComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "if")) {
+ xsltCheckInstructionElement(style, inst);
+ xsltIfComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "when")) {
+ xsltCheckParentElement(style, inst, BAD_CAST "choose", NULL);
+ xsltWhenComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "choose")) {
+ xsltCheckInstructionElement(style, inst);
+ xsltChooseComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "for-each")) {
+ xsltCheckInstructionElement(style, inst);
+ xsltForEachComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "apply-imports")) {
+ xsltCheckInstructionElement(style, inst);
+ xsltApplyImportsComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "attribute")) {
+ xmlNodePtr parent = inst->parent;
+
+ if ((parent == NULL) ||
+ (parent->type != XML_ELEMENT_NODE) || (parent->ns == NULL) ||
+ ((parent->ns != inst->ns) &&
+ (!xmlStrEqual(parent->ns->href, inst->ns->href))) ||
+ (!xmlStrEqual(parent->name, BAD_CAST "attribute-set"))) {
+ xsltCheckInstructionElement(style, inst);
+ }
+ xsltAttributeComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "element")) {
+ xsltCheckInstructionElement(style, inst);
+ xsltElementComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "text")) {
+ xsltCheckInstructionElement(style, inst);
+ xsltTextComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "sort")) {
+ xsltCheckParentElement(style, inst, BAD_CAST "apply-templates",
+ BAD_CAST "for-each");
+ xsltSortComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "comment")) {
+ xsltCheckInstructionElement(style, inst);
+ xsltCommentComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "number")) {
+ xsltCheckInstructionElement(style, inst);
+ xsltNumberComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "processing-instruction")) {
+ xsltCheckInstructionElement(style, inst);
+ xsltProcessingInstructionComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "call-template")) {
+ xsltCheckInstructionElement(style, inst);
+ xsltCallTemplateComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "param")) {
+ if (xsltCheckTopLevelElement(style, inst, 0) == 0)
+ xsltCheckInstructionElement(style, inst);
+ xsltParamComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "variable")) {
+ if (xsltCheckTopLevelElement(style, inst, 0) == 0)
+ xsltCheckInstructionElement(style, inst);
+ xsltVariableComp(style, inst);
+ } else if (IS_XSLT_NAME(inst, "otherwise")) {
+ xsltCheckParentElement(style, inst, BAD_CAST "choose", NULL);
+ xsltCheckInstructionElement(style, inst);
+ return;
+ } else if (IS_XSLT_NAME(inst, "template")) {
+ xsltCheckTopLevelElement(style, inst, 1);
+ return;
+ } else if (IS_XSLT_NAME(inst, "output")) {
+ xsltCheckTopLevelElement(style, inst, 1);
+ return;
+ } else if (IS_XSLT_NAME(inst, "preserve-space")) {
+ xsltCheckTopLevelElement(style, inst, 1);
+ return;
+ } else if (IS_XSLT_NAME(inst, "strip-space")) {
+ xsltCheckTopLevelElement(style, inst, 1);
+ return;
+ } else if ((IS_XSLT_NAME(inst, "stylesheet")) ||
+ (IS_XSLT_NAME(inst, "transform"))) {
+ xmlNodePtr parent = inst->parent;
+
+ if ((parent == NULL) || (parent->type != XML_DOCUMENT_NODE)) {
+ xsltTransformError(NULL, style, inst,
+ "element %s only allowed only as root element\n",
+ inst->name);
+ style->errors++;
+ }
+ return;
+ } else if (IS_XSLT_NAME(inst, "key")) {
+ xsltCheckTopLevelElement(style, inst, 1);
+ return;
+ } else if (IS_XSLT_NAME(inst, "message")) {
+ xsltCheckInstructionElement(style, inst);
+ return;
+ } else if (IS_XSLT_NAME(inst, "attribute-set")) {
+ xsltCheckTopLevelElement(style, inst, 1);
+ return;
+ } else if (IS_XSLT_NAME(inst, "namespace-alias")) {
+ xsltCheckTopLevelElement(style, inst, 1);
+ return;
+ } else if (IS_XSLT_NAME(inst, "include")) {
+ xsltCheckTopLevelElement(style, inst, 1);
+ return;
+ } else if (IS_XSLT_NAME(inst, "import")) {
+ xsltCheckTopLevelElement(style, inst, 1);
+ return;
+ } else if (IS_XSLT_NAME(inst, "decimal-format")) {
+ xsltCheckTopLevelElement(style, inst, 1);
+ return;
+ } else if (IS_XSLT_NAME(inst, "fallback")) {
+ xsltCheckInstructionElement(style, inst);
+ return;
+ } else if (IS_XSLT_NAME(inst, "document")) {
+ xsltCheckInstructionElement(style, inst);
+ inst->psvi = (void *) xsltDocumentComp(style, inst,
+ (xsltTransformFunction) xsltDocumentElem);
+ } else if ((style == NULL) || (style->forwards_compatible == 0)) {
+ xsltTransformError(NULL, style, inst,
+ "xsltStylePreCompute: unknown xsl:%s\n", inst->name);
+ if (style != NULL) style->warnings++;
+ }
+
+ cur = (xsltStylePreCompPtr) inst->psvi;
+ /*
+ * A ns-list is build for every XSLT item in the
+ * node-tree. This is needed for XPath expressions.
+ */
+ if (cur != NULL) {
+ int i = 0;
+
+ cur->nsList = xmlGetNsList(inst->doc, inst);
+ if (cur->nsList != NULL) {
+ while (cur->nsList[i] != NULL)
+ i++;
+ }
+ cur->nsNr = i;
+ }
+ } else {
+ inst->psvi =
+ (void *) xsltPreComputeExtModuleElement(style, inst);
+
+ /*
+ * Unknown element, maybe registered at the context
+ * level. Mark it for later recognition.
+ */
+ if (inst->psvi == NULL)
+ inst->psvi = (void *) xsltExtMarker;
+ }
+}
+#endif /* XSLT_REFACTORED */
diff --git a/libxslt/preproc.h b/libxslt/preproc.h
new file mode 100644
index 0000000..caf464a
--- /dev/null
+++ b/libxslt/preproc.h
@@ -0,0 +1,43 @@
+/*
+ * Summary: precomputing stylesheets
+ * Description: this is the compilation phase, where most of the
+ * stylesheet is "compiled" into faster to use data.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_PRECOMP_H__
+#define __XML_XSLT_PRECOMP_H__
+
+#include <libxml/tree.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Interfaces
+ */
+extern const xmlChar *xsltExtMarker;
+
+XSLTPUBFUN xsltElemPreCompPtr XSLTCALL
+ xsltDocumentComp (xsltStylesheetPtr style,
+ xmlNodePtr inst,
+ xsltTransformFunction function);
+
+XSLTPUBFUN void XSLTCALL
+ xsltStylePreCompute (xsltStylesheetPtr style,
+ xmlNodePtr inst);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeStylePreComps (xsltStylesheetPtr style);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_PRECOMP_H__ */
+
diff --git a/libxslt/security.c b/libxslt/security.c
new file mode 100644
index 0000000..965175f
--- /dev/null
+++ b/libxslt/security.c
@@ -0,0 +1,480 @@
+/*
+ * security.c: Implementation of the XSLT security framework
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+#ifdef HAVE_NAN_H
+#include <nan.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#if defined(WIN32) && !defined(__CYGWIN__)
+#include <windows.h>
+#ifndef INVALID_FILE_ATTRIBUTES
+#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+#endif
+#endif
+
+#ifndef HAVE_STAT
+# ifdef HAVE__STAT
+ /* MS C library seems to define stat and _stat. The definition
+ * is identical. Still, mapping them to each other causes a warning. */
+# ifndef _MSC_VER
+# define stat(x,y) _stat(x,y)
+# endif
+# define HAVE_STAT
+# endif
+#endif
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/uri.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "xsltutils.h"
+#include "extensions.h"
+#include "security.h"
+
+
+struct _xsltSecurityPrefs {
+ xsltSecurityCheck readFile;
+ xsltSecurityCheck createFile;
+ xsltSecurityCheck createDir;
+ xsltSecurityCheck readNet;
+ xsltSecurityCheck writeNet;
+};
+
+static xsltSecurityPrefsPtr xsltDefaultSecurityPrefs = NULL;
+
+/************************************************************************
+ * *
+ * Module interfaces *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltNewSecurityPrefs:
+ *
+ * Create a new security preference block
+ *
+ * Returns a pointer to the new block or NULL in case of error
+ */
+xsltSecurityPrefsPtr
+xsltNewSecurityPrefs(void) {
+ xsltSecurityPrefsPtr ret;
+
+ xsltInitGlobals();
+
+ ret = (xsltSecurityPrefsPtr) xmlMalloc(sizeof(xsltSecurityPrefs));
+ if (ret == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltNewSecurityPrefs : malloc failed\n");
+ return(NULL);
+ }
+ memset(ret, 0, sizeof(xsltSecurityPrefs));
+ return(ret);
+}
+
+/**
+ * xsltFreeSecurityPrefs:
+ * @sec: the security block to free
+ *
+ * Free up a security preference block
+ */
+void
+xsltFreeSecurityPrefs(xsltSecurityPrefsPtr sec) {
+ if (sec == NULL)
+ return;
+ xmlFree(sec);
+}
+
+/**
+ * xsltSetSecurityPrefs:
+ * @sec: the security block to update
+ * @option: the option to update
+ * @func: the user callback to use for this option
+ *
+ * Update the security option to use the new callback checking function
+ *
+ * Returns -1 in case of error, 0 otherwise
+ */
+int
+xsltSetSecurityPrefs(xsltSecurityPrefsPtr sec, xsltSecurityOption option,
+ xsltSecurityCheck func) {
+ xsltInitGlobals();
+ if (sec == NULL)
+ return(-1);
+ switch (option) {
+ case XSLT_SECPREF_READ_FILE:
+ sec->readFile = func; return(0);
+ case XSLT_SECPREF_WRITE_FILE:
+ sec->createFile = func; return(0);
+ case XSLT_SECPREF_CREATE_DIRECTORY:
+ sec->createDir = func; return(0);
+ case XSLT_SECPREF_READ_NETWORK:
+ sec->readNet = func; return(0);
+ case XSLT_SECPREF_WRITE_NETWORK:
+ sec->writeNet = func; return(0);
+ }
+ return(-1);
+}
+
+/**
+ * xsltGetSecurityPrefs:
+ * @sec: the security block to update
+ * @option: the option to lookup
+ *
+ * Lookup the security option to get the callback checking function
+ *
+ * Returns NULL if not found, the function otherwise
+ */
+xsltSecurityCheck
+xsltGetSecurityPrefs(xsltSecurityPrefsPtr sec, xsltSecurityOption option) {
+ if (sec == NULL)
+ return(NULL);
+ switch (option) {
+ case XSLT_SECPREF_READ_FILE:
+ return(sec->readFile);
+ case XSLT_SECPREF_WRITE_FILE:
+ return(sec->createFile);
+ case XSLT_SECPREF_CREATE_DIRECTORY:
+ return(sec->createDir);
+ case XSLT_SECPREF_READ_NETWORK:
+ return(sec->readNet);
+ case XSLT_SECPREF_WRITE_NETWORK:
+ return(sec->writeNet);
+ }
+ return(NULL);
+}
+
+/**
+ * xsltSetDefaultSecurityPrefs:
+ * @sec: the security block to use
+ *
+ * Set the default security preference application-wide
+ */
+void
+xsltSetDefaultSecurityPrefs(xsltSecurityPrefsPtr sec) {
+
+ xsltDefaultSecurityPrefs = sec;
+}
+
+/**
+ * xsltGetDefaultSecurityPrefs:
+ *
+ * Get the default security preference application-wide
+ *
+ * Returns the current xsltSecurityPrefsPtr in use or NULL if none
+ */
+xsltSecurityPrefsPtr
+xsltGetDefaultSecurityPrefs(void) {
+ return(xsltDefaultSecurityPrefs);
+}
+
+/**
+ * xsltSetCtxtSecurityPrefs:
+ * @sec: the security block to use
+ * @ctxt: an XSLT transformation context
+ *
+ * Set the security preference for a specific transformation
+ *
+ * Returns -1 in case of error, 0 otherwise
+ */
+int
+xsltSetCtxtSecurityPrefs(xsltSecurityPrefsPtr sec,
+ xsltTransformContextPtr ctxt) {
+ if (ctxt == NULL)
+ return(-1);
+ ctxt->sec = (void *) sec;
+ return(0);
+}
+
+
+/**
+ * xsltSecurityAllow:
+ * @sec: the security block to use
+ * @ctxt: an XSLT transformation context
+ * @value: unused
+ *
+ * Function used to always allow an operation
+ *
+ * Returns 1 always
+ */
+int
+xsltSecurityAllow(xsltSecurityPrefsPtr sec ATTRIBUTE_UNUSED,
+ xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
+ const char *value ATTRIBUTE_UNUSED) {
+ return(1);
+}
+
+/**
+ * xsltSecurityForbid:
+ * @sec: the security block to use
+ * @ctxt: an XSLT transformation context
+ * @value: unused
+ *
+ * Function used to always forbid an operation
+ *
+ * Returns 0 always
+ */
+int
+xsltSecurityForbid(xsltSecurityPrefsPtr sec ATTRIBUTE_UNUSED,
+ xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
+ const char *value ATTRIBUTE_UNUSED) {
+ return(0);
+}
+
+/************************************************************************
+ * *
+ * Internal interfaces *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltCheckFilename
+ * @path: the path to check
+ *
+ * function checks to see if @path is a valid source
+ * (file, socket...) for XML.
+ *
+ * TODO: remove at some point !!!
+ * Local copy of xmlCheckFilename to avoid a hard dependency on
+ * a new version of libxml2
+ *
+ * if stat is not available on the target machine,
+ * returns 1. if stat fails, returns 0 (if calling
+ * stat on the filename fails, it can't be right).
+ * if stat succeeds and the file is a directory,
+ * returns 2. otherwise returns 1.
+ */
+
+static int
+xsltCheckFilename (const char *path)
+{
+#ifdef HAVE_STAT
+ struct stat stat_buffer;
+#if defined(WIN32) && !defined(__CYGWIN__)
+ DWORD dwAttrs;
+
+ dwAttrs = GetFileAttributes(path);
+ if (dwAttrs != INVALID_FILE_ATTRIBUTES) {
+ if (dwAttrs & FILE_ATTRIBUTE_DIRECTORY) {
+ return 2;
+ }
+ }
+#endif
+
+ if (stat(path, &stat_buffer) == -1)
+ return 0;
+
+#ifdef S_ISDIR
+ if (S_ISDIR(stat_buffer.st_mode)) {
+ return 2;
+ }
+#endif
+#endif
+ return 1;
+}
+
+static int
+xsltCheckWritePath(xsltSecurityPrefsPtr sec,
+ xsltTransformContextPtr ctxt,
+ const char *path)
+{
+ int ret;
+ xsltSecurityCheck check;
+ char *directory;
+
+ check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_WRITE_FILE);
+ if (check != NULL) {
+ ret = check(sec, ctxt, path);
+ if (ret == 0) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "File write for %s refused\n", path);
+ return(0);
+ }
+ }
+
+ directory = xmlParserGetDirectory (path);
+
+ if (directory != NULL) {
+ ret = xsltCheckFilename(directory);
+ if (ret == 0) {
+ /*
+ * The directory doesn't exist check for creation
+ */
+ check = xsltGetSecurityPrefs(sec,
+ XSLT_SECPREF_CREATE_DIRECTORY);
+ if (check != NULL) {
+ ret = check(sec, ctxt, directory);
+ if (ret == 0) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "Directory creation for %s refused\n",
+ path);
+ xmlFree(directory);
+ return(0);
+ }
+ }
+ ret = xsltCheckWritePath(sec, ctxt, directory);
+ if (ret == 1)
+ ret = mkdir(directory, 0755);
+ }
+ xmlFree(directory);
+ if (ret < 0)
+ return(ret);
+ }
+
+ return(1);
+}
+
+/**
+ * xsltCheckWrite:
+ * @sec: the security options
+ * @ctxt: an XSLT transformation context
+ * @URL: the resource to be written
+ *
+ * Check if the resource is allowed to be written, if necessary makes
+ * some preliminary work like creating directories
+ *
+ * Return 1 if write is allowed, 0 if not and -1 in case or error.
+ */
+int
+xsltCheckWrite(xsltSecurityPrefsPtr sec,
+ xsltTransformContextPtr ctxt, const xmlChar *URL) {
+ int ret;
+ xmlURIPtr uri;
+ xsltSecurityCheck check;
+
+ uri = xmlParseURI((const char *)URL);
+ if (uri == NULL) {
+ uri = xmlCreateURI();
+ if (uri == NULL) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "xsltCheckWrite: out of memory for %s\n", URL);
+ return(-1);
+ }
+ uri->path = (char *)xmlStrdup(URL);
+ }
+ if ((uri->scheme == NULL) ||
+ (xmlStrEqual(BAD_CAST uri->scheme, BAD_CAST "file"))) {
+
+#if defined(WIN32) && !defined(__CYGWIN__)
+ if ((uri->path)&&(uri->path[0]=='/')&&
+ (uri->path[1]!='\0')&&(uri->path[2]==':'))
+ ret = xsltCheckWritePath(sec, ctxt, uri->path+1);
+ else
+#endif
+
+ /*
+ * Check if we are allowed to write this file
+ */
+ ret = xsltCheckWritePath(sec, ctxt, uri->path);
+ if (ret <= 0) {
+ xmlFreeURI(uri);
+ return(ret);
+ }
+ } else {
+ /*
+ * Check if we are allowed to write this network resource
+ */
+ check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_WRITE_NETWORK);
+ if (check != NULL) {
+ ret = check(sec, ctxt, (const char *)URL);
+ if (ret == 0) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "File write for %s refused\n", URL);
+ xmlFreeURI(uri);
+ return(0);
+ }
+ }
+ }
+ xmlFreeURI(uri);
+ return(1);
+}
+
+
+/**
+ * xsltCheckRead:
+ * @sec: the security options
+ * @ctxt: an XSLT transformation context
+ * @URL: the resource to be read
+ *
+ * Check if the resource is allowed to be read
+ *
+ * Return 1 if read is allowed, 0 if not and -1 in case or error.
+ */
+int
+xsltCheckRead(xsltSecurityPrefsPtr sec,
+ xsltTransformContextPtr ctxt, const xmlChar *URL) {
+ int ret;
+ xmlURIPtr uri;
+ xsltSecurityCheck check;
+
+ uri = xmlParseURI((const char *)URL);
+ if (uri == NULL) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "xsltCheckRead: URL parsing failed for %s\n",
+ URL);
+ return(-1);
+ }
+ if ((uri->scheme == NULL) ||
+ (xmlStrEqual(BAD_CAST uri->scheme, BAD_CAST "file"))) {
+
+ /*
+ * Check if we are allowed to read this file
+ */
+ check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_READ_FILE);
+ if (check != NULL) {
+ ret = check(sec, ctxt, uri->path);
+ if (ret == 0) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "Local file read for %s refused\n", URL);
+ xmlFreeURI(uri);
+ return(0);
+ }
+ }
+ } else {
+ /*
+ * Check if we are allowed to write this network resource
+ */
+ check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_READ_NETWORK);
+ if (check != NULL) {
+ ret = check(sec, ctxt, (const char *)URL);
+ if (ret == 0) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "Network file read for %s refused\n", URL);
+ xmlFreeURI(uri);
+ return(0);
+ }
+ }
+ }
+ xmlFreeURI(uri);
+ return(1);
+}
+
diff --git a/libxslt/security.h b/libxslt/security.h
new file mode 100644
index 0000000..bab5c8c
--- /dev/null
+++ b/libxslt/security.h
@@ -0,0 +1,104 @@
+/*
+ * Summary: interface for the libxslt security framework
+ * Description: the libxslt security framework allow to restrict
+ * the access to new resources (file or URL) from
+ * the stylesheet at runtime.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_SECURITY_H__
+#define __XML_XSLT_SECURITY_H__
+
+#include <libxml/tree.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * xsltSecurityPref:
+ *
+ * structure to indicate the preferences for security in the XSLT
+ * transformation.
+ */
+typedef struct _xsltSecurityPrefs xsltSecurityPrefs;
+typedef xsltSecurityPrefs *xsltSecurityPrefsPtr;
+
+/**
+ * xsltSecurityOption:
+ *
+ * the set of option that can be configured
+ */
+typedef enum {
+ XSLT_SECPREF_READ_FILE = 1,
+ XSLT_SECPREF_WRITE_FILE,
+ XSLT_SECPREF_CREATE_DIRECTORY,
+ XSLT_SECPREF_READ_NETWORK,
+ XSLT_SECPREF_WRITE_NETWORK
+} xsltSecurityOption;
+
+/**
+ * xsltSecurityCheck:
+ *
+ * User provided function to check the value of a string like a file
+ * path or an URL ...
+ */
+typedef int (*xsltSecurityCheck) (xsltSecurityPrefsPtr sec,
+ xsltTransformContextPtr ctxt,
+ const char *value);
+
+/*
+ * Module interfaces
+ */
+XSLTPUBFUN xsltSecurityPrefsPtr XSLTCALL
+ xsltNewSecurityPrefs (void);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeSecurityPrefs (xsltSecurityPrefsPtr sec);
+XSLTPUBFUN int XSLTCALL
+ xsltSetSecurityPrefs (xsltSecurityPrefsPtr sec,
+ xsltSecurityOption option,
+ xsltSecurityCheck func);
+XSLTPUBFUN xsltSecurityCheck XSLTCALL
+ xsltGetSecurityPrefs (xsltSecurityPrefsPtr sec,
+ xsltSecurityOption option);
+
+XSLTPUBFUN void XSLTCALL
+ xsltSetDefaultSecurityPrefs (xsltSecurityPrefsPtr sec);
+XSLTPUBFUN xsltSecurityPrefsPtr XSLTCALL
+ xsltGetDefaultSecurityPrefs (void);
+
+XSLTPUBFUN int XSLTCALL
+ xsltSetCtxtSecurityPrefs (xsltSecurityPrefsPtr sec,
+ xsltTransformContextPtr ctxt);
+
+XSLTPUBFUN int XSLTCALL
+ xsltSecurityAllow (xsltSecurityPrefsPtr sec,
+ xsltTransformContextPtr ctxt,
+ const char *value);
+XSLTPUBFUN int XSLTCALL
+ xsltSecurityForbid (xsltSecurityPrefsPtr sec,
+ xsltTransformContextPtr ctxt,
+ const char *value);
+/*
+ * internal interfaces
+ */
+XSLTPUBFUN int XSLTCALL
+ xsltCheckWrite (xsltSecurityPrefsPtr sec,
+ xsltTransformContextPtr ctxt,
+ const xmlChar *URL);
+XSLTPUBFUN int XSLTCALL
+ xsltCheckRead (xsltSecurityPrefsPtr sec,
+ xsltTransformContextPtr ctxt,
+ const xmlChar *URL);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_SECURITY_H__ */
+
diff --git a/libxslt/templates.c b/libxslt/templates.c
new file mode 100644
index 0000000..02193f7
--- /dev/null
+++ b/libxslt/templates.c
@@ -0,0 +1,843 @@
+/*
+ * templates.c: Implementation of the template processing
+ *
+ * Reference:
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/globals.h>
+#include <libxml/xmlerror.h>
+#include <libxml/tree.h>
+#include <libxml/dict.h>
+#include <libxml/xpathInternals.h>
+#include <libxml/parserInternals.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "xsltutils.h"
+#include "variables.h"
+#include "functions.h"
+#include "templates.h"
+#include "transform.h"
+#include "namespaces.h"
+#include "attributes.h"
+
+#ifdef WITH_XSLT_DEBUG
+#define WITH_XSLT_DEBUG_TEMPLATES
+#endif
+
+/************************************************************************
+ * *
+ * Module interfaces *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltEvalXPathPredicate:
+ * @ctxt: the XSLT transformation context
+ * @comp: the XPath compiled expression
+ * @nsList: the namespaces in scope
+ * @nsNr: the number of namespaces in scope
+ *
+ * Process the expression using XPath and evaluate the result as
+ * an XPath predicate
+ *
+ * Returns 1 is the predicate was true, 0 otherwise
+ */
+int
+xsltEvalXPathPredicate(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp,
+ xmlNsPtr *nsList, int nsNr) {
+ int ret;
+ xmlXPathObjectPtr res;
+ int oldNsNr;
+ xmlNsPtr *oldNamespaces;
+ xmlNodePtr oldInst;
+ int oldProximityPosition, oldContextSize;
+
+ oldContextSize = ctxt->xpathCtxt->contextSize;
+ oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
+ oldNsNr = ctxt->xpathCtxt->nsNr;
+ oldNamespaces = ctxt->xpathCtxt->namespaces;
+ oldInst = ctxt->inst;
+
+ ctxt->xpathCtxt->node = ctxt->node;
+ ctxt->xpathCtxt->namespaces = nsList;
+ ctxt->xpathCtxt->nsNr = nsNr;
+
+ res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
+
+ if (res != NULL) {
+ ret = xmlXPathEvalPredicate(ctxt->xpathCtxt, res);
+ xmlXPathFreeObject(res);
+#ifdef WITH_XSLT_DEBUG_TEMPLATES
+ XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltEvalXPathPredicate: returns %d\n", ret));
+#endif
+ } else {
+#ifdef WITH_XSLT_DEBUG_TEMPLATES
+ XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltEvalXPathPredicate: failed\n"));
+#endif
+ ctxt->state = XSLT_STATE_STOPPED;
+ ret = 0;
+ }
+ ctxt->xpathCtxt->nsNr = oldNsNr;
+
+ ctxt->xpathCtxt->namespaces = oldNamespaces;
+ ctxt->inst = oldInst;
+ ctxt->xpathCtxt->contextSize = oldContextSize;
+ ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
+
+ return(ret);
+}
+
+/**
+ * xsltEvalXPathStringNs:
+ * @ctxt: the XSLT transformation context
+ * @comp: the compiled XPath expression
+ * @nsNr: the number of namespaces in the list
+ * @nsList: the list of in-scope namespaces to use
+ *
+ * Process the expression using XPath, allowing to pass a namespace mapping
+ * context and get a string
+ *
+ * Returns the computed string value or NULL, must be deallocated by the
+ * caller.
+ */
+xmlChar *
+xsltEvalXPathStringNs(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp,
+ int nsNr, xmlNsPtr *nsList) {
+ xmlChar *ret = NULL;
+ xmlXPathObjectPtr res;
+ xmlNodePtr oldInst;
+ xmlNodePtr oldNode;
+ int oldPos, oldSize;
+ int oldNsNr;
+ xmlNsPtr *oldNamespaces;
+
+ oldInst = ctxt->inst;
+ oldNode = ctxt->node;
+ oldPos = ctxt->xpathCtxt->proximityPosition;
+ oldSize = ctxt->xpathCtxt->contextSize;
+ oldNsNr = ctxt->xpathCtxt->nsNr;
+ oldNamespaces = ctxt->xpathCtxt->namespaces;
+
+ ctxt->xpathCtxt->node = ctxt->node;
+ /* TODO: do we need to propagate the namespaces here ? */
+ ctxt->xpathCtxt->namespaces = nsList;
+ ctxt->xpathCtxt->nsNr = nsNr;
+ res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
+ if (res != NULL) {
+ if (res->type != XPATH_STRING)
+ res = xmlXPathConvertString(res);
+ if (res->type == XPATH_STRING) {
+ ret = res->stringval;
+ res->stringval = NULL;
+ } else {
+ xsltTransformError(ctxt, NULL, NULL,
+ "xpath : string() function didn't return a String\n");
+ }
+ xmlXPathFreeObject(res);
+ } else {
+ ctxt->state = XSLT_STATE_STOPPED;
+ }
+#ifdef WITH_XSLT_DEBUG_TEMPLATES
+ XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltEvalXPathString: returns %s\n", ret));
+#endif
+ ctxt->inst = oldInst;
+ ctxt->node = oldNode;
+ ctxt->xpathCtxt->contextSize = oldSize;
+ ctxt->xpathCtxt->proximityPosition = oldPos;
+ ctxt->xpathCtxt->nsNr = oldNsNr;
+ ctxt->xpathCtxt->namespaces = oldNamespaces;
+ return(ret);
+}
+
+/**
+ * xsltEvalXPathString:
+ * @ctxt: the XSLT transformation context
+ * @comp: the compiled XPath expression
+ *
+ * Process the expression using XPath and get a string
+ *
+ * Returns the computed string value or NULL, must be deallocated by the
+ * caller.
+ */
+xmlChar *
+xsltEvalXPathString(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp) {
+ return(xsltEvalXPathStringNs(ctxt, comp, 0, NULL));
+}
+
+/**
+ * xsltEvalTemplateString:
+ * @ctxt: the XSLT transformation context
+ * @contextNode: the current node in the source tree
+ * @inst: the XSLT instruction (xsl:comment, xsl:processing-instruction)
+ *
+ * Processes the sequence constructor of the given instruction on
+ * @contextNode and converts the resulting tree to a string.
+ * This is needed by e.g. xsl:comment and xsl:processing-instruction.
+ *
+ * Returns the computed string value or NULL; it's up to the caller to
+ * free the result.
+ */
+xmlChar *
+xsltEvalTemplateString(xsltTransformContextPtr ctxt,
+ xmlNodePtr contextNode,
+ xmlNodePtr inst)
+{
+ xmlNodePtr oldInsert, insert = NULL;
+ xmlChar *ret;
+
+ if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL) ||
+ (inst->type != XML_ELEMENT_NODE))
+ return(NULL);
+
+ if (inst->children == NULL)
+ return(NULL);
+
+ /*
+ * This creates a temporary element-node to add the resulting
+ * text content to.
+ * OPTIMIZE TODO: Keep such an element-node in the transformation
+ * context to avoid creating it every time.
+ */
+ insert = xmlNewDocNode(ctxt->output, NULL,
+ (const xmlChar *)"fake", NULL);
+ if (insert == NULL) {
+ xsltTransformError(ctxt, NULL, contextNode,
+ "Failed to create temporary node\n");
+ return(NULL);
+ }
+ oldInsert = ctxt->insert;
+ ctxt->insert = insert;
+ /*
+ * OPTIMIZE TODO: if inst->children consists only of text-nodes.
+ */
+ xsltApplyOneTemplate(ctxt, contextNode, inst->children, NULL, NULL);
+
+ ctxt->insert = oldInsert;
+
+ ret = xmlNodeGetContent(insert);
+ if (insert != NULL)
+ xmlFreeNode(insert);
+ return(ret);
+}
+
+/**
+ * xsltAttrTemplateValueProcessNode:
+ * @ctxt: the XSLT transformation context
+ * @str: the attribute template node value
+ * @inst: the instruction (or LRE) in the stylesheet holding the
+ * attribute with an AVT
+ *
+ * Process the given string, allowing to pass a namespace mapping
+ * context and return the new string value.
+ *
+ * Called by:
+ * - xsltAttrTemplateValueProcess() (templates.c)
+ * - xsltEvalAttrValueTemplate() (templates.c)
+ *
+ * QUESTION: Why is this function public? It is not used outside
+ * of templates.c.
+ *
+ * Returns the computed string value or NULL, must be deallocated by the
+ * caller.
+ */
+xmlChar *
+xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt,
+ const xmlChar *str, xmlNodePtr inst)
+{
+ xmlChar *ret = NULL;
+ const xmlChar *cur;
+ xmlChar *expr, *val;
+ xmlNsPtr *nsList = NULL;
+ int nsNr = 0;
+
+ if (str == NULL) return(NULL);
+ if (*str == 0)
+ return(xmlStrndup((xmlChar *)"", 0));
+
+ cur = str;
+ while (*cur != 0) {
+ if (*cur == '{') {
+ if (*(cur+1) == '{') { /* escaped '{' */
+ cur++;
+ ret = xmlStrncat(ret, str, cur - str);
+ cur++;
+ str = cur;
+ continue;
+ }
+ ret = xmlStrncat(ret, str, cur - str);
+ str = cur;
+ cur++;
+ while ((*cur != 0) && (*cur != '}')) {
+ /* Need to check for literal (bug539741) */
+ if ((*cur == '\'') || (*cur == '"')) {
+ char delim = *(cur++);
+ while ((*cur != 0) && (*cur != delim))
+ cur++;
+ if (*cur != 0)
+ cur++; /* skip the ending delimiter */
+ } else
+ cur++;
+ }
+ if (*cur == 0) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsltAttrTemplateValueProcessNode: unmatched '{'\n");
+ ret = xmlStrncat(ret, str, cur - str);
+ return(ret);
+ }
+ str++;
+ expr = xmlStrndup(str, cur - str);
+ if (expr == NULL)
+ return(ret);
+ else if (*expr == '{') {
+ ret = xmlStrcat(ret, expr);
+ xmlFree(expr);
+ } else {
+ xmlXPathCompExprPtr comp;
+ /*
+ * TODO: keep precompiled form around
+ */
+ if ((nsList == NULL) && (inst != NULL)) {
+ int i = 0;
+
+ nsList = xmlGetNsList(inst->doc, inst);
+ if (nsList != NULL) {
+ while (nsList[i] != NULL)
+ i++;
+ nsNr = i;
+ }
+ }
+ comp = xmlXPathCompile(expr);
+ val = xsltEvalXPathStringNs(ctxt, comp, nsNr, nsList);
+ xmlXPathFreeCompExpr(comp);
+ xmlFree(expr);
+ if (val != NULL) {
+ ret = xmlStrcat(ret, val);
+ xmlFree(val);
+ }
+ }
+ cur++;
+ str = cur;
+ } else if (*cur == '}') {
+ cur++;
+ if (*cur == '}') { /* escaped '}' */
+ ret = xmlStrncat(ret, str, cur - str);
+ cur++;
+ str = cur;
+ continue;
+ } else {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsltAttrTemplateValueProcessNode: unmatched '}'\n");
+ }
+ } else
+ cur++;
+ }
+ if (cur != str) {
+ ret = xmlStrncat(ret, str, cur - str);
+ }
+
+ if (nsList != NULL)
+ xmlFree(nsList);
+
+ return(ret);
+}
+
+/**
+ * xsltAttrTemplateValueProcess:
+ * @ctxt: the XSLT transformation context
+ * @str: the attribute template node value
+ *
+ * Process the given node and return the new string value.
+ *
+ * Returns the computed string value or NULL, must be deallocated by the
+ * caller.
+ */
+xmlChar *
+xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt, const xmlChar *str) {
+ return(xsltAttrTemplateValueProcessNode(ctxt, str, NULL));
+}
+
+/**
+ * xsltEvalAttrValueTemplate:
+ * @ctxt: the XSLT transformation context
+ * @inst: the instruction (or LRE) in the stylesheet holding the
+ * attribute with an AVT
+ * @name: the attribute QName
+ * @ns: the attribute namespace URI
+ *
+ * Evaluate a attribute value template, i.e. the attribute value can
+ * contain expressions contained in curly braces ({}) and those are
+ * substituted by they computed value.
+ *
+ * Returns the computed string value or NULL, must be deallocated by the
+ * caller.
+ */
+xmlChar *
+xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst,
+ const xmlChar *name, const xmlChar *ns)
+{
+ xmlChar *ret;
+ xmlChar *expr;
+
+ if ((ctxt == NULL) || (inst == NULL) || (name == NULL) ||
+ (inst->type != XML_ELEMENT_NODE))
+ return(NULL);
+
+ expr = xsltGetNsProp(inst, name, ns);
+ if (expr == NULL)
+ return(NULL);
+
+ /*
+ * TODO: though now {} is detected ahead, it would still be good to
+ * optimize both functions to keep the splitted value if the
+ * attribute content and the XPath precompiled expressions around
+ */
+
+ ret = xsltAttrTemplateValueProcessNode(ctxt, expr, inst);
+#ifdef WITH_XSLT_DEBUG_TEMPLATES
+ XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret));
+#endif
+ if (expr != NULL)
+ xmlFree(expr);
+ return(ret);
+}
+
+/**
+ * xsltEvalStaticAttrValueTemplate:
+ * @style: the XSLT stylesheet
+ * @inst: the instruction (or LRE) in the stylesheet holding the
+ * attribute with an AVT
+ * @name: the attribute Name
+ * @ns: the attribute namespace URI
+ * @found: indicator whether the attribute is present
+ *
+ * Check if an attribute value template has a static value, i.e. the
+ * attribute value does not contain expressions contained in curly braces ({})
+ *
+ * Returns the static string value or NULL, must be deallocated by the
+ * caller.
+ */
+const xmlChar *
+xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr inst,
+ const xmlChar *name, const xmlChar *ns, int *found) {
+ const xmlChar *ret;
+ xmlChar *expr;
+
+ if ((style == NULL) || (inst == NULL) || (name == NULL) ||
+ (inst->type != XML_ELEMENT_NODE))
+ return(NULL);
+
+ expr = xsltGetNsProp(inst, name, ns);
+ if (expr == NULL) {
+ *found = 0;
+ return(NULL);
+ }
+ *found = 1;
+
+ ret = xmlStrchr(expr, '{');
+ if (ret != NULL) {
+ xmlFree(expr);
+ return(NULL);
+ }
+ ret = xmlDictLookup(style->dict, expr, -1);
+ xmlFree(expr);
+ return(ret);
+}
+
+/**
+ * xsltAttrTemplateProcess:
+ * @ctxt: the XSLT transformation context
+ * @target: the element where the attribute will be grafted
+ * @attr: the attribute node of a literal result element
+ *
+ * Process one attribute of a Literal Result Element (in the stylesheet).
+ * Evaluates Attribute Value Templates and copies the attribute over to
+ * the result element.
+ * This does *not* process attribute sets (xsl:use-attribute-set).
+ *
+ *
+ * Returns the generated attribute node.
+ */
+xmlAttrPtr
+xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target,
+ xmlAttrPtr attr)
+{
+ const xmlChar *value;
+ xmlAttrPtr ret;
+
+ if ((ctxt == NULL) || (attr == NULL) || (target == NULL) ||
+ (target->type != XML_ELEMENT_NODE))
+ return(NULL);
+
+ if (attr->type != XML_ATTRIBUTE_NODE)
+ return(NULL);
+
+ /*
+ * Skip all XSLT attributes.
+ */
+#ifdef XSLT_REFACTORED
+ if (attr->psvi == xsltXSLTAttrMarker)
+ return(NULL);
+#else
+ if ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, XSLT_NAMESPACE))
+ return(NULL);
+#endif
+ /*
+ * Get the value.
+ */
+ if (attr->children != NULL) {
+ if ((attr->children->type != XML_TEXT_NODE) ||
+ (attr->children->next != NULL))
+ {
+ xsltTransformError(ctxt, NULL, attr->parent,
+ "Internal error: The children of an attribute node of a "
+ "literal result element are not in the expected form.\n");
+ return(NULL);
+ }
+ value = attr->children->content;
+ if (value == NULL)
+ value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
+ } else
+ value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
+ /*
+ * Overwrite duplicates.
+ */
+ ret = target->properties;
+ while (ret != NULL) {
+ if (((attr->ns != NULL) == (ret->ns != NULL)) &&
+ xmlStrEqual(ret->name, attr->name) &&
+ ((attr->ns == NULL) || xmlStrEqual(ret->ns->href, attr->ns->href)))
+ {
+ break;
+ }
+ ret = ret->next;
+ }
+ if (ret != NULL) {
+ /* free the existing value */
+ xmlFreeNodeList(ret->children);
+ ret->children = ret->last = NULL;
+ /*
+ * Adjust ns-prefix if needed.
+ */
+ if ((ret->ns != NULL) &&
+ (! xmlStrEqual(ret->ns->prefix, attr->ns->prefix)))
+ {
+ ret->ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target);
+ }
+ } else {
+ /* create a new attribute */
+ if (attr->ns != NULL)
+ ret = xmlNewNsProp(target,
+ xsltGetNamespace(ctxt, attr->parent, attr->ns, target),
+ attr->name, NULL);
+ else
+ ret = xmlNewNsProp(target, NULL, attr->name, NULL);
+ }
+ /*
+ * Set the value.
+ */
+ if (ret != NULL) {
+ xmlNodePtr text;
+
+ text = xmlNewText(NULL);
+ if (text != NULL) {
+ ret->last = ret->children = text;
+ text->parent = (xmlNodePtr) ret;
+ text->doc = ret->doc;
+
+ if (attr->psvi != NULL) {
+ /*
+ * Evaluate the Attribute Value Template.
+ */
+ xmlChar *val;
+ val = xsltEvalAVT(ctxt, attr->psvi, attr->parent);
+ if (val == NULL) {
+ /*
+ * TODO: Damn, we need an easy mechanism to report
+ * qualified names!
+ */
+ if (attr->ns) {
+ xsltTransformError(ctxt, NULL, attr->parent,
+ "Internal error: Failed to evaluate the AVT "
+ "of attribute '{%s}%s'.\n",
+ attr->ns->href, attr->name);
+ } else {
+ xsltTransformError(ctxt, NULL, attr->parent,
+ "Internal error: Failed to evaluate the AVT "
+ "of attribute '%s'.\n",
+ attr->name);
+ }
+ text->content = xmlStrdup(BAD_CAST "");
+ } else {
+ text->content = val;
+ }
+ } else if ((ctxt->internalized) && (target != NULL) &&
+ (target->doc != NULL) &&
+ (target->doc->dict == ctxt->dict) &&
+ xmlDictOwns(ctxt->dict, value)) {
+ text->content = (xmlChar *) value;
+ } else {
+ text->content = xmlStrdup(value);
+ }
+ }
+ } else {
+ if (attr->ns) {
+ xsltTransformError(ctxt, NULL, attr->parent,
+ "Internal error: Failed to create attribute '{%s}%s'.\n",
+ attr->ns->href, attr->name);
+ } else {
+ xsltTransformError(ctxt, NULL, attr->parent,
+ "Internal error: Failed to create attribute '%s'.\n",
+ attr->name);
+ }
+ }
+ return(ret);
+}
+
+
+/**
+ * xsltAttrListTemplateProcess:
+ * @ctxt: the XSLT transformation context
+ * @target: the element where the attributes will be grafted
+ * @attrs: the first attribute
+ *
+ * Processes all attributes of a Literal Result Element.
+ * Attribute references are applied via xsl:use-attribute-set
+ * attributes.
+ * Copies all non XSLT-attributes over to the @target element
+ * and evaluates Attribute Value Templates.
+ *
+ * Called by xsltApplySequenceConstructor() (transform.c).
+ *
+ * Returns a new list of attribute nodes, or NULL in case of error.
+ * (Don't assign the result to @target->properties; if
+ * the result is NULL, you'll get memory leaks, since the
+ * attributes will be disattached.)
+ */
+xmlAttrPtr
+xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt,
+ xmlNodePtr target, xmlAttrPtr attrs)
+{
+ xmlAttrPtr attr, copy, last;
+ xmlNodePtr oldInsert, text;
+ xmlNsPtr origNs = NULL, copyNs = NULL;
+ const xmlChar *value;
+ xmlChar *valueAVT;
+
+ if ((ctxt == NULL) || (target == NULL) || (attrs == NULL) ||
+ (target->type != XML_ELEMENT_NODE))
+ return(NULL);
+
+ oldInsert = ctxt->insert;
+ ctxt->insert = target;
+
+ /*
+ * Instantiate LRE-attributes.
+ */
+ if (target->properties) {
+ last = target->properties;
+ while (last->next != NULL)
+ last = last->next;
+ } else {
+ last = NULL;
+ }
+ attr = attrs;
+ do {
+ /*
+ * Skip XSLT attributes.
+ */
+#ifdef XSLT_REFACTORED
+ if (attr->psvi == xsltXSLTAttrMarker) {
+ goto next_attribute;
+ }
+#else
+ if ((attr->ns != NULL) &&
+ xmlStrEqual(attr->ns->href, XSLT_NAMESPACE))
+ {
+ goto next_attribute;
+ }
+#endif
+ /*
+ * Get the value.
+ */
+ if (attr->children != NULL) {
+ if ((attr->children->type != XML_TEXT_NODE) ||
+ (attr->children->next != NULL))
+ {
+ xsltTransformError(ctxt, NULL, attr->parent,
+ "Internal error: The children of an attribute node of a "
+ "literal result element are not in the expected form.\n");
+ goto error;
+ }
+ value = attr->children->content;
+ if (value == NULL)
+ value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
+ } else
+ value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
+
+ /*
+ * Create a new attribute.
+ */
+ copy = xmlNewDocProp(target->doc, attr->name, NULL);
+ if (copy == NULL) {
+ if (attr->ns) {
+ xsltTransformError(ctxt, NULL, attr->parent,
+ "Internal error: Failed to create attribute '{%s}%s'.\n",
+ attr->ns->href, attr->name);
+ } else {
+ xsltTransformError(ctxt, NULL, attr->parent,
+ "Internal error: Failed to create attribute '%s'.\n",
+ attr->name);
+ }
+ goto error;
+ }
+ /*
+ * Attach it to the target element.
+ */
+ copy->parent = target;
+ if (last == NULL) {
+ target->properties = copy;
+ last = copy;
+ } else {
+ last->next = copy;
+ copy->prev = last;
+ last = copy;
+ }
+ /*
+ * Set the namespace. Avoid lookups of same namespaces.
+ */
+ if (attr->ns != origNs) {
+ origNs = attr->ns;
+ if (attr->ns != NULL) {
+#ifdef XSLT_REFACTORED
+ copyNs = xsltGetSpecialNamespace(ctxt, attr->parent,
+ attr->ns->href, attr->ns->prefix, target);
+#else
+ copyNs = xsltGetNamespace(ctxt, attr->parent,
+ attr->ns, target);
+#endif
+ if (copyNs == NULL)
+ goto error;
+ } else
+ copyNs = NULL;
+ }
+ copy->ns = copyNs;
+
+ /*
+ * Set the value.
+ */
+ text = xmlNewText(NULL);
+ if (text != NULL) {
+ copy->last = copy->children = text;
+ text->parent = (xmlNodePtr) copy;
+ text->doc = copy->doc;
+
+ if (attr->psvi != NULL) {
+ /*
+ * Evaluate the Attribute Value Template.
+ */
+ valueAVT = xsltEvalAVT(ctxt, attr->psvi, attr->parent);
+ if (valueAVT == NULL) {
+ /*
+ * TODO: Damn, we need an easy mechanism to report
+ * qualified names!
+ */
+ if (attr->ns) {
+ xsltTransformError(ctxt, NULL, attr->parent,
+ "Internal error: Failed to evaluate the AVT "
+ "of attribute '{%s}%s'.\n",
+ attr->ns->href, attr->name);
+ } else {
+ xsltTransformError(ctxt, NULL, attr->parent,
+ "Internal error: Failed to evaluate the AVT "
+ "of attribute '%s'.\n",
+ attr->name);
+ }
+ text->content = xmlStrdup(BAD_CAST "");
+ goto error;
+ } else {
+ text->content = valueAVT;
+ }
+ } else if ((ctxt->internalized) &&
+ (target->doc != NULL) &&
+ (target->doc->dict == ctxt->dict) &&
+ xmlDictOwns(ctxt->dict, value))
+ {
+ text->content = (xmlChar *) value;
+ } else {
+ text->content = xmlStrdup(value);
+ }
+ if ((copy != NULL) && (text != NULL) &&
+ (xmlIsID(copy->doc, copy->parent, copy)))
+ xmlAddID(NULL, copy->doc, text->content, copy);
+ }
+
+next_attribute:
+ attr = attr->next;
+ } while (attr != NULL);
+
+ /*
+ * Apply attribute-sets.
+ * The creation of such attributes will not overwrite any existing
+ * attribute.
+ */
+ attr = attrs;
+ do {
+#ifdef XSLT_REFACTORED
+ if ((attr->psvi == xsltXSLTAttrMarker) &&
+ xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets"))
+ {
+ xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL);
+ }
+#else
+ if ((attr->ns != NULL) &&
+ xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets") &&
+ xmlStrEqual(attr->ns->href, XSLT_NAMESPACE))
+ {
+ xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL);
+ }
+#endif
+ attr = attr->next;
+ } while (attr != NULL);
+
+ ctxt->insert = oldInsert;
+ return(target->properties);
+
+error:
+ ctxt->insert = oldInsert;
+ return(NULL);
+}
+
+
+/**
+ * xsltTemplateProcess:
+ * @ctxt: the XSLT transformation context
+ * @node: the attribute template node
+ *
+ * Obsolete. Don't use it.
+ *
+ * Returns NULL.
+ */
+xmlNodePtr *
+xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) {
+ if (node == NULL)
+ return(NULL);
+
+ return(0);
+}
+
+
diff --git a/libxslt/templates.h b/libxslt/templates.h
new file mode 100644
index 0000000..84a9de4
--- /dev/null
+++ b/libxslt/templates.h
@@ -0,0 +1,77 @@
+/*
+ * Summary: interface for the template processing
+ * Description: This set of routine encapsulates XPath calls
+ * and Attribute Value Templates evaluation.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_TEMPLATES_H__
+#define __XML_XSLT_TEMPLATES_H__
+
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+XSLTPUBFUN int XSLTCALL
+ xsltEvalXPathPredicate (xsltTransformContextPtr ctxt,
+ xmlXPathCompExprPtr comp,
+ xmlNsPtr *nsList,
+ int nsNr);
+XSLTPUBFUN xmlChar * XSLTCALL
+ xsltEvalTemplateString (xsltTransformContextPtr ctxt,
+ xmlNodePtr contextNode,
+ xmlNodePtr inst);
+XSLTPUBFUN xmlChar * XSLTCALL
+ xsltEvalAttrValueTemplate (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ const xmlChar *name,
+ const xmlChar *ns);
+XSLTPUBFUN const xmlChar * XSLTCALL
+ xsltEvalStaticAttrValueTemplate (xsltStylesheetPtr style,
+ xmlNodePtr node,
+ const xmlChar *name,
+ const xmlChar *ns,
+ int *found);
+
+/* TODO: this is obviously broken ... the namespaces should be passed too ! */
+XSLTPUBFUN xmlChar * XSLTCALL
+ xsltEvalXPathString (xsltTransformContextPtr ctxt,
+ xmlXPathCompExprPtr comp);
+XSLTPUBFUN xmlChar * XSLTCALL
+ xsltEvalXPathStringNs (xsltTransformContextPtr ctxt,
+ xmlXPathCompExprPtr comp,
+ int nsNr,
+ xmlNsPtr *nsList);
+
+XSLTPUBFUN xmlNodePtr * XSLTCALL
+ xsltTemplateProcess (xsltTransformContextPtr ctxt,
+ xmlNodePtr node);
+XSLTPUBFUN xmlAttrPtr XSLTCALL
+ xsltAttrListTemplateProcess (xsltTransformContextPtr ctxt,
+ xmlNodePtr target,
+ xmlAttrPtr cur);
+XSLTPUBFUN xmlAttrPtr XSLTCALL
+ xsltAttrTemplateProcess (xsltTransformContextPtr ctxt,
+ xmlNodePtr target,
+ xmlAttrPtr attr);
+XSLTPUBFUN xmlChar * XSLTCALL
+ xsltAttrTemplateValueProcess (xsltTransformContextPtr ctxt,
+ const xmlChar* attr);
+XSLTPUBFUN xmlChar * XSLTCALL
+ xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt,
+ const xmlChar* str,
+ xmlNodePtr node);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_TEMPLATES_H__ */
+
diff --git a/libxslt/transform.c b/libxslt/transform.c
new file mode 100644
index 0000000..b3fce80
--- /dev/null
+++ b/libxslt/transform.c
@@ -0,0 +1,6449 @@
+/*
+ * transform.c: Implementation of the XSL Transformation 1.0 engine
+ * transform part, i.e. applying a Stylesheet to a document
+ *
+ * References:
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * Michael Kay "XSLT Programmer's Reference" pp 637-643
+ * Writing Multiple Output Files
+ *
+ * XSLT-1.1 Working Draft
+ * http://www.w3.org/TR/xslt11#multiple-output
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/valid.h>
+#include <libxml/hash.h>
+#include <libxml/encoding.h>
+#include <libxml/xmlerror.h>
+#include <libxml/xpath.h>
+#include <libxml/parserInternals.h>
+#include <libxml/xpathInternals.h>
+#include <libxml/HTMLtree.h>
+#include <libxml/debugXML.h>
+#include <libxml/uri.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "xsltutils.h"
+#include "pattern.h"
+#include "transform.h"
+#include "variables.h"
+#include "numbersInternals.h"
+#include "namespaces.h"
+#include "attributes.h"
+#include "templates.h"
+#include "imports.h"
+#include "keys.h"
+#include "documents.h"
+#include "extensions.h"
+#include "extra.h"
+#include "preproc.h"
+#include "security.h"
+
+#ifdef WITH_XSLT_DEBUG
+#define WITH_XSLT_DEBUG_EXTRA
+#define WITH_XSLT_DEBUG_PROCESS
+#define WITH_XSLT_DEBUG_VARIABLE
+#endif
+
+#define XSLT_GENERATE_HTML_DOCTYPE
+#ifdef XSLT_GENERATE_HTML_DOCTYPE
+static int xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID,
+ const xmlChar **systemID);
+#endif
+
+int xsltMaxDepth = 3000;
+int xsltMaxVars = 15000;
+
+/*
+ * Useful macros
+ */
+
+#ifndef FALSE
+# define FALSE (0 == 1)
+# define TRUE (!FALSE)
+#endif
+
+#define IS_BLANK_NODE(n) \
+ (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
+
+
+/*
+* Forward declarations
+*/
+
+static xmlNsPtr
+xsltCopyNamespaceListInternal(xmlNodePtr node, xmlNsPtr cur);
+
+static xmlNodePtr
+xsltCopyTreeInternal(xsltTransformContextPtr ctxt,
+ xmlNodePtr invocNode,
+ xmlNodePtr node,
+ xmlNodePtr insert, int isLRE, int topElemVisited);
+
+static void
+xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
+ xmlNodePtr contextNode, xmlNodePtr list,
+ xsltTemplatePtr templ);
+
+static void
+xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt,
+ xmlNodePtr contextNode,
+ xmlNodePtr list,
+ xsltTemplatePtr templ,
+ xsltStackElemPtr withParams);
+
+/**
+ * templPush:
+ * @ctxt: the transformation context
+ * @value: the template to push on the stack
+ *
+ * Push a template on the stack
+ *
+ * Returns the new index in the stack or 0 in case of error
+ */
+static int
+templPush(xsltTransformContextPtr ctxt, xsltTemplatePtr value)
+{
+ if (ctxt->templMax == 0) {
+ ctxt->templMax = 4;
+ ctxt->templTab =
+ (xsltTemplatePtr *) xmlMalloc(ctxt->templMax *
+ sizeof(ctxt->templTab[0]));
+ if (ctxt->templTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
+ return (0);
+ }
+ }
+ else if (ctxt->templNr >= ctxt->templMax) {
+ ctxt->templMax *= 2;
+ ctxt->templTab =
+ (xsltTemplatePtr *) xmlRealloc(ctxt->templTab,
+ ctxt->templMax *
+ sizeof(ctxt->templTab[0]));
+ if (ctxt->templTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
+ return (0);
+ }
+ }
+ ctxt->templTab[ctxt->templNr] = value;
+ ctxt->templ = value;
+ return (ctxt->templNr++);
+}
+/**
+ * templPop:
+ * @ctxt: the transformation context
+ *
+ * Pop a template value from the stack
+ *
+ * Returns the stored template value
+ */
+static xsltTemplatePtr
+templPop(xsltTransformContextPtr ctxt)
+{
+ xsltTemplatePtr ret;
+
+ if (ctxt->templNr <= 0)
+ return (0);
+ ctxt->templNr--;
+ if (ctxt->templNr > 0)
+ ctxt->templ = ctxt->templTab[ctxt->templNr - 1];
+ else
+ ctxt->templ = (xsltTemplatePtr) 0;
+ ret = ctxt->templTab[ctxt->templNr];
+ ctxt->templTab[ctxt->templNr] = 0;
+ return (ret);
+}
+
+/**
+ * xsltLocalVariablePop:
+ * @ctxt: the transformation context
+ * @limitNr: number of variables which should remain
+ * @level: the depth in the xsl:template's tree
+ *
+ * Pops all variable values at the given @depth from the stack.
+ *
+ * Returns the stored variable value
+ * **NOTE:**
+ * This is an internal routine and should not be called by users!
+ */
+void
+xsltLocalVariablePop(xsltTransformContextPtr ctxt, int limitNr, int level)
+{
+ xsltStackElemPtr variable;
+
+ if (ctxt->varsNr <= 0)
+ return;
+
+ do {
+ if (ctxt->varsNr <= limitNr)
+ break;
+ variable = ctxt->varsTab[ctxt->varsNr - 1];
+ if (variable->level <= level)
+ break;
+ if (variable->level >= 0)
+ xsltFreeStackElemList(variable);
+ ctxt->varsNr--;
+ } while (ctxt->varsNr != 0);
+ if (ctxt->varsNr > 0)
+ ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1];
+ else
+ ctxt->vars = NULL;
+}
+
+/**
+ * xsltTemplateParamsCleanup:
+ *
+ * Removes xsl:param and xsl:with-param items from the
+ * variable-stack. Only xsl:with-param items are not freed.
+ */
+static void
+xsltTemplateParamsCleanup(xsltTransformContextPtr ctxt)
+{
+ xsltStackElemPtr param;
+
+ for (; ctxt->varsNr > ctxt->varsBase; ctxt->varsNr--) {
+ param = ctxt->varsTab[ctxt->varsNr -1];
+ /*
+ * Free xsl:param items.
+ * xsl:with-param items will have a level of -1 or -2.
+ */
+ if (param->level >= 0) {
+ xsltFreeStackElemList(param);
+ }
+ }
+ if (ctxt->varsNr > 0)
+ ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1];
+ else
+ ctxt->vars = NULL;
+}
+
+/**
+ * profPush:
+ * @ctxt: the transformation context
+ * @value: the profiling value to push on the stack
+ *
+ * Push a profiling value on the stack
+ *
+ * Returns the new index in the stack or 0 in case of error
+ */
+static int
+profPush(xsltTransformContextPtr ctxt, long value)
+{
+ if (ctxt->profMax == 0) {
+ ctxt->profMax = 4;
+ ctxt->profTab =
+ (long *) xmlMalloc(ctxt->profMax * sizeof(ctxt->profTab[0]));
+ if (ctxt->profTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
+ return (0);
+ }
+ }
+ else if (ctxt->profNr >= ctxt->profMax) {
+ ctxt->profMax *= 2;
+ ctxt->profTab =
+ (long *) xmlRealloc(ctxt->profTab,
+ ctxt->profMax * sizeof(ctxt->profTab[0]));
+ if (ctxt->profTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
+ return (0);
+ }
+ }
+ ctxt->profTab[ctxt->profNr] = value;
+ ctxt->prof = value;
+ return (ctxt->profNr++);
+}
+/**
+ * profPop:
+ * @ctxt: the transformation context
+ *
+ * Pop a profiling value from the stack
+ *
+ * Returns the stored profiling value
+ */
+static long
+profPop(xsltTransformContextPtr ctxt)
+{
+ long ret;
+
+ if (ctxt->profNr <= 0)
+ return (0);
+ ctxt->profNr--;
+ if (ctxt->profNr > 0)
+ ctxt->prof = ctxt->profTab[ctxt->profNr - 1];
+ else
+ ctxt->prof = (long) 0;
+ ret = ctxt->profTab[ctxt->profNr];
+ ctxt->profTab[ctxt->profNr] = 0;
+ return (ret);
+}
+
+static void
+profCallgraphAdd(xsltTemplatePtr templ, xsltTemplatePtr parent)
+{
+ int i;
+
+ if (templ->templMax == 0) {
+ templ->templMax = 4;
+ templ->templCalledTab =
+ (xsltTemplatePtr *) xmlMalloc(templ->templMax *
+ sizeof(templ->templCalledTab[0]));
+ templ->templCountTab =
+ (int *) xmlMalloc(templ->templMax *
+ sizeof(templ->templCountTab[0]));
+ if (templ->templCalledTab == NULL || templ->templCountTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
+ return;
+ }
+ }
+ else if (templ->templNr >= templ->templMax) {
+ templ->templMax *= 2;
+ templ->templCalledTab =
+ (xsltTemplatePtr *) xmlRealloc(templ->templCalledTab,
+ templ->templMax *
+ sizeof(templ->templCalledTab[0]));
+ templ->templCountTab =
+ (int *) xmlRealloc(templ->templCountTab,
+ templ->templMax *
+ sizeof(templ->templCountTab[0]));
+ if (templ->templCalledTab == NULL || templ->templCountTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
+ return;
+ }
+ }
+
+ for (i = 0; i < templ->templNr; i++) {
+ if (templ->templCalledTab[i] == parent) {
+ templ->templCountTab[i]++;
+ break;
+ }
+ }
+ if (i == templ->templNr) {
+ /* not found, add new one */
+ templ->templCalledTab[templ->templNr] = parent;
+ templ->templCountTab[templ->templNr] = 1;
+ templ->templNr++;
+ }
+}
+
+/**
+ * xsltPreCompEval:
+ * @ctxt: transform context
+ * @node: context node
+ * @comp: precompiled expression
+ *
+ * Evaluate a precompiled XPath expression.
+ */
+static xmlXPathObjectPtr
+xsltPreCompEval(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xsltStylePreCompPtr comp) {
+ xmlXPathObjectPtr res;
+ xmlXPathContextPtr xpctxt;
+ xmlNodePtr oldXPContextNode;
+ xmlNsPtr *oldXPNamespaces;
+ int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
+
+ xpctxt = ctxt->xpathCtxt;
+ oldXPContextNode = xpctxt->node;
+ oldXPProximityPosition = xpctxt->proximityPosition;
+ oldXPContextSize = xpctxt->contextSize;
+ oldXPNsNr = xpctxt->nsNr;
+ oldXPNamespaces = xpctxt->namespaces;
+
+ xpctxt->node = node;
+#ifdef XSLT_REFACTORED
+ if (comp->inScopeNs != NULL) {
+ xpctxt->namespaces = comp->inScopeNs->list;
+ xpctxt->nsNr = comp->inScopeNs->xpathNumber;
+ } else {
+ xpctxt->namespaces = NULL;
+ xpctxt->nsNr = 0;
+ }
+#else
+ xpctxt->namespaces = comp->nsList;
+ xpctxt->nsNr = comp->nsNr;
+#endif
+
+ res = xmlXPathCompiledEval(comp->comp, xpctxt);
+
+ xpctxt->node = oldXPContextNode;
+ xpctxt->proximityPosition = oldXPProximityPosition;
+ xpctxt->contextSize = oldXPContextSize;
+ xpctxt->nsNr = oldXPNsNr;
+ xpctxt->namespaces = oldXPNamespaces;
+
+ return(res);
+}
+
+/**
+ * xsltPreCompEvalToBoolean:
+ * @ctxt: transform context
+ * @node: context node
+ * @comp: precompiled expression
+ *
+ * Evaluate a precompiled XPath expression as boolean.
+ */
+static int
+xsltPreCompEvalToBoolean(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xsltStylePreCompPtr comp) {
+ int res;
+ xmlXPathContextPtr xpctxt;
+ xmlNodePtr oldXPContextNode;
+ xmlNsPtr *oldXPNamespaces;
+ int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
+
+ xpctxt = ctxt->xpathCtxt;
+ oldXPContextNode = xpctxt->node;
+ oldXPProximityPosition = xpctxt->proximityPosition;
+ oldXPContextSize = xpctxt->contextSize;
+ oldXPNsNr = xpctxt->nsNr;
+ oldXPNamespaces = xpctxt->namespaces;
+
+ xpctxt->node = node;
+#ifdef XSLT_REFACTORED
+ if (comp->inScopeNs != NULL) {
+ xpctxt->namespaces = comp->inScopeNs->list;
+ xpctxt->nsNr = comp->inScopeNs->xpathNumber;
+ } else {
+ xpctxt->namespaces = NULL;
+ xpctxt->nsNr = 0;
+ }
+#else
+ xpctxt->namespaces = comp->nsList;
+ xpctxt->nsNr = comp->nsNr;
+#endif
+
+ res = xmlXPathCompiledEvalToBoolean(comp->comp, xpctxt);
+
+ xpctxt->node = oldXPContextNode;
+ xpctxt->proximityPosition = oldXPProximityPosition;
+ xpctxt->contextSize = oldXPContextSize;
+ xpctxt->nsNr = oldXPNsNr;
+ xpctxt->namespaces = oldXPNamespaces;
+
+ return(res);
+}
+
+/************************************************************************
+ * *
+ * XInclude default settings *
+ * *
+ ************************************************************************/
+
+static int xsltDoXIncludeDefault = 0;
+
+/**
+ * xsltSetXIncludeDefault:
+ * @xinclude: whether to do XInclude processing
+ *
+ * Set whether XInclude should be processed on document being loaded by default
+ */
+void
+xsltSetXIncludeDefault(int xinclude) {
+ xsltDoXIncludeDefault = (xinclude != 0);
+}
+
+/**
+ * xsltGetXIncludeDefault:
+ *
+ * Provides the default state for XInclude processing
+ *
+ * Returns 0 if there is no processing 1 otherwise
+ */
+int
+xsltGetXIncludeDefault(void) {
+ return(xsltDoXIncludeDefault);
+}
+
+unsigned long xsltDefaultTrace = (unsigned long) XSLT_TRACE_ALL;
+
+/**
+ * xsltDebugSetDefaultTrace:
+ * @val: tracing level mask
+ *
+ * Set the default debug tracing level mask
+ */
+void xsltDebugSetDefaultTrace(xsltDebugTraceCodes val) {
+ xsltDefaultTrace = val;
+}
+
+/**
+ * xsltDebugGetDefaultTrace:
+ *
+ * Get the current default debug tracing level mask
+ *
+ * Returns the current default debug tracing level mask
+ */
+xsltDebugTraceCodes xsltDebugGetDefaultTrace() {
+ return xsltDefaultTrace;
+}
+
+/************************************************************************
+ * *
+ * Handling of Transformation Contexts *
+ * *
+ ************************************************************************/
+
+static xsltTransformCachePtr
+xsltTransformCacheCreate(void)
+{
+ xsltTransformCachePtr ret;
+
+ ret = (xsltTransformCachePtr) xmlMalloc(sizeof(xsltTransformCache));
+ if (ret == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltTransformCacheCreate : malloc failed\n");
+ return(NULL);
+ }
+ memset(ret, 0, sizeof(xsltTransformCache));
+ return(ret);
+}
+
+static void
+xsltTransformCacheFree(xsltTransformCachePtr cache)
+{
+ if (cache == NULL)
+ return;
+ /*
+ * Free tree fragments.
+ */
+ if (cache->RVT) {
+ xmlDocPtr tmp, cur = cache->RVT;
+ while (cur) {
+ tmp = cur;
+ cur = (xmlDocPtr) cur->next;
+ if (tmp->_private != NULL) {
+ /*
+ * Tree the document info.
+ */
+ xsltFreeDocumentKeys((xsltDocumentPtr) tmp->_private);
+ xmlFree(tmp->_private);
+ }
+ xmlFreeDoc(tmp);
+ }
+ }
+ /*
+ * Free vars/params.
+ */
+ if (cache->stackItems) {
+ xsltStackElemPtr tmp, cur = cache->stackItems;
+ while (cur) {
+ tmp = cur;
+ cur = cur->next;
+ /*
+ * REVISIT TODO: Should be call a destruction-function
+ * instead?
+ */
+ xmlFree(tmp);
+ }
+ }
+ xmlFree(cache);
+}
+
+/**
+ * xsltNewTransformContext:
+ * @style: a parsed XSLT stylesheet
+ * @doc: the input document
+ *
+ * Create a new XSLT TransformContext
+ *
+ * Returns the newly allocated xsltTransformContextPtr or NULL in case of error
+ */
+xsltTransformContextPtr
+xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) {
+ xsltTransformContextPtr cur;
+ xsltDocumentPtr docu;
+ int i;
+
+ xsltInitGlobals();
+
+ cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext));
+ if (cur == NULL) {
+ xsltTransformError(NULL, NULL, (xmlNodePtr)doc,
+ "xsltNewTransformContext : malloc failed\n");
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltTransformContext));
+
+ cur->cache = xsltTransformCacheCreate();
+ if (cur->cache == NULL)
+ goto internal_err;
+ /*
+ * setup of the dictionary must be done early as some of the
+ * processing later like key handling may need it.
+ */
+ cur->dict = xmlDictCreateSub(style->dict);
+ cur->internalized = ((style->internalized) && (cur->dict != NULL));
+#ifdef WITH_XSLT_DEBUG
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Creating sub-dictionary from stylesheet for transformation\n");
+#endif
+
+ /*
+ * initialize the template stack
+ */
+ cur->templTab = (xsltTemplatePtr *)
+ xmlMalloc(10 * sizeof(xsltTemplatePtr));
+ if (cur->templTab == NULL) {
+ xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
+ "xsltNewTransformContext: out of memory\n");
+ goto internal_err;
+ }
+ cur->templNr = 0;
+ cur->templMax = 5;
+ cur->templ = NULL;
+ cur->maxTemplateDepth = xsltMaxDepth;
+
+ /*
+ * initialize the variables stack
+ */
+ cur->varsTab = (xsltStackElemPtr *)
+ xmlMalloc(10 * sizeof(xsltStackElemPtr));
+ if (cur->varsTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xsltNewTransformContext: out of memory\n");
+ goto internal_err;
+ }
+ cur->varsNr = 0;
+ cur->varsMax = 10;
+ cur->vars = NULL;
+ cur->varsBase = 0;
+ cur->maxTemplateVars = xsltMaxVars;
+
+ /*
+ * the profiling stack is not initialized by default
+ */
+ cur->profTab = NULL;
+ cur->profNr = 0;
+ cur->profMax = 0;
+ cur->prof = 0;
+
+ cur->style = style;
+ xmlXPathInit();
+ cur->xpathCtxt = xmlXPathNewContext(doc);
+ if (cur->xpathCtxt == NULL) {
+ xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
+ "xsltNewTransformContext : xmlXPathNewContext failed\n");
+ goto internal_err;
+ }
+ /*
+ * Create an XPath cache.
+ */
+ if (xmlXPathContextSetCache(cur->xpathCtxt, 1, -1, 0) == -1)
+ goto internal_err;
+ /*
+ * Initialize the extras array
+ */
+ if (style->extrasNr != 0) {
+ cur->extrasMax = style->extrasNr + 20;
+ cur->extras = (xsltRuntimeExtraPtr)
+ xmlMalloc(cur->extrasMax * sizeof(xsltRuntimeExtra));
+ if (cur->extras == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xsltNewTransformContext: out of memory\n");
+ goto internal_err;
+ }
+ cur->extrasNr = style->extrasNr;
+ for (i = 0;i < cur->extrasMax;i++) {
+ cur->extras[i].info = NULL;
+ cur->extras[i].deallocate = NULL;
+ cur->extras[i].val.ptr = NULL;
+ }
+ } else {
+ cur->extras = NULL;
+ cur->extrasNr = 0;
+ cur->extrasMax = 0;
+ }
+
+ XSLT_REGISTER_VARIABLE_LOOKUP(cur);
+ XSLT_REGISTER_FUNCTION_LOOKUP(cur);
+ cur->xpathCtxt->nsHash = style->nsHash;
+ /*
+ * Initialize the registered external modules
+ */
+ xsltInitCtxtExts(cur);
+ /*
+ * Setup document element ordering for later efficiencies
+ * (bug 133289)
+ */
+ if (xslDebugStatus == XSLT_DEBUG_NONE)
+ xmlXPathOrderDocElems(doc);
+ /*
+ * Must set parserOptions before calling xsltNewDocument
+ * (bug 164530)
+ */
+ cur->parserOptions = XSLT_PARSE_OPTIONS;
+ docu = xsltNewDocument(cur, doc);
+ if (docu == NULL) {
+ xsltTransformError(cur, NULL, (xmlNodePtr)doc,
+ "xsltNewTransformContext : xsltNewDocument failed\n");
+ goto internal_err;
+ }
+ docu->main = 1;
+ cur->document = docu;
+ cur->inst = NULL;
+ cur->outputFile = NULL;
+ cur->sec = xsltGetDefaultSecurityPrefs();
+ cur->debugStatus = xslDebugStatus;
+ cur->traceCode = (unsigned long*) &xsltDefaultTrace;
+ cur->xinclude = xsltGetXIncludeDefault();
+ cur->keyInitLevel = 0;
+
+ return(cur);
+
+internal_err:
+ if (cur != NULL)
+ xsltFreeTransformContext(cur);
+ return(NULL);
+}
+
+/**
+ * xsltFreeTransformContext:
+ * @ctxt: an XSLT parser context
+ *
+ * Free up the memory allocated by @ctxt
+ */
+void
+xsltFreeTransformContext(xsltTransformContextPtr ctxt) {
+ if (ctxt == NULL)
+ return;
+
+ /*
+ * Shutdown the extension modules associated to the stylesheet
+ * used if needed.
+ */
+ xsltShutdownCtxtExts(ctxt);
+
+ if (ctxt->xpathCtxt != NULL) {
+ ctxt->xpathCtxt->nsHash = NULL;
+ xmlXPathFreeContext(ctxt->xpathCtxt);
+ }
+ if (ctxt->templTab != NULL)
+ xmlFree(ctxt->templTab);
+ if (ctxt->varsTab != NULL)
+ xmlFree(ctxt->varsTab);
+ if (ctxt->profTab != NULL)
+ xmlFree(ctxt->profTab);
+ if ((ctxt->extrasNr > 0) && (ctxt->extras != NULL)) {
+ int i;
+
+ for (i = 0;i < ctxt->extrasNr;i++) {
+ if ((ctxt->extras[i].deallocate != NULL) &&
+ (ctxt->extras[i].info != NULL))
+ ctxt->extras[i].deallocate(ctxt->extras[i].info);
+ }
+ xmlFree(ctxt->extras);
+ }
+ xsltFreeGlobalVariables(ctxt);
+ xsltFreeDocuments(ctxt);
+ xsltFreeCtxtExts(ctxt);
+ xsltFreeRVTs(ctxt);
+ xsltTransformCacheFree(ctxt->cache);
+ xmlDictFree(ctxt->dict);
+#ifdef WITH_XSLT_DEBUG
+ xsltGenericDebug(xsltGenericDebugContext,
+ "freeing transformation dictionary\n");
+#endif
+ memset(ctxt, -1, sizeof(xsltTransformContext));
+ xmlFree(ctxt);
+}
+
+/************************************************************************
+ * *
+ * Copy of Nodes in an XSLT fashion *
+ * *
+ ************************************************************************/
+
+xmlNodePtr xsltCopyTree(xsltTransformContextPtr ctxt,
+ xmlNodePtr node, xmlNodePtr insert, int literal);
+
+/**
+ * xsltAddChild:
+ * @parent: the parent node
+ * @cur: the child node
+ *
+ * Wrapper version of xmlAddChild with a more consistent behaviour on
+ * error. One expect the use to be child = xsltAddChild(parent, child);
+ * and the routine will take care of not leaking on errors or node merge
+ *
+ * Returns the child is successfully attached or NULL if merged or freed
+ */
+static xmlNodePtr
+xsltAddChild(xmlNodePtr parent, xmlNodePtr cur) {
+ xmlNodePtr ret;
+
+ if (cur == NULL)
+ return(NULL);
+ if (parent == NULL) {
+ xmlFreeNode(cur);
+ return(NULL);
+ }
+ ret = xmlAddChild(parent, cur);
+
+ return(ret);
+}
+
+/**
+ * xsltAddTextString:
+ * @ctxt: a XSLT process context
+ * @target: the text node where the text will be attached
+ * @string: the text string
+ * @len: the string length in byte
+ *
+ * Extend the current text node with the new string, it handles coalescing
+ *
+ * Returns: the text node
+ */
+static xmlNodePtr
+xsltAddTextString(xsltTransformContextPtr ctxt, xmlNodePtr target,
+ const xmlChar *string, int len) {
+ /*
+ * optimization
+ */
+ if ((len <= 0) || (string == NULL) || (target == NULL))
+ return(target);
+
+ if (ctxt->lasttext == target->content) {
+
+ if (ctxt->lasttuse + len >= ctxt->lasttsize) {
+ xmlChar *newbuf;
+ int size;
+
+ size = ctxt->lasttsize + len + 100;
+ size *= 2;
+ newbuf = (xmlChar *) xmlRealloc(target->content,size);
+ if (newbuf == NULL) {
+ xsltTransformError(ctxt, NULL, target,
+ "xsltCopyText: text allocation failed\n");
+ return(NULL);
+ }
+ ctxt->lasttsize = size;
+ ctxt->lasttext = newbuf;
+ target->content = newbuf;
+ }
+ memcpy(&(target->content[ctxt->lasttuse]), string, len);
+ ctxt->lasttuse += len;
+ target->content[ctxt->lasttuse] = 0;
+ } else {
+ xmlNodeAddContent(target, string);
+ ctxt->lasttext = target->content;
+ len = xmlStrlen(target->content);
+ ctxt->lasttsize = len;
+ ctxt->lasttuse = len;
+ }
+ return(target);
+}
+
+/**
+ * xsltCopyTextString:
+ * @ctxt: a XSLT process context
+ * @target: the element where the text will be attached
+ * @string: the text string
+ * @noescape: should disable-escaping be activated for this text node.
+ *
+ * Adds @string to a newly created or an existent text node child of
+ * @target.
+ *
+ * Returns: the text node, where the text content of @cur is copied to.
+ * NULL in case of API or internal errors.
+ */
+xmlNodePtr
+xsltCopyTextString(xsltTransformContextPtr ctxt, xmlNodePtr target,
+ const xmlChar *string, int noescape)
+{
+ xmlNodePtr copy;
+ int len;
+
+ if (string == NULL)
+ return(NULL);
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCopyTextString: copy text %s\n",
+ string));
+#endif
+
+ /*
+ * Play safe and reset the merging mechanism for every new
+ * target node.
+ */
+ if ((target == NULL) || (target->children == NULL)) {
+ ctxt->lasttext = NULL;
+ }
+
+ /* handle coalescing of text nodes here */
+ len = xmlStrlen(string);
+ if ((ctxt->type == XSLT_OUTPUT_XML) &&
+ (ctxt->style->cdataSection != NULL) &&
+ (target != NULL) &&
+ (target->type == XML_ELEMENT_NODE) &&
+ (((target->ns == NULL) &&
+ (xmlHashLookup2(ctxt->style->cdataSection,
+ target->name, NULL) != NULL)) ||
+ ((target->ns != NULL) &&
+ (xmlHashLookup2(ctxt->style->cdataSection,
+ target->name, target->ns->href) != NULL))))
+ {
+ /*
+ * Process "cdata-section-elements".
+ */
+ if ((target->last != NULL) &&
+ (target->last->type == XML_CDATA_SECTION_NODE))
+ {
+ return(xsltAddTextString(ctxt, target->last, string, len));
+ }
+ copy = xmlNewCDataBlock(ctxt->output, string, len);
+ } else if (noescape) {
+ /*
+ * Process "disable-output-escaping".
+ */
+ if ((target != NULL) && (target->last != NULL) &&
+ (target->last->type == XML_TEXT_NODE) &&
+ (target->last->name == xmlStringTextNoenc))
+ {
+ return(xsltAddTextString(ctxt, target->last, string, len));
+ }
+ copy = xmlNewTextLen(string, len);
+ if (copy != NULL)
+ copy->name = xmlStringTextNoenc;
+ } else {
+ /*
+ * Default processing.
+ */
+ if ((target != NULL) && (target->last != NULL) &&
+ (target->last->type == XML_TEXT_NODE) &&
+ (target->last->name == xmlStringText)) {
+ return(xsltAddTextString(ctxt, target->last, string, len));
+ }
+ copy = xmlNewTextLen(string, len);
+ }
+ if (copy != NULL && target != NULL)
+ copy = xsltAddChild(target, copy);
+ if (copy != NULL) {
+ ctxt->lasttext = copy->content;
+ ctxt->lasttsize = len;
+ ctxt->lasttuse = len;
+ } else {
+ xsltTransformError(ctxt, NULL, target,
+ "xsltCopyTextString: text copy failed\n");
+ ctxt->lasttext = NULL;
+ }
+ return(copy);
+}
+
+/**
+ * xsltCopyText:
+ * @ctxt: a XSLT process context
+ * @target: the element where the text will be attached
+ * @cur: the text or CDATA node
+ * @interned: the string is in the target doc dictionary
+ *
+ * Copy the text content of @cur and append it to @target's children.
+ *
+ * Returns: the text node, where the text content of @cur is copied to.
+ * NULL in case of API or internal errors.
+ */
+static xmlNodePtr
+xsltCopyText(xsltTransformContextPtr ctxt, xmlNodePtr target,
+ xmlNodePtr cur, int interned)
+{
+ xmlNodePtr copy;
+
+ if ((cur->type != XML_TEXT_NODE) &&
+ (cur->type != XML_CDATA_SECTION_NODE))
+ return(NULL);
+ if (cur->content == NULL)
+ return(NULL);
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if (cur->type == XML_CDATA_SECTION_NODE) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCopyText: copy CDATA text %s\n",
+ cur->content));
+ } else if (cur->name == xmlStringTextNoenc) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCopyText: copy unescaped text %s\n",
+ cur->content));
+ } else {
+ XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCopyText: copy text %s\n",
+ cur->content));
+ }
+#endif
+
+ /*
+ * Play save and reset the merging mechanism for every new
+ * target node.
+ */
+ if ((target == NULL) || (target->children == NULL)) {
+ ctxt->lasttext = NULL;
+ }
+
+ if ((ctxt->style->cdataSection != NULL) &&
+ (ctxt->type == XSLT_OUTPUT_XML) &&
+ (target != NULL) &&
+ (target->type == XML_ELEMENT_NODE) &&
+ (((target->ns == NULL) &&
+ (xmlHashLookup2(ctxt->style->cdataSection,
+ target->name, NULL) != NULL)) ||
+ ((target->ns != NULL) &&
+ (xmlHashLookup2(ctxt->style->cdataSection,
+ target->name, target->ns->href) != NULL))))
+ {
+ /*
+ * Process "cdata-section-elements".
+ */
+ /*
+ * OPTIMIZE TODO: xsltCopyText() is also used for attribute content.
+ */
+ /*
+ * TODO: Since this doesn't merge adjacent CDATA-section nodes,
+ * we'll get: <![CDATA[x]]><!CDATA[y]]>.
+ * TODO: Reported in #321505.
+ */
+ if ((target->last != NULL) &&
+ (target->last->type == XML_CDATA_SECTION_NODE))
+ {
+ /*
+ * Append to existing CDATA-section node.
+ */
+ copy = xsltAddTextString(ctxt, target->last, cur->content,
+ xmlStrlen(cur->content));
+ goto exit;
+ } else {
+ unsigned int len;
+
+ len = xmlStrlen(cur->content);
+ copy = xmlNewCDataBlock(ctxt->output, cur->content, len);
+ if (copy == NULL)
+ goto exit;
+ ctxt->lasttext = copy->content;
+ ctxt->lasttsize = len;
+ ctxt->lasttuse = len;
+ }
+ } else if ((target != NULL) &&
+ (target->last != NULL) &&
+ /* both escaped or both non-escaped text-nodes */
+ (((target->last->type == XML_TEXT_NODE) &&
+ (target->last->name == cur->name)) ||
+ /* non-escaped text nodes and CDATA-section nodes */
+ (((target->last->type == XML_CDATA_SECTION_NODE) &&
+ (cur->name == xmlStringTextNoenc)))))
+ {
+ /*
+ * we are appending to an existing text node
+ */
+ copy = xsltAddTextString(ctxt, target->last, cur->content,
+ xmlStrlen(cur->content));
+ goto exit;
+ } else if ((interned) && (target != NULL) &&
+ (target->doc != NULL) &&
+ (target->doc->dict == ctxt->dict))
+ {
+ /*
+ * TODO: DO we want to use this also for "text" output?
+ */
+ copy = xmlNewTextLen(NULL, 0);
+ if (copy == NULL)
+ goto exit;
+ if (cur->name == xmlStringTextNoenc)
+ copy->name = xmlStringTextNoenc;
+
+ /*
+ * Must confirm that content is in dict (bug 302821)
+ * TODO: This check should be not needed for text coming
+ * from the stylesheets
+ */
+ if (xmlDictOwns(ctxt->dict, cur->content))
+ copy->content = cur->content;
+ else {
+ if ((copy->content = xmlStrdup(cur->content)) == NULL)
+ return NULL;
+ }
+ } else {
+ /*
+ * normal processing. keep counters to extend the text node
+ * in xsltAddTextString if needed.
+ */
+ unsigned int len;
+
+ len = xmlStrlen(cur->content);
+ copy = xmlNewTextLen(cur->content, len);
+ if (copy == NULL)
+ goto exit;
+ if (cur->name == xmlStringTextNoenc)
+ copy->name = xmlStringTextNoenc;
+ ctxt->lasttext = copy->content;
+ ctxt->lasttsize = len;
+ ctxt->lasttuse = len;
+ }
+ if (copy != NULL) {
+ if (target != NULL) {
+ copy->doc = target->doc;
+ /*
+ * MAYBE TODO: Maybe we should reset the ctxt->lasttext here
+ * to ensure that the optimized text-merging mechanism
+ * won't interfere with normal node-merging in any case.
+ */
+ copy = xsltAddChild(target, copy);
+ }
+ } else {
+ xsltTransformError(ctxt, NULL, target,
+ "xsltCopyText: text copy failed\n");
+ }
+
+exit:
+ if ((copy == NULL) || (copy->content == NULL)) {
+ xsltTransformError(ctxt, NULL, target,
+ "Internal error in xsltCopyText(): "
+ "Failed to copy the string.\n");
+ ctxt->state = XSLT_STATE_STOPPED;
+ }
+ return(copy);
+}
+
+/**
+ * xsltShallowCopyAttr:
+ * @ctxt: a XSLT process context
+ * @invocNode: responsible node in the stylesheet; used for error reports
+ * @target: the element where the attribute will be grafted
+ * @attr: the attribute to be copied
+ *
+ * Do a copy of an attribute.
+ * Called by:
+ * - xsltCopyTreeInternal()
+ * - xsltCopyOf()
+ * - xsltCopy()
+ *
+ * Returns: a new xmlAttrPtr, or NULL in case of error.
+ */
+static xmlAttrPtr
+xsltShallowCopyAttr(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
+ xmlNodePtr target, xmlAttrPtr attr)
+{
+ xmlAttrPtr copy;
+ xmlChar *value;
+
+ if (attr == NULL)
+ return(NULL);
+
+ if (target->type != XML_ELEMENT_NODE) {
+ xsltTransformError(ctxt, NULL, invocNode,
+ "Cannot add an attribute node to a non-element node.\n");
+ return(NULL);
+ }
+
+ if (target->children != NULL) {
+ xsltTransformError(ctxt, NULL, invocNode,
+ "Attribute nodes must be added before "
+ "any child nodes to an element.\n");
+ return(NULL);
+ }
+
+ value = xmlNodeListGetString(attr->doc, attr->children, 1);
+ if (attr->ns != NULL) {
+ xmlNsPtr ns;
+
+ ns = xsltGetSpecialNamespace(ctxt, invocNode,
+ attr->ns->href, attr->ns->prefix, target);
+ if (ns == NULL) {
+ xsltTransformError(ctxt, NULL, invocNode,
+ "Namespace fixup error: Failed to acquire an in-scope "
+ "namespace binding of the copied attribute '{%s}%s'.\n",
+ attr->ns->href, attr->name);
+ /*
+ * TODO: Should we just stop here?
+ */
+ }
+ /*
+ * Note that xmlSetNsProp() will take care of duplicates
+ * and assigns the new namespace even to a duplicate.
+ */
+ copy = xmlSetNsProp(target, ns, attr->name, value);
+ } else {
+ copy = xmlSetNsProp(target, NULL, attr->name, value);
+ }
+ if (value != NULL)
+ xmlFree(value);
+
+ if (copy == NULL)
+ return(NULL);
+
+#if 0
+ /*
+ * NOTE: This was optimized according to bug #342695.
+ * TODO: Can this further be optimized, if source and target
+ * share the same dict and attr->children is just 1 text node
+ * which is in the dict? How probable is such a case?
+ */
+ /*
+ * TODO: Do we need to create an empty text node if the value
+ * is the empty string?
+ */
+ value = xmlNodeListGetString(attr->doc, attr->children, 1);
+ if (value != NULL) {
+ txtNode = xmlNewDocText(target->doc, NULL);
+ if (txtNode == NULL)
+ return(NULL);
+ if ((target->doc != NULL) &&
+ (target->doc->dict != NULL))
+ {
+ txtNode->content =
+ (xmlChar *) xmlDictLookup(target->doc->dict,
+ BAD_CAST value, -1);
+ xmlFree(value);
+ } else
+ txtNode->content = value;
+ copy->children = txtNode;
+ }
+#endif
+
+ return(copy);
+}
+
+/**
+ * xsltCopyAttrListNoOverwrite:
+ * @ctxt: a XSLT process context
+ * @invocNode: responsible node in the stylesheet; used for error reports
+ * @target: the element where the new attributes will be grafted
+ * @attr: the first attribute in the list to be copied
+ *
+ * Copies a list of attribute nodes, starting with @attr, over to the
+ * @target element node.
+ *
+ * Called by:
+ * - xsltCopyTreeInternal()
+ *
+ * Returns 0 on success and -1 on errors and internal errors.
+ */
+static int
+xsltCopyAttrListNoOverwrite(xsltTransformContextPtr ctxt,
+ xmlNodePtr invocNode,
+ xmlNodePtr target, xmlAttrPtr attr)
+{
+ xmlAttrPtr copy;
+ xmlNsPtr origNs = NULL, copyNs = NULL;
+ xmlChar *value;
+
+ /*
+ * Don't use xmlCopyProp() here, since it will try to
+ * reconciliate namespaces.
+ */
+ while (attr != NULL) {
+ /*
+ * Find a namespace node in the tree of @target.
+ * Avoid searching for the same ns.
+ */
+ if (attr->ns != origNs) {
+ origNs = attr->ns;
+ if (attr->ns != NULL) {
+ copyNs = xsltGetSpecialNamespace(ctxt, invocNode,
+ attr->ns->href, attr->ns->prefix, target);
+ if (copyNs == NULL)
+ return(-1);
+ } else
+ copyNs = NULL;
+ }
+ /*
+ * If attribute has a value, we need to copy it (watching out
+ * for possible entities)
+ */
+ if ((attr->children) && (attr->children->type == XML_TEXT_NODE) &&
+ (attr->children->next == NULL)) {
+ copy = xmlNewNsProp(target, copyNs, attr->name,
+ attr->children->content);
+ } else if (attr->children != NULL) {
+ value = xmlNodeListGetString(attr->doc, attr->children, 1);
+ copy = xmlNewNsProp(target, copyNs, attr->name, BAD_CAST value);
+ xmlFree(value);
+ } else {
+ copy = xmlNewNsProp(target, copyNs, attr->name, NULL);
+ }
+
+ if (copy == NULL)
+ return(-1);
+
+ attr = attr->next;
+ }
+ return(0);
+}
+
+/**
+ * xsltShallowCopyElem:
+ * @ctxt: the XSLT process context
+ * @node: the element node in the source tree
+ * or the Literal Result Element
+ * @insert: the parent in the result tree
+ * @isLRE: if @node is a Literal Result Element
+ *
+ * Make a copy of the element node @node
+ * and insert it as last child of @insert.
+ *
+ * URGENT TODO: The problem with this one (for the non-refactored code)
+ * is that it is used for both, Literal Result Elements *and*
+ * copying input nodes.
+ *
+ * BIG NOTE: This is only called for XML_ELEMENT_NODEs.
+ *
+ * Called from:
+ * xsltApplySequenceConstructor()
+ * (for Literal Result Elements - which is a problem)
+ * xsltCopy() (for shallow-copying elements via xsl:copy)
+ *
+ * Returns a pointer to the new node, or NULL in case of error
+ */
+static xmlNodePtr
+xsltShallowCopyElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr insert, int isLRE)
+{
+ xmlNodePtr copy;
+
+ if ((node->type == XML_DTD_NODE) || (insert == NULL))
+ return(NULL);
+ if ((node->type == XML_TEXT_NODE) ||
+ (node->type == XML_CDATA_SECTION_NODE))
+ return(xsltCopyText(ctxt, insert, node, 0));
+
+ copy = xmlDocCopyNode(node, insert->doc, 0);
+ if (copy != NULL) {
+ copy->doc = ctxt->output;
+ copy = xsltAddChild(insert, copy);
+ if (copy == NULL) {
+ xsltTransformError(ctxt, NULL, node,
+ "xsltShallowCopyElem: copy failed\n");
+ return (copy);
+ }
+
+ if (node->type == XML_ELEMENT_NODE) {
+ /*
+ * Add namespaces as they are needed
+ */
+ if (node->nsDef != NULL) {
+ /*
+ * TODO: Remove the LRE case in the refactored code
+ * gets enabled.
+ */
+ if (isLRE)
+ xsltCopyNamespaceList(ctxt, copy, node->nsDef);
+ else
+ xsltCopyNamespaceListInternal(copy, node->nsDef);
+ }
+
+ /*
+ * URGENT TODO: The problem with this is that it does not
+ * copy over all namespace nodes in scope.
+ * The damn thing about this is, that we would need to
+ * use the xmlGetNsList(), for every single node; this is
+ * also done in xsltCopyTreeInternal(), but only for the top node.
+ */
+ if (node->ns != NULL) {
+ if (isLRE) {
+ /*
+ * REVISIT TODO: Since the non-refactored code still does
+ * ns-aliasing, we need to call xsltGetNamespace() here.
+ * Remove this when ready.
+ */
+ copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy);
+ } else {
+ copy->ns = xsltGetSpecialNamespace(ctxt,
+ node, node->ns->href, node->ns->prefix, copy);
+
+ }
+ } else if ((insert->type == XML_ELEMENT_NODE) &&
+ (insert->ns != NULL))
+ {
+ /*
+ * "Undeclare" the default namespace.
+ */
+ xsltGetSpecialNamespace(ctxt, node, NULL, NULL, copy);
+ }
+ }
+ } else {
+ xsltTransformError(ctxt, NULL, node,
+ "xsltShallowCopyElem: copy %s failed\n", node->name);
+ }
+ return(copy);
+}
+
+/**
+ * xsltCopyTreeList:
+ * @ctxt: a XSLT process context
+ * @invocNode: responsible node in the stylesheet; used for error reports
+ * @list: the list of element nodes in the source tree.
+ * @insert: the parent in the result tree.
+ * @isLRE: is this a literal result element list
+ * @topElemVisited: indicates if a top-most element was already processed
+ *
+ * Make a copy of the full list of tree @list
+ * and insert it as last children of @insert
+ *
+ * NOTE: Not to be used for Literal Result Elements.
+ *
+ * Used by:
+ * - xsltCopyOf()
+ *
+ * Returns a pointer to the new list, or NULL in case of error
+ */
+static xmlNodePtr
+xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
+ xmlNodePtr list,
+ xmlNodePtr insert, int isLRE, int topElemVisited)
+{
+ xmlNodePtr copy, ret = NULL;
+
+ while (list != NULL) {
+ copy = xsltCopyTreeInternal(ctxt, invocNode,
+ list, insert, isLRE, topElemVisited);
+ if (copy != NULL) {
+ if (ret == NULL) {
+ ret = copy;
+ }
+ }
+ list = list->next;
+ }
+ return(ret);
+}
+
+/**
+ * xsltCopyNamespaceListInternal:
+ * @node: the target node
+ * @cur: the first namespace
+ *
+ * Do a copy of a namespace list. If @node is non-NULL the
+ * new namespaces are added automatically.
+ * Called by:
+ * xsltCopyTreeInternal()
+ *
+ * QUESTION: What is the exact difference between this function
+ * and xsltCopyNamespaceList() in "namespaces.c"?
+ * ANSWER: xsltCopyNamespaceList() tries to apply ns-aliases.
+ *
+ * Returns: a new xmlNsPtr, or NULL in case of error.
+ */
+static xmlNsPtr
+xsltCopyNamespaceListInternal(xmlNodePtr elem, xmlNsPtr ns) {
+ xmlNsPtr ret = NULL;
+ xmlNsPtr p = NULL, q, luNs;
+
+ if (ns == NULL)
+ return(NULL);
+ /*
+ * One can add namespaces only on element nodes
+ */
+ if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE))
+ elem = NULL;
+
+ do {
+ if (ns->type != XML_NAMESPACE_DECL)
+ break;
+ /*
+ * Avoid duplicating namespace declarations on the tree.
+ */
+ if (elem != NULL) {
+ if ((elem->ns != NULL) &&
+ xmlStrEqual(elem->ns->prefix, ns->prefix) &&
+ xmlStrEqual(elem->ns->href, ns->href))
+ {
+ ns = ns->next;
+ continue;
+ }
+ luNs = xmlSearchNs(elem->doc, elem, ns->prefix);
+ if ((luNs != NULL) && (xmlStrEqual(luNs->href, ns->href)))
+ {
+ ns = ns->next;
+ continue;
+ }
+ }
+ q = xmlNewNs(elem, ns->href, ns->prefix);
+ if (p == NULL) {
+ ret = p = q;
+ } else if (q != NULL) {
+ p->next = q;
+ p = q;
+ }
+ ns = ns->next;
+ } while (ns != NULL);
+ return(ret);
+}
+
+/**
+ * xsltShallowCopyNsNode:
+ * @ctxt: the XSLT transformation context
+ * @invocNode: responsible node in the stylesheet; used for error reports
+ * @insert: the target element node in the result tree
+ * @ns: the namespace node
+ *
+ * This is used for copying ns-nodes with xsl:copy-of and xsl:copy.
+ *
+ * Returns a new/existing ns-node, or NULL.
+ */
+static xmlNsPtr
+xsltShallowCopyNsNode(xsltTransformContextPtr ctxt,
+ xmlNodePtr invocNode,
+ xmlNodePtr insert,
+ xmlNsPtr ns)
+{
+ /*
+ * TODO: Contrary to header comments, this is declared as int.
+ * be modified to return a node pointer, or NULL if any error
+ */
+ xmlNsPtr tmpns;
+
+ if ((insert == NULL) || (insert->type != XML_ELEMENT_NODE))
+ return(NULL);
+
+ if (insert->children != NULL) {
+ xsltTransformError(ctxt, NULL, invocNode,
+ "Namespace nodes must be added before "
+ "any child nodes are added to an element.\n");
+ return(NULL);
+ }
+ /*
+ * BIG NOTE: Xalan-J simply overwrites any ns-decls with
+ * an equal prefix. We definitively won't do that.
+ *
+ * MSXML 4.0 and the .NET ignores ns-decls for which an
+ * equal prefix is already in use.
+ *
+ * Saxon raises an error like:
+ * "net.sf.saxon.xpath.DynamicError: Cannot create two namespace
+ * nodes with the same name".
+ *
+ * NOTE: We'll currently follow MSXML here.
+ * REVISIT TODO: Check if it's better to follow Saxon here.
+ */
+ if (ns->prefix == NULL) {
+ /*
+ * If we are adding ns-nodes to an element using e.g.
+ * <xsl:copy-of select="/foo/namespace::*">, then we need
+ * to ensure that we don't incorrectly declare a default
+ * namespace on an element in no namespace, which otherwise
+ * would move the element incorrectly into a namespace, if
+ * the node tree is serialized.
+ */
+ if (insert->ns == NULL)
+ goto occupied;
+ } else if ((ns->prefix[0] == 'x') &&
+ xmlStrEqual(ns->prefix, BAD_CAST "xml"))
+ {
+ /*
+ * The XML namespace is built in.
+ */
+ return(NULL);
+ }
+
+ if (insert->nsDef != NULL) {
+ tmpns = insert->nsDef;
+ do {
+ if ((tmpns->prefix == NULL) == (ns->prefix == NULL)) {
+ if ((tmpns->prefix == ns->prefix) ||
+ xmlStrEqual(tmpns->prefix, ns->prefix))
+ {
+ /*
+ * Same prefix.
+ */
+ if (xmlStrEqual(tmpns->href, ns->href))
+ return(NULL);
+ goto occupied;
+ }
+ }
+ tmpns = tmpns->next;
+ } while (tmpns != NULL);
+ }
+ tmpns = xmlSearchNs(insert->doc, insert, ns->prefix);
+ if ((tmpns != NULL) && xmlStrEqual(tmpns->href, ns->href))
+ return(NULL);
+ /*
+ * Declare a new namespace.
+ * TODO: The problem (wrt efficiency) with this xmlNewNs() is
+ * that it will again search the already declared namespaces
+ * for a duplicate :-/
+ */
+ return(xmlNewNs(insert, ns->href, ns->prefix));
+
+occupied:
+ /*
+ * TODO: We could as well raise an error here (like Saxon does),
+ * or at least generate a warning.
+ */
+ return(NULL);
+}
+
+/**
+ * xsltCopyTreeInternal:
+ * @ctxt: the XSLT transformation context
+ * @invocNode: responsible node in the stylesheet; used for error reports
+ * @node: the element node in the source tree
+ * @insert: the parent in the result tree
+ * @isLRE: indicates if @node is a Literal Result Element
+ * @topElemVisited: indicates if a top-most element was already processed
+ *
+ * Make a copy of the full tree under the element node @node
+ * and insert it as last child of @insert
+ *
+ * NOTE: Not to be used for Literal Result Elements.
+ *
+ * Used by:
+ * - xsltCopyOf()
+ *
+ * Returns a pointer to the new tree, or NULL in case of error
+ */
+static xmlNodePtr
+xsltCopyTreeInternal(xsltTransformContextPtr ctxt,
+ xmlNodePtr invocNode,
+ xmlNodePtr node,
+ xmlNodePtr insert, int isLRE, int topElemVisited)
+{
+ xmlNodePtr copy;
+
+ if (node == NULL)
+ return(NULL);
+ switch (node->type) {
+ case XML_ELEMENT_NODE:
+ case XML_ENTITY_REF_NODE:
+ case XML_ENTITY_NODE:
+ case XML_PI_NODE:
+ case XML_COMMENT_NODE:
+ case XML_DOCUMENT_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+#ifdef LIBXML_DOCB_ENABLED
+ case XML_DOCB_DOCUMENT_NODE:
+#endif
+ break;
+ case XML_TEXT_NODE: {
+ int noenc = (node->name == xmlStringTextNoenc);
+ return(xsltCopyTextString(ctxt, insert, node->content, noenc));
+ }
+ case XML_CDATA_SECTION_NODE:
+ return(xsltCopyTextString(ctxt, insert, node->content, 0));
+ case XML_ATTRIBUTE_NODE:
+ return((xmlNodePtr)
+ xsltShallowCopyAttr(ctxt, invocNode, insert, (xmlAttrPtr) node));
+ case XML_NAMESPACE_DECL:
+ return((xmlNodePtr) xsltShallowCopyNsNode(ctxt, invocNode,
+ insert, (xmlNsPtr) node));
+
+ case XML_DOCUMENT_TYPE_NODE:
+ case XML_DOCUMENT_FRAG_NODE:
+ case XML_NOTATION_NODE:
+ case XML_DTD_NODE:
+ case XML_ELEMENT_DECL:
+ case XML_ATTRIBUTE_DECL:
+ case XML_ENTITY_DECL:
+ case XML_XINCLUDE_START:
+ case XML_XINCLUDE_END:
+ return(NULL);
+ }
+ if (XSLT_IS_RES_TREE_FRAG(node)) {
+ if (node->children != NULL)
+ copy = xsltCopyTreeList(ctxt, invocNode,
+ node->children, insert, 0, 0);
+ else
+ copy = NULL;
+ return(copy);
+ }
+ copy = xmlDocCopyNode(node, insert->doc, 0);
+ if (copy != NULL) {
+ copy->doc = ctxt->output;
+ copy = xsltAddChild(insert, copy);
+ if (copy == NULL) {
+ xsltTransformError(ctxt, NULL, invocNode,
+ "xsltCopyTreeInternal: Copying of '%s' failed.\n", node->name);
+ return (copy);
+ }
+ /*
+ * The node may have been coalesced into another text node.
+ */
+ if (insert->last != copy)
+ return(insert->last);
+ copy->next = NULL;
+
+ if (node->type == XML_ELEMENT_NODE) {
+ /*
+ * Copy in-scope namespace nodes.
+ *
+ * REVISIT: Since we try to reuse existing in-scope ns-decls by
+ * using xmlSearchNsByHref(), this will eventually change
+ * the prefix of an original ns-binding; thus it might
+ * break QNames in element/attribute content.
+ * OPTIMIZE TODO: If we had a xmlNsPtr * on the transformation
+ * context, plus a ns-lookup function, which writes directly
+ * to a given list, then we wouldn't need to create/free the
+ * nsList every time.
+ */
+ if ((topElemVisited == 0) &&
+ (node->parent != NULL) &&
+ (node->parent->type != XML_DOCUMENT_NODE) &&
+ (node->parent->type != XML_HTML_DOCUMENT_NODE))
+ {
+ xmlNsPtr *nsList, *curns, ns;
+
+ /*
+ * If this is a top-most element in a tree to be
+ * copied, then we need to ensure that all in-scope
+ * namespaces are copied over. For nodes deeper in the
+ * tree, it is sufficient to reconcile only the ns-decls
+ * (node->nsDef entries).
+ */
+
+ nsList = xmlGetNsList(node->doc, node);
+ if (nsList != NULL) {
+ curns = nsList;
+ do {
+ /*
+ * Search by prefix first in order to break as less
+ * QNames in element/attribute content as possible.
+ */
+ ns = xmlSearchNs(insert->doc, insert,
+ (*curns)->prefix);
+
+ if ((ns == NULL) ||
+ (! xmlStrEqual(ns->href, (*curns)->href)))
+ {
+ ns = NULL;
+ /*
+ * Search by namespace name.
+ * REVISIT TODO: Currently disabled.
+ */
+#if 0
+ ns = xmlSearchNsByHref(insert->doc,
+ insert, (*curns)->href);
+#endif
+ }
+ if (ns == NULL) {
+ /*
+ * Declare a new namespace on the copied element.
+ */
+ ns = xmlNewNs(copy, (*curns)->href,
+ (*curns)->prefix);
+ /* TODO: Handle errors */
+ }
+ if (node->ns == *curns) {
+ /*
+ * If this was the original's namespace then set
+ * the generated counterpart on the copy.
+ */
+ copy->ns = ns;
+ }
+ curns++;
+ } while (*curns != NULL);
+ xmlFree(nsList);
+ }
+ } else if (node->nsDef != NULL) {
+ /*
+ * Copy over all namespace declaration attributes.
+ */
+ if (node->nsDef != NULL) {
+ if (isLRE)
+ xsltCopyNamespaceList(ctxt, copy, node->nsDef);
+ else
+ xsltCopyNamespaceListInternal(copy, node->nsDef);
+ }
+ }
+ /*
+ * Set the namespace.
+ */
+ if (node->ns != NULL) {
+ if (copy->ns == NULL) {
+ /*
+ * This will map copy->ns to one of the newly created
+ * in-scope ns-decls, OR create a new ns-decl on @copy.
+ */
+ copy->ns = xsltGetSpecialNamespace(ctxt, invocNode,
+ node->ns->href, node->ns->prefix, copy);
+ }
+ } else if ((insert->type == XML_ELEMENT_NODE) &&
+ (insert->ns != NULL))
+ {
+ /*
+ * "Undeclare" the default namespace on @copy with xmlns="".
+ */
+ xsltGetSpecialNamespace(ctxt, invocNode, NULL, NULL, copy);
+ }
+ /*
+ * Copy attribute nodes.
+ */
+ if (node->properties != NULL) {
+ xsltCopyAttrListNoOverwrite(ctxt, invocNode,
+ copy, node->properties);
+ }
+ if (topElemVisited == 0)
+ topElemVisited = 1;
+ }
+ /*
+ * Copy the subtree.
+ */
+ if (node->children != NULL) {
+ xsltCopyTreeList(ctxt, invocNode,
+ node->children, copy, isLRE, topElemVisited);
+ }
+ } else {
+ xsltTransformError(ctxt, NULL, invocNode,
+ "xsltCopyTreeInternal: Copying of '%s' failed.\n", node->name);
+ }
+ return(copy);
+}
+
+/**
+ * xsltCopyTree:
+ * @ctxt: the XSLT transformation context
+ * @node: the element node in the source tree
+ * @insert: the parent in the result tree
+ * @literal: indicates if @node is a Literal Result Element
+ *
+ * Make a copy of the full tree under the element node @node
+ * and insert it as last child of @insert
+ * For literal result element, some of the namespaces may not be copied
+ * over according to section 7.1.
+ * TODO: Why is this a public function?
+ *
+ * Returns a pointer to the new tree, or NULL in case of error
+ */
+xmlNodePtr
+xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr insert, int literal)
+{
+ return(xsltCopyTreeInternal(ctxt, node, node, insert, literal, 0));
+
+}
+
+/************************************************************************
+ * *
+ * Error/fallback processing *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltApplyFallbacks:
+ * @ctxt: a XSLT process context
+ * @node: the node in the source tree.
+ * @inst: the node generating the error
+ *
+ * Process possible xsl:fallback nodes present under @inst
+ *
+ * Returns the number of xsl:fallback element found and processed
+ */
+static int
+xsltApplyFallbacks(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr inst) {
+
+ xmlNodePtr child;
+ int ret = 0;
+
+ if ((ctxt == NULL) || (node == NULL) || (inst == NULL) ||
+ (inst->children == NULL))
+ return(0);
+
+ child = inst->children;
+ while (child != NULL) {
+ if ((IS_XSLT_ELEM(child)) &&
+ (xmlStrEqual(child->name, BAD_CAST "fallback"))) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "applying xsl:fallback\n");
+#endif
+ ret++;
+ xsltApplySequenceConstructor(ctxt, node, child->children,
+ NULL);
+ }
+ child = child->next;
+ }
+ return(ret);
+}
+
+/************************************************************************
+ * *
+ * Default processing *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltDefaultProcessOneNode:
+ * @ctxt: a XSLT process context
+ * @node: the node in the source tree.
+ * @params: extra parameters passed to the template if any
+ *
+ * Process the source node with the default built-in template rule:
+ * <xsl:template match="*|/">
+ * <xsl:apply-templates/>
+ * </xsl:template>
+ *
+ * and
+ *
+ * <xsl:template match="text()|@*">
+ * <xsl:value-of select="."/>
+ * </xsl:template>
+ *
+ * Note also that namespace declarations are copied directly:
+ *
+ * the built-in template rule is the only template rule that is applied
+ * for namespace nodes.
+ */
+static void
+xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xsltStackElemPtr params) {
+ xmlNodePtr copy;
+ xmlNodePtr delete = NULL, cur;
+ int nbchild = 0, oldSize;
+ int childno = 0, oldPos;
+ xsltTemplatePtr template;
+
+ CHECK_STOPPED;
+ /*
+ * Handling of leaves
+ */
+ switch (node->type) {
+ case XML_DOCUMENT_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+ case XML_ELEMENT_NODE:
+ break;
+ case XML_CDATA_SECTION_NODE:
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltDefaultProcessOneNode: copy CDATA %s\n",
+ node->content));
+#endif
+ copy = xsltCopyText(ctxt, ctxt->insert, node, 0);
+ if (copy == NULL) {
+ xsltTransformError(ctxt, NULL, node,
+ "xsltDefaultProcessOneNode: cdata copy failed\n");
+ }
+ return;
+ case XML_TEXT_NODE:
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if (node->content == NULL) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltDefaultProcessOneNode: copy empty text\n"));
+ return;
+ } else {
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltDefaultProcessOneNode: copy text %s\n",
+ node->content));
+ }
+#endif
+ copy = xsltCopyText(ctxt, ctxt->insert, node, 0);
+ if (copy == NULL) {
+ xsltTransformError(ctxt, NULL, node,
+ "xsltDefaultProcessOneNode: text copy failed\n");
+ }
+ return;
+ case XML_ATTRIBUTE_NODE:
+ cur = node->children;
+ while ((cur != NULL) && (cur->type != XML_TEXT_NODE))
+ cur = cur->next;
+ if (cur == NULL) {
+ xsltTransformError(ctxt, NULL, node,
+ "xsltDefaultProcessOneNode: no text for attribute\n");
+ } else {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if (cur->content == NULL) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltDefaultProcessOneNode: copy empty text\n"));
+ } else {
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltDefaultProcessOneNode: copy text %s\n",
+ cur->content));
+ }
+#endif
+ copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
+ if (copy == NULL) {
+ xsltTransformError(ctxt, NULL, node,
+ "xsltDefaultProcessOneNode: text copy failed\n");
+ }
+ }
+ return;
+ default:
+ return;
+ }
+ /*
+ * Handling of Elements: first pass, cleanup and counting
+ */
+ cur = node->children;
+ while (cur != NULL) {
+ switch (cur->type) {
+ case XML_TEXT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_DOCUMENT_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+ case XML_ELEMENT_NODE:
+ case XML_PI_NODE:
+ case XML_COMMENT_NODE:
+ nbchild++;
+ break;
+ case XML_DTD_NODE:
+ /* Unlink the DTD, it's still reachable using doc->intSubset */
+ if (cur->next != NULL)
+ cur->next->prev = cur->prev;
+ if (cur->prev != NULL)
+ cur->prev->next = cur->next;
+ break;
+ default:
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltDefaultProcessOneNode: skipping node type %d\n",
+ cur->type));
+#endif
+ delete = cur;
+ }
+ cur = cur->next;
+ if (delete != NULL) {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltDefaultProcessOneNode: removing ignorable blank node\n"));
+#endif
+ xmlUnlinkNode(delete);
+ xmlFreeNode(delete);
+ delete = NULL;
+ }
+ }
+ if (delete != NULL) {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltDefaultProcessOneNode: removing ignorable blank node\n"));
+#endif
+ xmlUnlinkNode(delete);
+ xmlFreeNode(delete);
+ delete = NULL;
+ }
+
+ /*
+ * Handling of Elements: second pass, actual processing
+ *
+ * Note that params are passed to the next template. This matches
+ * XSLT 2.0 behavior but doesn't conform to XSLT 1.0.
+ */
+ oldSize = ctxt->xpathCtxt->contextSize;
+ oldPos = ctxt->xpathCtxt->proximityPosition;
+ cur = node->children;
+ while (cur != NULL) {
+ childno++;
+ switch (cur->type) {
+ case XML_DOCUMENT_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+ case XML_ELEMENT_NODE:
+ ctxt->xpathCtxt->contextSize = nbchild;
+ ctxt->xpathCtxt->proximityPosition = childno;
+ xsltProcessOneNode(ctxt, cur, params);
+ break;
+ case XML_CDATA_SECTION_NODE:
+ template = xsltGetTemplate(ctxt, cur, NULL);
+ if (template) {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltDefaultProcessOneNode: applying template for CDATA %s\n",
+ cur->content));
+#endif
+ /*
+ * Instantiate the xsl:template.
+ */
+ xsltApplyXSLTTemplate(ctxt, cur, template->content,
+ template, params);
+ } else /* if (ctxt->mode == NULL) */ {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltDefaultProcessOneNode: copy CDATA %s\n",
+ cur->content));
+#endif
+ copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
+ if (copy == NULL) {
+ xsltTransformError(ctxt, NULL, cur,
+ "xsltDefaultProcessOneNode: cdata copy failed\n");
+ }
+ }
+ break;
+ case XML_TEXT_NODE:
+ template = xsltGetTemplate(ctxt, cur, NULL);
+ if (template) {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltDefaultProcessOneNode: applying template for text %s\n",
+ cur->content));
+#endif
+ ctxt->xpathCtxt->contextSize = nbchild;
+ ctxt->xpathCtxt->proximityPosition = childno;
+ /*
+ * Instantiate the xsl:template.
+ */
+ xsltApplyXSLTTemplate(ctxt, cur, template->content,
+ template, params);
+ } else /* if (ctxt->mode == NULL) */ {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if (cur->content == NULL) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltDefaultProcessOneNode: copy empty text\n"));
+ } else {
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltDefaultProcessOneNode: copy text %s\n",
+ cur->content));
+ }
+#endif
+ copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
+ if (copy == NULL) {
+ xsltTransformError(ctxt, NULL, cur,
+ "xsltDefaultProcessOneNode: text copy failed\n");
+ }
+ }
+ break;
+ case XML_PI_NODE:
+ case XML_COMMENT_NODE:
+ template = xsltGetTemplate(ctxt, cur, NULL);
+ if (template) {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if (cur->type == XML_PI_NODE) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltDefaultProcessOneNode: template found for PI %s\n",
+ cur->name));
+ } else if (cur->type == XML_COMMENT_NODE) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltDefaultProcessOneNode: template found for comment\n"));
+ }
+#endif
+ ctxt->xpathCtxt->contextSize = nbchild;
+ ctxt->xpathCtxt->proximityPosition = childno;
+ /*
+ * Instantiate the xsl:template.
+ */
+ xsltApplyXSLTTemplate(ctxt, cur, template->content,
+ template, params);
+ }
+ break;
+ default:
+ break;
+ }
+ cur = cur->next;
+ }
+ ctxt->xpathCtxt->contextSize = oldSize;
+ ctxt->xpathCtxt->proximityPosition = oldPos;
+}
+
+/**
+ * xsltProcessOneNode:
+ * @ctxt: a XSLT process context
+ * @contextNode: the "current node" in the source tree
+ * @withParams: extra parameters (e.g. xsl:with-param) passed to the
+ * template if any
+ *
+ * Process the source node.
+ */
+void
+xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
+ xsltStackElemPtr withParams)
+{
+ xsltTemplatePtr templ;
+ xmlNodePtr oldNode;
+
+ templ = xsltGetTemplate(ctxt, contextNode, NULL);
+ /*
+ * If no template is found, apply the default rule.
+ */
+ if (templ == NULL) {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if (contextNode->type == XML_DOCUMENT_NODE) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltProcessOneNode: no template found for /\n"));
+ } else if (contextNode->type == XML_CDATA_SECTION_NODE) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltProcessOneNode: no template found for CDATA\n"));
+ } else if (contextNode->type == XML_ATTRIBUTE_NODE) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltProcessOneNode: no template found for attribute %s\n",
+ ((xmlAttrPtr) contextNode)->name));
+ } else {
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltProcessOneNode: no template found for %s\n", contextNode->name));
+ }
+#endif
+ oldNode = ctxt->node;
+ ctxt->node = contextNode;
+ xsltDefaultProcessOneNode(ctxt, contextNode, withParams);
+ ctxt->node = oldNode;
+ return;
+ }
+
+ if (contextNode->type == XML_ATTRIBUTE_NODE) {
+ xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule;
+ /*
+ * Set the "current template rule".
+ */
+ ctxt->currentTemplateRule = templ;
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltProcessOneNode: applying template '%s' for attribute %s\n",
+ templ->match, contextNode->name));
+#endif
+ xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams);
+
+ ctxt->currentTemplateRule = oldCurTempRule;
+ } else {
+ xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule;
+ /*
+ * Set the "current template rule".
+ */
+ ctxt->currentTemplateRule = templ;
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if (contextNode->type == XML_DOCUMENT_NODE) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltProcessOneNode: applying template '%s' for /\n",
+ templ->match));
+ } else {
+ XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltProcessOneNode: applying template '%s' for %s\n",
+ templ->match, contextNode->name));
+ }
+#endif
+ xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams);
+
+ ctxt->currentTemplateRule = oldCurTempRule;
+ }
+}
+
+static xmlNodePtr
+xsltDebuggerStartSequenceConstructor(xsltTransformContextPtr ctxt,
+ xmlNodePtr contextNode,
+ xmlNodePtr list,
+ xsltTemplatePtr templ,
+ int *addCallResult)
+{
+ xmlNodePtr debugedNode = NULL;
+
+ if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
+ if (templ) {
+ *addCallResult = xslAddCall(templ, templ->elem);
+ } else {
+ *addCallResult = xslAddCall(NULL, list);
+ }
+ switch (ctxt->debugStatus) {
+ case XSLT_DEBUG_RUN_RESTART:
+ case XSLT_DEBUG_QUIT:
+ if (*addCallResult)
+ xslDropCall();
+ return(NULL);
+ }
+ if (templ) {
+ xslHandleDebugger(templ->elem, contextNode, templ, ctxt);
+ debugedNode = templ->elem;
+ } else if (list) {
+ xslHandleDebugger(list, contextNode, templ, ctxt);
+ debugedNode = list;
+ } else if (ctxt->inst) {
+ xslHandleDebugger(ctxt->inst, contextNode, templ, ctxt);
+ debugedNode = ctxt->inst;
+ }
+ }
+ return(debugedNode);
+}
+
+/**
+ * xsltLocalVariablePush:
+ * @ctxt: the transformation context
+ * @variable: variable to be pushed to the variable stack
+ * @level: new value for variable's level
+ *
+ * Places the variable onto the local variable stack
+ *
+ * Returns: 0 for success, -1 for any error
+ * **NOTE:**
+ * This is an internal routine and should not be called by users!
+ */
+int
+xsltLocalVariablePush(xsltTransformContextPtr ctxt,
+ xsltStackElemPtr variable,
+ int level)
+{
+ if (ctxt->varsMax == 0) {
+ ctxt->varsMax = 10;
+ ctxt->varsTab =
+ (xsltStackElemPtr *) xmlMalloc(ctxt->varsMax *
+ sizeof(ctxt->varsTab[0]));
+ if (ctxt->varsTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
+ return (-1);
+ }
+ }
+ if (ctxt->varsNr >= ctxt->varsMax) {
+ ctxt->varsMax *= 2;
+ ctxt->varsTab =
+ (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab,
+ ctxt->varsMax *
+ sizeof(ctxt->varsTab[0]));
+ if (ctxt->varsTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
+ return (-1);
+ }
+ }
+ ctxt->varsTab[ctxt->varsNr++] = variable;
+ ctxt->vars = variable;
+ variable->level = level;
+ return(0);
+}
+
+/**
+ * xsltReleaseLocalRVTs:
+ *
+ * Fragments which are results of extension instructions
+ * are preserved; all other fragments are freed/cached.
+ */
+static void
+xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt, xmlDocPtr base)
+{
+ xmlDocPtr cur = ctxt->localRVT, tmp;
+
+ if (cur == base)
+ return;
+ if (cur->prev != NULL)
+ xsltTransformError(ctxt, NULL, NULL, "localRVT not head of list\n");
+
+ do {
+ tmp = cur;
+ cur = (xmlDocPtr) cur->next;
+ if (tmp->psvi == XSLT_RVT_LOCAL) {
+ xsltReleaseRVT(ctxt, tmp);
+ } else if (tmp->psvi == XSLT_RVT_GLOBAL) {
+ xsltRegisterPersistRVT(ctxt, tmp);
+ } else if (tmp->psvi != XSLT_RVT_FUNC_RESULT) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xsltReleaseLocalRVTs: Unexpected RVT flag %p\n",
+ tmp->psvi);
+ }
+ } while (cur != base);
+
+ if (base != NULL)
+ base->prev = NULL;
+ ctxt->localRVT = base;
+}
+
+/**
+ * xsltApplySequenceConstructor:
+ * @ctxt: a XSLT process context
+ * @contextNode: the "current node" in the source tree
+ * @list: the nodes of a sequence constructor;
+ * (plus leading xsl:param elements)
+ * @templ: the compiled xsl:template (optional)
+ *
+ * Processes a sequence constructor.
+ *
+ * NOTE: ctxt->currentTemplateRule was introduced to reflect the
+ * semantics of "current template rule". I.e. the field ctxt->templ
+ * is not intended to reflect this, thus always pushed onto the
+ * template stack.
+ */
+static void
+xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
+ xmlNodePtr contextNode, xmlNodePtr list,
+ xsltTemplatePtr templ)
+{
+ xmlNodePtr oldInsert, oldInst, oldCurInst, oldContextNode;
+ xmlNodePtr cur, insert, copy = NULL;
+ int level = 0, oldVarsNr;
+ xmlDocPtr oldLocalFragmentTop;
+
+#ifdef XSLT_REFACTORED
+ xsltStylePreCompPtr info;
+#endif
+
+#ifdef WITH_DEBUGGER
+ int addCallResult = 0;
+ xmlNodePtr debuggedNode = NULL;
+#endif
+
+ if (ctxt == NULL)
+ return;
+
+#ifdef WITH_DEBUGGER
+ if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
+ debuggedNode =
+ xsltDebuggerStartSequenceConstructor(ctxt, contextNode,
+ list, templ, &addCallResult);
+ if (debuggedNode == NULL)
+ return;
+ }
+#endif
+
+ if (list == NULL)
+ return;
+ CHECK_STOPPED;
+
+ /*
+ * Check for infinite recursion: stop if the maximum of nested templates
+ * is excceeded. Adjust xsltMaxDepth if you need more.
+ */
+ if (ctxt->depth >= ctxt->maxTemplateDepth) {
+ xsltTransformError(ctxt, NULL, list,
+ "xsltApplySequenceConstructor: A potential infinite template "
+ "recursion was detected.\n"
+ "You can adjust xsltMaxDepth (--maxdepth) in order to "
+ "raise the maximum number of nested template calls and "
+ "variables/params (currently set to %d).\n",
+ ctxt->maxTemplateDepth);
+ xsltDebug(ctxt, contextNode, list, NULL);
+ ctxt->state = XSLT_STATE_STOPPED;
+ return;
+ }
+ ctxt->depth++;
+
+ oldLocalFragmentTop = ctxt->localRVT;
+ oldInsert = insert = ctxt->insert;
+ oldInst = oldCurInst = ctxt->inst;
+ oldContextNode = ctxt->node;
+ /*
+ * Save current number of variables on the stack; new vars are popped when
+ * exiting.
+ */
+ oldVarsNr = ctxt->varsNr;
+ /*
+ * Process the sequence constructor.
+ */
+ cur = list;
+ while (cur != NULL) {
+ ctxt->inst = cur;
+
+#ifdef WITH_DEBUGGER
+ switch (ctxt->debugStatus) {
+ case XSLT_DEBUG_RUN_RESTART:
+ case XSLT_DEBUG_QUIT:
+ break;
+
+ }
+#endif
+ /*
+ * Test; we must have a valid insertion point.
+ */
+ if (insert == NULL) {
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplySequenceConstructor: insert == NULL !\n"));
+#endif
+ goto error;
+ }
+
+#ifdef WITH_DEBUGGER
+ if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (debuggedNode != cur))
+ xslHandleDebugger(cur, contextNode, templ, ctxt);
+#endif
+
+#ifdef XSLT_REFACTORED
+ if (cur->type == XML_ELEMENT_NODE) {
+ info = (xsltStylePreCompPtr) cur->psvi;
+ /*
+ * We expect a compiled representation on:
+ * 1) XSLT instructions of this XSLT version (1.0)
+ * (with a few exceptions)
+ * 2) Literal result elements
+ * 3) Extension instructions
+ * 4) XSLT instructions of future XSLT versions
+ * (forwards-compatible mode).
+ */
+ if (info == NULL) {
+ /*
+ * Handle the rare cases where we don't expect a compiled
+ * representation on an XSLT element.
+ */
+ if (IS_XSLT_ELEM_FAST(cur) && IS_XSLT_NAME(cur, "message")) {
+ xsltMessage(ctxt, contextNode, cur);
+ goto skip_children;
+ }
+ /*
+ * Something really went wrong:
+ */
+ xsltTransformError(ctxt, NULL, cur,
+ "Internal error in xsltApplySequenceConstructor(): "
+ "The element '%s' in the stylesheet has no compiled "
+ "representation.\n",
+ cur->name);
+ goto skip_children;
+ }
+
+ if (info->type == XSLT_FUNC_LITERAL_RESULT_ELEMENT) {
+ xsltStyleItemLRElementInfoPtr lrInfo =
+ (xsltStyleItemLRElementInfoPtr) info;
+ /*
+ * Literal result elements
+ * --------------------------------------------------------
+ */
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplySequenceConstructor: copy literal result "
+ "element '%s'\n", cur->name));
+#endif
+ /*
+ * Copy the raw element-node.
+ * OLD: if ((copy = xsltShallowCopyElem(ctxt, cur, insert))
+ * == NULL)
+ * goto error;
+ */
+ copy = xmlDocCopyNode(cur, insert->doc, 0);
+ if (copy == NULL) {
+ xsltTransformError(ctxt, NULL, cur,
+ "Internal error in xsltApplySequenceConstructor(): "
+ "Failed to copy literal result element '%s'.\n",
+ cur->name);
+ goto error;
+ } else {
+ /*
+ * Add the element-node to the result tree.
+ */
+ copy->doc = ctxt->output;
+ copy = xsltAddChild(insert, copy);
+ /*
+ * Create effective namespaces declarations.
+ * OLD: xsltCopyNamespaceList(ctxt, copy, cur->nsDef);
+ */
+ if (lrInfo->effectiveNs != NULL) {
+ xsltEffectiveNsPtr effNs = lrInfo->effectiveNs;
+ xmlNsPtr ns, lastns = NULL;
+
+ while (effNs != NULL) {
+ /*
+ * Avoid generating redundant namespace
+ * declarations; thus lookup if there is already
+ * such a ns-decl in the result.
+ */
+ ns = xmlSearchNs(copy->doc, copy, effNs->prefix);
+ if ((ns != NULL) &&
+ (xmlStrEqual(ns->href, effNs->nsName)))
+ {
+ effNs = effNs->next;
+ continue;
+ }
+ ns = xmlNewNs(copy, effNs->nsName, effNs->prefix);
+ if (ns == NULL) {
+ xsltTransformError(ctxt, NULL, cur,
+ "Internal error in "
+ "xsltApplySequenceConstructor(): "
+ "Failed to copy a namespace "
+ "declaration.\n");
+ goto error;
+ }
+
+ if (lastns == NULL)
+ copy->nsDef = ns;
+ else
+ lastns->next =ns;
+ lastns = ns;
+
+ effNs = effNs->next;
+ }
+
+ }
+ /*
+ * NOTE that we don't need to apply ns-alising: this was
+ * already done at compile-time.
+ */
+ if (cur->ns != NULL) {
+ /*
+ * If there's no such ns-decl in the result tree,
+ * then xsltGetSpecialNamespace() will
+ * create a ns-decl on the copied node.
+ */
+ copy->ns = xsltGetSpecialNamespace(ctxt, cur,
+ cur->ns->href, cur->ns->prefix, copy);
+ } else {
+ /*
+ * Undeclare the default namespace if needed.
+ * This can be skipped, if the result element has
+ * no ns-decls, in which case the result element
+ * obviously does not declare a default namespace;
+ * AND there's either no parent, or the parent
+ * element is in no namespace; this means there's no
+ * default namespace is scope to care about.
+ *
+ * REVISIT: This might result in massive
+ * generation of ns-decls if nodes in a default
+ * namespaces are mixed with nodes in no namespace.
+ *
+ */
+ if (copy->nsDef ||
+ ((insert != NULL) &&
+ (insert->type == XML_ELEMENT_NODE) &&
+ (insert->ns != NULL)))
+ {
+ xsltGetSpecialNamespace(ctxt, cur,
+ NULL, NULL, copy);
+ }
+ }
+ }
+ /*
+ * SPEC XSLT 2.0 "Each attribute of the literal result
+ * element, other than an attribute in the XSLT namespace,
+ * is processed to produce an attribute for the element in
+ * the result tree."
+ * NOTE: See bug #341325.
+ */
+ if (cur->properties != NULL) {
+ xsltAttrListTemplateProcess(ctxt, copy, cur->properties);
+ }
+ } else if (IS_XSLT_ELEM_FAST(cur)) {
+ /*
+ * XSLT instructions
+ * --------------------------------------------------------
+ */
+ if (info->type == XSLT_FUNC_UNKOWN_FORWARDS_COMPAT) {
+ /*
+ * We hit an unknown XSLT element.
+ * Try to apply one of the fallback cases.
+ */
+ ctxt->insert = insert;
+ if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
+ xsltTransformError(ctxt, NULL, cur,
+ "The is no fallback behaviour defined for "
+ "the unknown XSLT element '%s'.\n",
+ cur->name);
+ }
+ ctxt->insert = oldInsert;
+ } else if (info->func != NULL) {
+ /*
+ * Execute the XSLT instruction.
+ */
+ ctxt->insert = insert;
+
+ info->func(ctxt, contextNode, cur,
+ (xsltElemPreCompPtr) info);
+
+ /*
+ * Cleanup temporary tree fragments.
+ */
+ if (oldLocalFragmentTop != ctxt->localRVT)
+ xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
+
+ ctxt->insert = oldInsert;
+ } else if (info->type == XSLT_FUNC_VARIABLE) {
+ xsltStackElemPtr tmpvar = ctxt->vars;
+
+ xsltParseStylesheetVariable(ctxt, cur);
+
+ if (tmpvar != ctxt->vars) {
+ /*
+ * TODO: Using a @tmpvar is an annoying workaround, but
+ * the current mechanisms do not provide any other way
+ * of knowing if the var was really pushed onto the
+ * stack.
+ */
+ ctxt->vars->level = level;
+ }
+ } else if (info->type == XSLT_FUNC_MESSAGE) {
+ /*
+ * TODO: Won't be hit, since we don't compile xsl:message.
+ */
+ xsltMessage(ctxt, contextNode, cur);
+ } else {
+ xsltTransformError(ctxt, NULL, cur,
+ "Unexpected XSLT element '%s'.\n", cur->name);
+ }
+ goto skip_children;
+
+ } else {
+ xsltTransformFunction func;
+ /*
+ * Extension intructions (elements)
+ * --------------------------------------------------------
+ */
+ if (cur->psvi == xsltExtMarker) {
+ /*
+ * The xsltExtMarker was set during the compilation
+ * of extension instructions if there was no registered
+ * handler for this specific extension function at
+ * compile-time.
+ * Libxslt will now lookup if a handler is
+ * registered in the context of this transformation.
+ */
+ func = (xsltTransformFunction)
+ xsltExtElementLookup(ctxt, cur->name, cur->ns->href);
+ } else
+ func = ((xsltElemPreCompPtr) cur->psvi)->func;
+
+ if (func == NULL) {
+ /*
+ * No handler available.
+ * Try to execute fallback behaviour via xsl:fallback.
+ */
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplySequenceConstructor: unknown extension %s\n",
+ cur->name));
+#endif
+ ctxt->insert = insert;
+ if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
+ xsltTransformError(ctxt, NULL, cur,
+ "Unknown extension instruction '{%s}%s'.\n",
+ cur->ns->href, cur->name);
+ }
+ ctxt->insert = oldInsert;
+ } else {
+ /*
+ * Execute the handler-callback.
+ */
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplySequenceConstructor: extension construct %s\n",
+ cur->name));
+#endif
+ ctxt->insert = insert;
+
+ func(ctxt, contextNode, cur, cur->psvi);
+
+ /*
+ * Cleanup temporary tree fragments.
+ */
+ if (oldLocalFragmentTop != ctxt->localRVT)
+ xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
+
+ ctxt->insert = oldInsert;
+ }
+ goto skip_children;
+ }
+
+ } else if (XSLT_IS_TEXT_NODE(cur)) {
+ /*
+ * Text
+ * ------------------------------------------------------------
+ */
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if (cur->name == xmlStringTextNoenc) {
+ XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplySequenceConstructor: copy unescaped text '%s'\n",
+ cur->content));
+ } else {
+ XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplySequenceConstructor: copy text '%s'\n",
+ cur->content));
+ }
+#endif
+ if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL)
+ goto error;
+ }
+
+#else /* XSLT_REFACTORED */
+
+ if (IS_XSLT_ELEM(cur)) {
+ /*
+ * This is an XSLT node
+ */
+ xsltStylePreCompPtr info = (xsltStylePreCompPtr) cur->psvi;
+
+ if (info == NULL) {
+ if (IS_XSLT_NAME(cur, "message")) {
+ xsltMessage(ctxt, contextNode, cur);
+ } else {
+ /*
+ * That's an error try to apply one of the fallback cases
+ */
+ ctxt->insert = insert;
+ if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltApplySequenceConstructor: %s was not compiled\n",
+ cur->name);
+ }
+ ctxt->insert = oldInsert;
+ }
+ goto skip_children;
+ }
+
+ if (info->func != NULL) {
+ oldCurInst = ctxt->inst;
+ ctxt->inst = cur;
+ ctxt->insert = insert;
+
+ info->func(ctxt, contextNode, cur, (xsltElemPreCompPtr) info);
+
+ /*
+ * Cleanup temporary tree fragments.
+ */
+ if (oldLocalFragmentTop != ctxt->localRVT)
+ xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
+
+ ctxt->insert = oldInsert;
+ ctxt->inst = oldCurInst;
+ goto skip_children;
+ }
+
+ if (IS_XSLT_NAME(cur, "variable")) {
+ xsltStackElemPtr tmpvar = ctxt->vars;
+
+ oldCurInst = ctxt->inst;
+ ctxt->inst = cur;
+
+ xsltParseStylesheetVariable(ctxt, cur);
+
+ ctxt->inst = oldCurInst;
+
+ if (tmpvar != ctxt->vars) {
+ /*
+ * TODO: Using a @tmpvar is an annoying workaround, but
+ * the current mechanisms do not provide any other way
+ * of knowing if the var was really pushed onto the
+ * stack.
+ */
+ ctxt->vars->level = level;
+ }
+ } else if (IS_XSLT_NAME(cur, "message")) {
+ xsltMessage(ctxt, contextNode, cur);
+ } else {
+ xsltTransformError(ctxt, NULL, cur,
+ "Unexpected XSLT element '%s'.\n", cur->name);
+ }
+ goto skip_children;
+ } else if ((cur->type == XML_TEXT_NODE) ||
+ (cur->type == XML_CDATA_SECTION_NODE)) {
+
+ /*
+ * This text comes from the stylesheet
+ * For stylesheets, the set of whitespace-preserving
+ * element names consists of just xsl:text.
+ */
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if (cur->type == XML_CDATA_SECTION_NODE) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplySequenceConstructor: copy CDATA text %s\n",
+ cur->content));
+ } else if (cur->name == xmlStringTextNoenc) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplySequenceConstructor: copy unescaped text %s\n",
+ cur->content));
+ } else {
+ XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplySequenceConstructor: copy text %s\n",
+ cur->content));
+ }
+#endif
+ if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL)
+ goto error;
+ } else if ((cur->type == XML_ELEMENT_NODE) &&
+ (cur->ns != NULL) && (cur->psvi != NULL)) {
+ xsltTransformFunction function;
+
+ oldCurInst = ctxt->inst;
+ ctxt->inst = cur;
+ /*
+ * Flagged as an extension element
+ */
+ if (cur->psvi == xsltExtMarker)
+ function = (xsltTransformFunction)
+ xsltExtElementLookup(ctxt, cur->name, cur->ns->href);
+ else
+ function = ((xsltElemPreCompPtr) cur->psvi)->func;
+
+ if (function == NULL) {
+ xmlNodePtr child;
+ int found = 0;
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplySequenceConstructor: unknown extension %s\n",
+ cur->name));
+#endif
+ /*
+ * Search if there are fallbacks
+ */
+ child = cur->children;
+ while (child != NULL) {
+ if ((IS_XSLT_ELEM(child)) &&
+ (IS_XSLT_NAME(child, "fallback")))
+ {
+ found = 1;
+ xsltApplySequenceConstructor(ctxt, contextNode,
+ child->children, NULL);
+ }
+ child = child->next;
+ }
+
+ if (!found) {
+ xsltTransformError(ctxt, NULL, cur,
+ "xsltApplySequenceConstructor: failed to find extension %s\n",
+ cur->name);
+ }
+ } else {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplySequenceConstructor: extension construct %s\n",
+ cur->name));
+#endif
+
+ ctxt->insert = insert;
+
+ function(ctxt, contextNode, cur, cur->psvi);
+ /*
+ * Cleanup temporary tree fragments.
+ */
+ if (oldLocalFragmentTop != ctxt->localRVT)
+ xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
+
+ ctxt->insert = oldInsert;
+
+ }
+ ctxt->inst = oldCurInst;
+ goto skip_children;
+ } else if (cur->type == XML_ELEMENT_NODE) {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplySequenceConstructor: copy node %s\n",
+ cur->name));
+#endif
+ oldCurInst = ctxt->inst;
+ ctxt->inst = cur;
+
+ if ((copy = xsltShallowCopyElem(ctxt, cur, insert, 1)) == NULL)
+ goto error;
+ /*
+ * Add extra namespaces inherited from the current template
+ * if we are in the first level children and this is a
+ * "real" template.
+ */
+ if ((templ != NULL) && (oldInsert == insert) &&
+ (ctxt->templ != NULL) && (ctxt->templ->inheritedNs != NULL)) {
+ int i;
+ xmlNsPtr ns, ret;
+
+ for (i = 0; i < ctxt->templ->inheritedNsNr; i++) {
+ const xmlChar *URI = NULL;
+ xsltStylesheetPtr style;
+ ns = ctxt->templ->inheritedNs[i];
+
+ /* Note that the XSLT namespace was already excluded
+ * in xsltGetInheritedNsList().
+ */
+#if 0
+ if (xmlStrEqual(ns->href, XSLT_NAMESPACE))
+ continue;
+#endif
+ style = ctxt->style;
+ while (style != NULL) {
+ if (style->nsAliases != NULL)
+ URI = (const xmlChar *)
+ xmlHashLookup(style->nsAliases, ns->href);
+ if (URI != NULL)
+ break;
+
+ style = xsltNextImport(style);
+ }
+ if (URI == UNDEFINED_DEFAULT_NS)
+ continue;
+ if (URI == NULL)
+ URI = ns->href;
+ /*
+ * TODO: The following will still be buggy for the
+ * non-refactored code.
+ */
+ ret = xmlSearchNs(copy->doc, copy, ns->prefix);
+ if ((ret == NULL) || (!xmlStrEqual(ret->href, URI)))
+ {
+ xmlNewNs(copy, URI, ns->prefix);
+ }
+ }
+ if (copy->ns != NULL) {
+ /*
+ * Fix the node namespace if needed
+ */
+ copy->ns = xsltGetNamespace(ctxt, cur, copy->ns, copy);
+ }
+ }
+ /*
+ * all the attributes are directly inherited
+ */
+ if (cur->properties != NULL) {
+ xsltAttrListTemplateProcess(ctxt, copy, cur->properties);
+ }
+ ctxt->inst = oldCurInst;
+ }
+#endif /* else of XSLT_REFACTORED */
+
+ /*
+ * Descend into content in document order.
+ */
+ if (cur->children != NULL) {
+ if (cur->children->type != XML_ENTITY_DECL) {
+ cur = cur->children;
+ level++;
+ if (copy != NULL)
+ insert = copy;
+ continue;
+ }
+ }
+
+skip_children:
+ /*
+ * If xslt:message was just processed, we might have hit a
+ * terminate='yes'; if so, then break the loop and clean up.
+ * TODO: Do we need to check this also before trying to descend
+ * into the content?
+ */
+ if (ctxt->state == XSLT_STATE_STOPPED)
+ break;
+ if (cur->next != NULL) {
+ cur = cur->next;
+ continue;
+ }
+
+ do {
+ cur = cur->parent;
+ level--;
+ /*
+ * Pop variables/params (xsl:variable and xsl:param).
+ */
+ if ((ctxt->varsNr > oldVarsNr) && (ctxt->vars->level > level)) {
+ xsltLocalVariablePop(ctxt, oldVarsNr, level);
+ }
+
+ insert = insert->parent;
+ if (cur == NULL)
+ break;
+ if (cur == list->parent) {
+ cur = NULL;
+ break;
+ }
+ if (cur->next != NULL) {
+ cur = cur->next;
+ break;
+ }
+ } while (cur != NULL);
+ }
+
+error:
+ /*
+ * In case of errors: pop remaining variables.
+ */
+ if (ctxt->varsNr > oldVarsNr)
+ xsltLocalVariablePop(ctxt, oldVarsNr, -1);
+
+ ctxt->node = oldContextNode;
+ ctxt->inst = oldInst;
+ ctxt->insert = oldInsert;
+
+ ctxt->depth--;
+
+#ifdef WITH_DEBUGGER
+ if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) {
+ xslDropCall();
+ }
+#endif
+}
+
+/*
+* xsltApplyXSLTTemplate:
+* @ctxt: a XSLT transformation context
+* @contextNode: the node in the source tree.
+* @list: the nodes of a sequence constructor;
+* (plus leading xsl:param elements)
+* @templ: the compiled xsl:template declaration;
+* NULL if a sequence constructor
+* @withParams: a set of caller-parameters (xsl:with-param) or NULL
+*
+* Called by:
+* - xsltApplyImports()
+* - xsltCallTemplate()
+* - xsltDefaultProcessOneNode()
+* - xsltProcessOneNode()
+*/
+static void
+xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt,
+ xmlNodePtr contextNode,
+ xmlNodePtr list,
+ xsltTemplatePtr templ,
+ xsltStackElemPtr withParams)
+{
+ int oldVarsBase = 0;
+ long start = 0;
+ xmlNodePtr cur;
+ xsltStackElemPtr tmpParam = NULL;
+ xmlDocPtr oldUserFragmentTop;
+
+#ifdef XSLT_REFACTORED
+ xsltStyleItemParamPtr iparam;
+#else
+ xsltStylePreCompPtr iparam;
+#endif
+
+#ifdef WITH_DEBUGGER
+ int addCallResult = 0;
+#endif
+
+ if (ctxt == NULL)
+ return;
+ if (templ == NULL) {
+ xsltTransformError(ctxt, NULL, list,
+ "xsltApplyXSLTTemplate: Bad arguments; @templ is mandatory.\n");
+ return;
+ }
+
+#ifdef WITH_DEBUGGER
+ if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
+ if (xsltDebuggerStartSequenceConstructor(ctxt, contextNode,
+ list, templ, &addCallResult) == NULL)
+ return;
+ }
+#endif
+
+ if (list == NULL)
+ return;
+ CHECK_STOPPED;
+
+ if (ctxt->varsNr >= ctxt->maxTemplateVars)
+ {
+ xsltTransformError(ctxt, NULL, list,
+ "xsltApplyXSLTTemplate: A potential infinite template recursion "
+ "was detected.\n"
+ "You can adjust maxTemplateVars (--maxvars) in order to "
+ "raise the maximum number of variables/params (currently set to %d).\n",
+ ctxt->maxTemplateVars);
+ xsltDebug(ctxt, contextNode, list, NULL);
+ ctxt->state = XSLT_STATE_STOPPED;
+ return;
+ }
+
+ oldUserFragmentTop = ctxt->tmpRVT;
+ ctxt->tmpRVT = NULL;
+
+ /*
+ * Initiate a distinct scope of local params/variables.
+ */
+ oldVarsBase = ctxt->varsBase;
+ ctxt->varsBase = ctxt->varsNr;
+
+ ctxt->node = contextNode;
+ if (ctxt->profile) {
+ templ->nbCalls++;
+ start = xsltTimestamp();
+ profPush(ctxt, 0);
+ profCallgraphAdd(templ, ctxt->templ);
+ }
+ /*
+ * Push the xsl:template declaration onto the stack.
+ */
+ templPush(ctxt, templ);
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if (templ->name != NULL)
+ XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
+ "applying xsl:template '%s'\n", templ->name));
+#endif
+ /*
+ * Process xsl:param instructions and skip those elements for
+ * further processing.
+ */
+ cur = list;
+ do {
+ if (cur->type == XML_TEXT_NODE) {
+ cur = cur->next;
+ continue;
+ }
+ if ((cur->type != XML_ELEMENT_NODE) ||
+ (cur->name[0] != 'p') ||
+ (cur->psvi == NULL) ||
+ (! xmlStrEqual(cur->name, BAD_CAST "param")) ||
+ (! IS_XSLT_ELEM(cur)))
+ {
+ break;
+ }
+
+ list = cur->next;
+
+#ifdef XSLT_REFACTORED
+ iparam = (xsltStyleItemParamPtr) cur->psvi;
+#else
+ iparam = (xsltStylePreCompPtr) cur->psvi;
+#endif
+
+ /*
+ * Substitute xsl:param for a given xsl:with-param.
+ * Since the XPath expression will reference the params/vars
+ * by index, we need to slot the xsl:with-params in the
+ * order of encountered xsl:params to keep the sequence of
+ * params/variables in the stack exactly as it was at
+ * compile time,
+ */
+ tmpParam = NULL;
+ if (withParams) {
+ tmpParam = withParams;
+ do {
+ if ((tmpParam->name == (iparam->name)) &&
+ (tmpParam->nameURI == (iparam->ns)))
+ {
+ /*
+ * Push the caller-parameter.
+ */
+ xsltLocalVariablePush(ctxt, tmpParam, -1);
+ break;
+ }
+ tmpParam = tmpParam->next;
+ } while (tmpParam != NULL);
+ }
+ /*
+ * Push the xsl:param.
+ */
+ if (tmpParam == NULL) {
+ /*
+ * Note that we must assume that the added parameter
+ * has a @depth of 0.
+ */
+ xsltParseStylesheetParam(ctxt, cur);
+ }
+ cur = cur->next;
+ } while (cur != NULL);
+ /*
+ * Process the sequence constructor.
+ */
+ xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
+
+ /*
+ * Remove remaining xsl:param and xsl:with-param items from
+ * the stack. Don't free xsl:with-param items.
+ */
+ if (ctxt->varsNr > ctxt->varsBase)
+ xsltTemplateParamsCleanup(ctxt);
+ ctxt->varsBase = oldVarsBase;
+
+ /*
+ * Release user-created fragments stored in the scope
+ * of xsl:template. Note that this mechanism is deprecated:
+ * user code should now use xsltRegisterLocalRVT() instead
+ * of the obsolete xsltRegisterTmpRVT().
+ */
+ if (ctxt->tmpRVT) {
+ xmlDocPtr curdoc = ctxt->tmpRVT, tmp;
+
+ while (curdoc != NULL) {
+ tmp = curdoc;
+ curdoc = (xmlDocPtr) curdoc->next;
+ xsltReleaseRVT(ctxt, tmp);
+ }
+ }
+ ctxt->tmpRVT = oldUserFragmentTop;
+
+ /*
+ * Pop the xsl:template declaration from the stack.
+ */
+ templPop(ctxt);
+ if (ctxt->profile) {
+ long spent, child, total, end;
+
+ end = xsltTimestamp();
+ child = profPop(ctxt);
+ total = end - start;
+ spent = total - child;
+ if (spent <= 0) {
+ /*
+ * Not possible unless the original calibration failed
+ * we can try to correct it on the fly.
+ */
+ xsltCalibrateAdjust(spent);
+ spent = 0;
+ }
+
+ templ->time += spent;
+ if (ctxt->profNr > 0)
+ ctxt->profTab[ctxt->profNr - 1] += total;
+ }
+
+#ifdef WITH_DEBUGGER
+ if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) {
+ xslDropCall();
+ }
+#endif
+}
+
+
+/**
+ * xsltApplyOneTemplate:
+ * @ctxt: a XSLT process context
+ * @contextNode: the node in the source tree.
+ * @list: the nodes of a sequence constructor
+ * @templ: not used
+ * @params: a set of parameters (xsl:param) or NULL
+ *
+ * Processes a sequence constructor on the current node in the source tree.
+ *
+ * @params are the already computed variable stack items; this function
+ * pushes them on the variable stack, and pops them before exiting; it's
+ * left to the caller to free or reuse @params afterwards. The initial
+ * states of the variable stack will always be restored before this
+ * function exits.
+ * NOTE that this does *not* initiate a new distinct variable scope; i.e.
+ * variables already on the stack are visible to the process. The caller's
+ * side needs to start a new variable scope if needed (e.g. in exsl:function).
+ *
+ * @templ is obsolete and not used anymore (e.g. <exslt:function> does not
+ * provide a @templ); a non-NULL @templ might raise an error in the future.
+ *
+ * BIG NOTE: This function is not intended to process the content of an
+ * xsl:template; it does not expect xsl:param instructions in @list and
+ * will report errors if found.
+ *
+ * Called by:
+ * - xsltEvalVariable() (variables.c)
+ * - exsltFuncFunctionFunction() (libexsl/functions.c)
+ */
+void
+xsltApplyOneTemplate(xsltTransformContextPtr ctxt,
+ xmlNodePtr contextNode,
+ xmlNodePtr list,
+ xsltTemplatePtr templ ATTRIBUTE_UNUSED,
+ xsltStackElemPtr params)
+{
+ if ((ctxt == NULL) || (list == NULL))
+ return;
+ CHECK_STOPPED;
+
+ if (params) {
+ /*
+ * This code should be obsolete - was previously used
+ * by libexslt/functions.c, but due to bug 381319 the
+ * logic there was changed.
+ */
+ int oldVarsNr = ctxt->varsNr;
+
+ /*
+ * Push the given xsl:param(s) onto the variable stack.
+ */
+ while (params != NULL) {
+ xsltLocalVariablePush(ctxt, params, -1);
+ params = params->next;
+ }
+ xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
+ /*
+ * Pop the given xsl:param(s) from the stack but don't free them.
+ */
+ xsltLocalVariablePop(ctxt, oldVarsNr, -2);
+ } else
+ xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
+}
+
+/************************************************************************
+ * *
+ * XSLT-1.1 extensions *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltDocumentElem:
+ * @ctxt: an XSLT processing context
+ * @node: The current node
+ * @inst: the instruction in the stylesheet
+ * @castedComp: precomputed information
+ *
+ * Process an EXSLT/XSLT-1.1 document element
+ */
+void
+xsltDocumentElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr inst, xsltStylePreCompPtr castedComp)
+{
+#ifdef XSLT_REFACTORED
+ xsltStyleItemDocumentPtr comp = (xsltStyleItemDocumentPtr) castedComp;
+#else
+ xsltStylePreCompPtr comp = castedComp;
+#endif
+ xsltStylesheetPtr style = NULL;
+ int ret;
+ xmlChar *filename = NULL, *prop, *elements;
+ xmlChar *element, *end;
+ xmlDocPtr res = NULL;
+ xmlDocPtr oldOutput;
+ xmlNodePtr oldInsert, root;
+ const char *oldOutputFile;
+ xsltOutputType oldType;
+ xmlChar *URL = NULL;
+ const xmlChar *method;
+ const xmlChar *doctypePublic;
+ const xmlChar *doctypeSystem;
+ const xmlChar *version;
+ const xmlChar *encoding;
+ int redirect_write_append = 0;
+
+ if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
+ return;
+
+ if (comp->filename == NULL) {
+
+ if (xmlStrEqual(inst->name, (const xmlChar *) "output")) {
+ /*
+ * The element "output" is in the namespace XSLT_SAXON_NAMESPACE
+ * (http://icl.com/saxon)
+ * The @file is in no namespace.
+ */
+#ifdef WITH_XSLT_DEBUG_EXTRA
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Found saxon:output extension\n");
+#endif
+ URL = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *) "file",
+ XSLT_SAXON_NAMESPACE);
+
+ if (URL == NULL)
+ URL = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *) "href",
+ XSLT_SAXON_NAMESPACE);
+ } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) {
+#ifdef WITH_XSLT_DEBUG_EXTRA
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Found xalan:write extension\n");
+#endif
+ URL = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *)
+ "select",
+ XSLT_XALAN_NAMESPACE);
+ if (URL != NULL) {
+ xmlXPathCompExprPtr cmp;
+ xmlChar *val;
+
+ /*
+ * Trying to handle bug #59212
+ * The value of the "select" attribute is an
+ * XPath expression.
+ * (see http://xml.apache.org/xalan-j/extensionslib.html#redirect)
+ */
+ cmp = xmlXPathCompile(URL);
+ val = xsltEvalXPathString(ctxt, cmp);
+ xmlXPathFreeCompExpr(cmp);
+ xmlFree(URL);
+ URL = val;
+ }
+ if (URL == NULL)
+ URL = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *)
+ "file",
+ XSLT_XALAN_NAMESPACE);
+ if (URL == NULL)
+ URL = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *)
+ "href",
+ XSLT_XALAN_NAMESPACE);
+ } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) {
+ URL = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *) "href",
+ NULL);
+ }
+
+ } else {
+ URL = xmlStrdup(comp->filename);
+ }
+
+ if (URL == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsltDocumentElem: href/URI-Reference not found\n");
+ return;
+ }
+
+ /*
+ * If the computation failed, it's likely that the URL wasn't escaped
+ */
+ filename = xmlBuildURI(URL, (const xmlChar *) ctxt->outputFile);
+ if (filename == NULL) {
+ xmlChar *escURL;
+
+ escURL=xmlURIEscapeStr(URL, BAD_CAST ":/.?,");
+ if (escURL != NULL) {
+ filename = xmlBuildURI(escURL, (const xmlChar *) ctxt->outputFile);
+ xmlFree(escURL);
+ }
+ }
+
+ if (filename == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsltDocumentElem: URL computation failed for %s\n",
+ URL);
+ xmlFree(URL);
+ return;
+ }
+
+ /*
+ * Security checking: can we write to this resource
+ */
+ if (ctxt->sec != NULL) {
+ ret = xsltCheckWrite(ctxt->sec, ctxt, filename);
+ if (ret == 0) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsltDocumentElem: write rights for %s denied\n",
+ filename);
+ xmlFree(URL);
+ xmlFree(filename);
+ return;
+ }
+ }
+
+ oldOutputFile = ctxt->outputFile;
+ oldOutput = ctxt->output;
+ oldInsert = ctxt->insert;
+ oldType = ctxt->type;
+ ctxt->outputFile = (const char *) filename;
+
+ style = xsltNewStylesheet();
+ if (style == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsltDocumentElem: out of memory\n");
+ goto error;
+ }
+
+ /*
+ * Version described in 1.1 draft allows full parameterization
+ * of the output.
+ */
+ prop = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *) "version",
+ NULL);
+ if (prop != NULL) {
+ if (style->version != NULL)
+ xmlFree(style->version);
+ style->version = prop;
+ }
+ prop = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *) "encoding",
+ NULL);
+ if (prop != NULL) {
+ if (style->encoding != NULL)
+ xmlFree(style->encoding);
+ style->encoding = prop;
+ }
+ prop = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *) "method",
+ NULL);
+ if (prop != NULL) {
+ const xmlChar *URI;
+
+ if (style->method != NULL)
+ xmlFree(style->method);
+ style->method = NULL;
+ if (style->methodURI != NULL)
+ xmlFree(style->methodURI);
+ style->methodURI = NULL;
+
+ URI = xsltGetQNameURI(inst, &prop);
+ if (prop == NULL) {
+ if (style != NULL) style->errors++;
+ } else if (URI == NULL) {
+ if ((xmlStrEqual(prop, (const xmlChar *) "xml")) ||
+ (xmlStrEqual(prop, (const xmlChar *) "html")) ||
+ (xmlStrEqual(prop, (const xmlChar *) "text"))) {
+ style->method = prop;
+ } else {
+ xsltTransformError(ctxt, NULL, inst,
+ "invalid value for method: %s\n", prop);
+ if (style != NULL) style->warnings++;
+ }
+ } else {
+ style->method = prop;
+ style->methodURI = xmlStrdup(URI);
+ }
+ }
+ prop = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *)
+ "doctype-system", NULL);
+ if (prop != NULL) {
+ if (style->doctypeSystem != NULL)
+ xmlFree(style->doctypeSystem);
+ style->doctypeSystem = prop;
+ }
+ prop = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *)
+ "doctype-public", NULL);
+ if (prop != NULL) {
+ if (style->doctypePublic != NULL)
+ xmlFree(style->doctypePublic);
+ style->doctypePublic = prop;
+ }
+ prop = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *) "standalone",
+ NULL);
+ if (prop != NULL) {
+ if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
+ style->standalone = 1;
+ } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
+ style->standalone = 0;
+ } else {
+ xsltTransformError(ctxt, NULL, inst,
+ "invalid value for standalone: %s\n",
+ prop);
+ if (style != NULL) style->warnings++;
+ }
+ xmlFree(prop);
+ }
+
+ prop = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *) "indent",
+ NULL);
+ if (prop != NULL) {
+ if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
+ style->indent = 1;
+ } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
+ style->indent = 0;
+ } else {
+ xsltTransformError(ctxt, NULL, inst,
+ "invalid value for indent: %s\n", prop);
+ if (style != NULL) style->warnings++;
+ }
+ xmlFree(prop);
+ }
+
+ prop = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *)
+ "omit-xml-declaration",
+ NULL);
+ if (prop != NULL) {
+ if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
+ style->omitXmlDeclaration = 1;
+ } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
+ style->omitXmlDeclaration = 0;
+ } else {
+ xsltTransformError(ctxt, NULL, inst,
+ "invalid value for omit-xml-declaration: %s\n",
+ prop);
+ if (style != NULL) style->warnings++;
+ }
+ xmlFree(prop);
+ }
+
+ elements = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *)
+ "cdata-section-elements",
+ NULL);
+ if (elements != NULL) {
+ if (style->stripSpaces == NULL)
+ style->stripSpaces = xmlHashCreate(10);
+ if (style->stripSpaces == NULL)
+ return;
+
+ element = elements;
+ while (*element != 0) {
+ while (IS_BLANK_CH(*element))
+ element++;
+ if (*element == 0)
+ break;
+ end = element;
+ while ((*end != 0) && (!IS_BLANK_CH(*end)))
+ end++;
+ element = xmlStrndup(element, end - element);
+ if (element) {
+ const xmlChar *URI;
+
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "add cdata section output element %s\n",
+ element);
+#endif
+ URI = xsltGetQNameURI(inst, &element);
+
+ xmlHashAddEntry2(style->stripSpaces, element, URI,
+ (xmlChar *) "cdata");
+ xmlFree(element);
+ }
+ element = end;
+ }
+ xmlFree(elements);
+ }
+
+ /*
+ * Create a new document tree and process the element template
+ */
+ XSLT_GET_IMPORT_PTR(method, style, method)
+ XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
+ XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
+ XSLT_GET_IMPORT_PTR(version, style, version)
+ XSLT_GET_IMPORT_PTR(encoding, style, encoding)
+
+ if ((method != NULL) &&
+ (!xmlStrEqual(method, (const xmlChar *) "xml"))) {
+ if (xmlStrEqual(method, (const xmlChar *) "html")) {
+ ctxt->type = XSLT_OUTPUT_HTML;
+ if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
+ res = htmlNewDoc(doctypeSystem, doctypePublic);
+ else {
+ if (version != NULL) {
+#ifdef XSLT_GENERATE_HTML_DOCTYPE
+ xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
+#endif
+ }
+ res = htmlNewDocNoDtD(doctypeSystem, doctypePublic);
+ }
+ if (res == NULL)
+ goto error;
+ res->dict = ctxt->dict;
+ xmlDictReference(res->dict);
+ } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsltDocumentElem: unsupported method xhtml\n");
+ ctxt->type = XSLT_OUTPUT_HTML;
+ res = htmlNewDocNoDtD(doctypeSystem, doctypePublic);
+ if (res == NULL)
+ goto error;
+ res->dict = ctxt->dict;
+ xmlDictReference(res->dict);
+ } else if (xmlStrEqual(method, (const xmlChar *) "text")) {
+ ctxt->type = XSLT_OUTPUT_TEXT;
+ res = xmlNewDoc(style->version);
+ if (res == NULL)
+ goto error;
+ res->dict = ctxt->dict;
+ xmlDictReference(res->dict);
+#ifdef WITH_XSLT_DEBUG
+ xsltGenericDebug(xsltGenericDebugContext,
+ "reusing transformation dict for output\n");
+#endif
+ } else {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsltDocumentElem: unsupported method (%s)\n",
+ method);
+ goto error;
+ }
+ } else {
+ ctxt->type = XSLT_OUTPUT_XML;
+ res = xmlNewDoc(style->version);
+ if (res == NULL)
+ goto error;
+ res->dict = ctxt->dict;
+ xmlDictReference(res->dict);
+#ifdef WITH_XSLT_DEBUG
+ xsltGenericDebug(xsltGenericDebugContext,
+ "reusing transformation dict for output\n");
+#endif
+ }
+ res->charset = XML_CHAR_ENCODING_UTF8;
+ if (encoding != NULL)
+ res->encoding = xmlStrdup(encoding);
+ ctxt->output = res;
+ ctxt->insert = (xmlNodePtr) res;
+ xsltApplySequenceConstructor(ctxt, node, inst->children, NULL);
+
+ /*
+ * Do some post processing work depending on the generated output
+ */
+ root = xmlDocGetRootElement(res);
+ if (root != NULL) {
+ const xmlChar *doctype = NULL;
+
+ if ((root->ns != NULL) && (root->ns->prefix != NULL))
+ doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name);
+ if (doctype == NULL)
+ doctype = root->name;
+
+ /*
+ * Apply the default selection of the method
+ */
+ if ((method == NULL) &&
+ (root->ns == NULL) &&
+ (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) {
+ xmlNodePtr tmp;
+
+ tmp = res->children;
+ while ((tmp != NULL) && (tmp != root)) {
+ if (tmp->type == XML_ELEMENT_NODE)
+ break;
+ if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp)))
+ break;
+ tmp = tmp->next;
+ }
+ if (tmp == root) {
+ ctxt->type = XSLT_OUTPUT_HTML;
+ res->type = XML_HTML_DOCUMENT_NODE;
+ if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
+ res->intSubset = xmlCreateIntSubset(res, doctype,
+ doctypePublic,
+ doctypeSystem);
+#ifdef XSLT_GENERATE_HTML_DOCTYPE
+ } else if (version != NULL) {
+ xsltGetHTMLIDs(version, &doctypePublic,
+ &doctypeSystem);
+ if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
+ res->intSubset =
+ xmlCreateIntSubset(res, doctype,
+ doctypePublic,
+ doctypeSystem);
+#endif
+ }
+ }
+
+ }
+ if (ctxt->type == XSLT_OUTPUT_XML) {
+ XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
+ XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
+ if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
+ res->intSubset = xmlCreateIntSubset(res, doctype,
+ doctypePublic,
+ doctypeSystem);
+ }
+ }
+
+ /*
+ * Calls to redirect:write also take an optional attribute append.
+ * Attribute append="true|yes" which will attempt to simply append
+ * to an existing file instead of always opening a new file. The
+ * default behavior of always overwriting the file still happens
+ * if we do not specify append.
+ * Note that append use will forbid use of remote URI target.
+ */
+ prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"append",
+ NULL);
+ if (prop != NULL) {
+ if (xmlStrEqual(prop, (const xmlChar *) "true") ||
+ xmlStrEqual(prop, (const xmlChar *) "yes")) {
+ style->omitXmlDeclaration = 1;
+ redirect_write_append = 1;
+ } else
+ style->omitXmlDeclaration = 0;
+ xmlFree(prop);
+ }
+
+ if (redirect_write_append) {
+ FILE *f;
+
+ f = fopen((const char *) filename, "ab");
+ if (f == NULL) {
+ ret = -1;
+ } else {
+ ret = xsltSaveResultToFile(f, res, style);
+ fclose(f);
+ }
+ } else {
+ ret = xsltSaveResultToFilename((const char *) filename, res, style, 0);
+ }
+ if (ret < 0) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsltDocumentElem: unable to save to %s\n",
+ filename);
+#ifdef WITH_XSLT_DEBUG_EXTRA
+ } else {
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Wrote %d bytes to %s\n", ret, filename);
+#endif
+ }
+
+ error:
+ ctxt->output = oldOutput;
+ ctxt->insert = oldInsert;
+ ctxt->type = oldType;
+ ctxt->outputFile = oldOutputFile;
+ if (URL != NULL)
+ xmlFree(URL);
+ if (filename != NULL)
+ xmlFree(filename);
+ if (style != NULL)
+ xsltFreeStylesheet(style);
+ if (res != NULL)
+ xmlFreeDoc(res);
+}
+
+/************************************************************************
+ * *
+ * Most of the XSLT-1.0 transformations *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltSort:
+ * @ctxt: a XSLT process context
+ * @node: the node in the source tree.
+ * @inst: the xslt sort node
+ * @comp: precomputed information
+ *
+ * function attached to xsl:sort nodes, but this should not be
+ * called directly
+ */
+void
+xsltSort(xsltTransformContextPtr ctxt,
+ xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst,
+ xsltStylePreCompPtr comp) {
+ if (comp == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:sort : compilation failed\n");
+ return;
+ }
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:sort : improper use this should not be reached\n");
+}
+
+/**
+ * xsltCopy:
+ * @ctxt: an XSLT process context
+ * @node: the node in the source tree
+ * @inst: the element node of the XSLT-copy instruction
+ * @castedComp: computed information of the XSLT-copy instruction
+ *
+ * Execute the XSLT-copy instruction on the source node.
+ */
+void
+xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr inst, xsltStylePreCompPtr castedComp)
+{
+#ifdef XSLT_REFACTORED
+ xsltStyleItemCopyPtr comp = (xsltStyleItemCopyPtr) castedComp;
+#else
+ xsltStylePreCompPtr comp = castedComp;
+#endif
+ xmlNodePtr copy, oldInsert;
+
+ oldInsert = ctxt->insert;
+ if (ctxt->insert != NULL) {
+ switch (node->type) {
+ case XML_TEXT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ /*
+ * This text comes from the stylesheet
+ * For stylesheets, the set of whitespace-preserving
+ * element names consists of just xsl:text.
+ */
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if (node->type == XML_CDATA_SECTION_NODE) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCopy: CDATA text %s\n", node->content));
+ } else {
+ XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCopy: text %s\n", node->content));
+ }
+#endif
+ xsltCopyText(ctxt, ctxt->insert, node, 0);
+ break;
+ case XML_DOCUMENT_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+ break;
+ case XML_ELEMENT_NODE:
+ /*
+ * REVISIT NOTE: The "fake" is a doc-node, not an element node.
+ * REMOVED:
+ * if (xmlStrEqual(node->name, BAD_CAST " fake node libxslt"))
+ * return;
+ */
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCopy: node %s\n", node->name));
+#endif
+ copy = xsltShallowCopyElem(ctxt, node, ctxt->insert, 0);
+ ctxt->insert = copy;
+ if (comp->use != NULL) {
+ xsltApplyAttributeSet(ctxt, node, inst, comp->use);
+ }
+ break;
+ case XML_ATTRIBUTE_NODE: {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCopy: attribute %s\n", node->name));
+#endif
+ /*
+ * REVISIT: We could also raise an error if the parent is not
+ * an element node.
+ * OPTIMIZE TODO: Can we set the value/children of the
+ * attribute without an intermediate copy of the string value?
+ */
+ xsltShallowCopyAttr(ctxt, inst, ctxt->insert, (xmlAttrPtr) node);
+ break;
+ }
+ case XML_PI_NODE:
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCopy: PI %s\n", node->name));
+#endif
+ copy = xmlNewDocPI(ctxt->insert->doc, node->name,
+ node->content);
+ copy = xsltAddChild(ctxt->insert, copy);
+ break;
+ case XML_COMMENT_NODE:
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCopy: comment\n"));
+#endif
+ copy = xmlNewComment(node->content);
+ copy = xsltAddChild(ctxt->insert, copy);
+ break;
+ case XML_NAMESPACE_DECL:
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCopy: namespace declaration\n"));
+#endif
+ xsltShallowCopyNsNode(ctxt, inst, ctxt->insert, (xmlNsPtr)node);
+ break;
+ default:
+ break;
+
+ }
+ }
+
+ switch (node->type) {
+ case XML_DOCUMENT_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+ case XML_ELEMENT_NODE:
+ xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children,
+ NULL);
+ break;
+ default:
+ break;
+ }
+ ctxt->insert = oldInsert;
+}
+
+/**
+ * xsltText:
+ * @ctxt: a XSLT process context
+ * @node: the node in the source tree.
+ * @inst: the xslt text node
+ * @comp: precomputed information
+ *
+ * Process the xslt text node on the source node
+ */
+void
+xsltText(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED,
+ xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
+ if ((inst->children != NULL) && (comp != NULL)) {
+ xmlNodePtr text = inst->children;
+ xmlNodePtr copy;
+
+ while (text != NULL) {
+ if ((text->type != XML_TEXT_NODE) &&
+ (text->type != XML_CDATA_SECTION_NODE)) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:text content problem\n");
+ break;
+ }
+ copy = xmlNewDocText(ctxt->output, text->content);
+ if (text->type != XML_CDATA_SECTION_NODE) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Disable escaping: %s\n", text->content);
+#endif
+ copy->name = xmlStringTextNoenc;
+ }
+ copy = xsltAddChild(ctxt->insert, copy);
+ text = text->next;
+ }
+ }
+}
+
+/**
+ * xsltElement:
+ * @ctxt: a XSLT process context
+ * @node: the node in the source tree.
+ * @inst: the xslt element node
+ * @castedComp: precomputed information
+ *
+ * Process the xslt element node on the source node
+ */
+void
+xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr inst, xsltStylePreCompPtr castedComp) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemElementPtr comp = (xsltStyleItemElementPtr) castedComp;
+#else
+ xsltStylePreCompPtr comp = castedComp;
+#endif
+ xmlChar *prop = NULL;
+ const xmlChar *name, *prefix = NULL, *nsName = NULL;
+ xmlNodePtr copy;
+ xmlNodePtr oldInsert;
+
+ if (ctxt->insert == NULL)
+ return;
+
+ /*
+ * A comp->has_name == 0 indicates that we need to skip this instruction,
+ * since it was evaluated to be invalid already during compilation.
+ */
+ if (!comp->has_name)
+ return;
+
+ /*
+ * stack and saves
+ */
+ oldInsert = ctxt->insert;
+
+ if (comp->name == NULL) {
+ /* TODO: fix attr acquisition wrt to the XSLT namespace */
+ prop = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *) "name", XSLT_NAMESPACE);
+ if (prop == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:element: The attribute 'name' is missing.\n");
+ goto error;
+ }
+ if (xmlValidateQName(prop, 0)) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:element: The effective name '%s' is not a "
+ "valid QName.\n", prop);
+ /* we fall through to catch any further errors, if possible */
+ }
+ name = xsltSplitQName(ctxt->dict, prop, &prefix);
+ xmlFree(prop);
+ } else {
+ /*
+ * The "name" value was static.
+ */
+#ifdef XSLT_REFACTORED
+ prefix = comp->nsPrefix;
+ name = comp->name;
+#else
+ name = xsltSplitQName(ctxt->dict, comp->name, &prefix);
+#endif
+ }
+
+ /*
+ * Create the new element
+ */
+ if (ctxt->output->dict == ctxt->dict) {
+ copy = xmlNewDocNodeEatName(ctxt->output, NULL, (xmlChar *)name, NULL);
+ } else {
+ copy = xmlNewDocNode(ctxt->output, NULL, (xmlChar *)name, NULL);
+ }
+ if (copy == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:element : creation of %s failed\n", name);
+ return;
+ }
+ copy = xsltAddChild(ctxt->insert, copy);
+ if (copy == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:element : xsltAddChild failed\n");
+ return;
+ }
+
+ /*
+ * Namespace
+ * ---------
+ */
+ if (comp->has_ns) {
+ if (comp->ns != NULL) {
+ /*
+ * No AVT; just plain text for the namespace name.
+ */
+ if (comp->ns[0] != 0)
+ nsName = comp->ns;
+ } else {
+ xmlChar *tmpNsName;
+ /*
+ * Eval the AVT.
+ */
+ /* TODO: check attr acquisition wrt to the XSLT namespace */
+ tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *) "namespace", XSLT_NAMESPACE);
+ /*
+ * SPEC XSLT 1.0:
+ * "If the string is empty, then the expanded-name of the
+ * attribute has a null namespace URI."
+ */
+ if ((tmpNsName != NULL) && (tmpNsName[0] != 0))
+ nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1);
+ xmlFree(tmpNsName);
+ }
+
+ if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ "
+ "forbidden.\n");
+ goto error;
+ }
+ if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) {
+ prefix = BAD_CAST "xml";
+ } else if (xmlStrEqual(prefix, BAD_CAST "xml")) {
+ prefix = NULL;
+ }
+ } else {
+ xmlNsPtr ns;
+ /*
+ * SPEC XSLT 1.0:
+ * "If the namespace attribute is not present, then the QName is
+ * expanded into an expanded-name using the namespace declarations
+ * in effect for the xsl:element element, including any default
+ * namespace declaration.
+ */
+ ns = xmlSearchNs(inst->doc, inst, prefix);
+ if (ns == NULL) {
+ /*
+ * TODO: Check this in the compilation layer in case it's a
+ * static value.
+ */
+ if (prefix != NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:element: The QName '%s:%s' has no "
+ "namespace binding in scope in the stylesheet; "
+ "this is an error, since the namespace was not "
+ "specified by the instruction itself.\n", prefix, name);
+ }
+ } else
+ nsName = ns->href;
+ }
+ /*
+ * Find/create a matching ns-decl in the result tree.
+ */
+ if (nsName != NULL) {
+ if (xmlStrEqual(prefix, BAD_CAST "xmlns")) {
+ /* Don't use a prefix of "xmlns" */
+ xmlChar *pref = xmlStrdup(BAD_CAST "ns_1");
+
+ copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, copy);
+
+ xmlFree(pref);
+ } else {
+ copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix,
+ copy);
+ }
+ } else if ((copy->parent != NULL) &&
+ (copy->parent->type == XML_ELEMENT_NODE) &&
+ (copy->parent->ns != NULL))
+ {
+ /*
+ * "Undeclare" the default namespace.
+ */
+ xsltGetSpecialNamespace(ctxt, inst, NULL, NULL, copy);
+ }
+
+ ctxt->insert = copy;
+
+ if (comp->has_use) {
+ if (comp->use != NULL) {
+ xsltApplyAttributeSet(ctxt, node, inst, comp->use);
+ } else {
+ xmlChar *attrSets = NULL;
+ /*
+ * BUG TODO: use-attribute-sets is not a value template.
+ * use-attribute-sets = qnames
+ */
+ attrSets = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *)"use-attribute-sets", NULL);
+ if (attrSets != NULL) {
+ xsltApplyAttributeSet(ctxt, node, inst, attrSets);
+ xmlFree(attrSets);
+ }
+ }
+ }
+ /*
+ * Instantiate the sequence constructor.
+ */
+ if (inst->children != NULL)
+ xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children,
+ NULL);
+
+error:
+ ctxt->insert = oldInsert;
+ return;
+}
+
+
+/**
+ * xsltComment:
+ * @ctxt: a XSLT process context
+ * @node: the node in the source tree.
+ * @inst: the xslt comment node
+ * @comp: precomputed information
+ *
+ * Process the xslt comment node on the source node
+ */
+void
+xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
+ xmlChar *value = NULL;
+ xmlNodePtr commentNode;
+ int len;
+
+ value = xsltEvalTemplateString(ctxt, node, inst);
+ /* TODO: use or generate the compiled form */
+ len = xmlStrlen(value);
+ if (len > 0) {
+ if ((value[len-1] == '-') ||
+ (xmlStrstr(value, BAD_CAST "--"))) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:comment : '--' or ending '-' not allowed in comment\n");
+ /* fall through to try to catch further errors */
+ }
+ }
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if (value == NULL) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltComment: empty\n"));
+ } else {
+ XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltComment: content %s\n", value));
+ }
+#endif
+
+ commentNode = xmlNewComment(value);
+ commentNode = xsltAddChild(ctxt->insert, commentNode);
+
+ if (value != NULL)
+ xmlFree(value);
+}
+
+/**
+ * xsltProcessingInstruction:
+ * @ctxt: a XSLT process context
+ * @node: the node in the source tree.
+ * @inst: the xslt processing-instruction node
+ * @castedComp: precomputed information
+ *
+ * Process the xslt processing-instruction node on the source node
+ */
+void
+xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr inst, xsltStylePreCompPtr castedComp) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemPIPtr comp = (xsltStyleItemPIPtr) castedComp;
+#else
+ xsltStylePreCompPtr comp = castedComp;
+#endif
+ const xmlChar *name;
+ xmlChar *value = NULL;
+ xmlNodePtr pi;
+
+
+ if (ctxt->insert == NULL)
+ return;
+ if (comp->has_name == 0)
+ return;
+ if (comp->name == NULL) {
+ name = xsltEvalAttrValueTemplate(ctxt, inst,
+ (const xmlChar *)"name", NULL);
+ if (name == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:processing-instruction : name is missing\n");
+ goto error;
+ }
+ } else {
+ name = comp->name;
+ }
+ /* TODO: check that it's both an an NCName and a PITarget. */
+
+
+ value = xsltEvalTemplateString(ctxt, node, inst);
+ if (xmlStrstr(value, BAD_CAST "?>") != NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:processing-instruction: '?>' not allowed within PI content\n");
+ goto error;
+ }
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if (value == NULL) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltProcessingInstruction: %s empty\n", name));
+ } else {
+ XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltProcessingInstruction: %s content %s\n", name, value));
+ }
+#endif
+
+ pi = xmlNewDocPI(ctxt->insert->doc, name, value);
+ pi = xsltAddChild(ctxt->insert, pi);
+
+error:
+ if ((name != NULL) && (name != comp->name))
+ xmlFree((xmlChar *) name);
+ if (value != NULL)
+ xmlFree(value);
+}
+
+/**
+ * xsltCopyOf:
+ * @ctxt: an XSLT transformation context
+ * @node: the current node in the source tree
+ * @inst: the element node of the XSLT copy-of instruction
+ * @castedComp: precomputed information of the XSLT copy-of instruction
+ *
+ * Process the XSLT copy-of instruction.
+ */
+void
+xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr inst, xsltStylePreCompPtr castedComp) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemCopyOfPtr comp = (xsltStyleItemCopyOfPtr) castedComp;
+#else
+ xsltStylePreCompPtr comp = castedComp;
+#endif
+ xmlXPathObjectPtr res = NULL;
+ xmlNodeSetPtr list = NULL;
+ int i;
+
+ if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
+ return;
+ if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:copy-of : compilation failed\n");
+ return;
+ }
+
+ /*
+ * SPEC XSLT 1.0:
+ * "The xsl:copy-of element can be used to insert a result tree
+ * fragment into the result tree, without first converting it to
+ * a string as xsl:value-of does (see [7.6.1 Generating Text with
+ * xsl:value-of]). The required select attribute contains an
+ * expression. When the result of evaluating the expression is a
+ * result tree fragment, the complete fragment is copied into the
+ * result tree. When the result is a node-set, all the nodes in the
+ * set are copied in document order into the result tree; copying
+ * an element node copies the attribute nodes, namespace nodes and
+ * children of the element node as well as the element node itself;
+ * a root node is copied by copying its children. When the result
+ * is neither a node-set nor a result tree fragment, the result is
+ * converted to a string and then inserted into the result tree,
+ * as with xsl:value-of.
+ */
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCopyOf: select %s\n", comp->select));
+#endif
+
+ /*
+ * Evaluate the "select" expression.
+ */
+ res = xsltPreCompEval(ctxt, node, comp);
+
+ if (res != NULL) {
+ if (res->type == XPATH_NODESET) {
+ /*
+ * Node-set
+ * --------
+ */
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCopyOf: result is a node set\n"));
+#endif
+ list = res->nodesetval;
+ if (list != NULL) {
+ xmlNodePtr cur;
+ /*
+ * The list is already sorted in document order by XPath.
+ * Append everything in this order under ctxt->insert.
+ */
+ for (i = 0;i < list->nodeNr;i++) {
+ cur = list->nodeTab[i];
+ if (cur == NULL)
+ continue;
+ if ((cur->type == XML_DOCUMENT_NODE) ||
+ (cur->type == XML_HTML_DOCUMENT_NODE))
+ {
+ xsltCopyTreeList(ctxt, inst,
+ cur->children, ctxt->insert, 0, 0);
+ } else if (cur->type == XML_ATTRIBUTE_NODE) {
+ xsltShallowCopyAttr(ctxt, inst,
+ ctxt->insert, (xmlAttrPtr) cur);
+ } else {
+ xsltCopyTreeInternal(ctxt, inst,
+ cur, ctxt->insert, 0, 0);
+ }
+ }
+ }
+ } else if (res->type == XPATH_XSLT_TREE) {
+ /*
+ * Result tree fragment
+ * --------------------
+ * E.g. via <xsl:variable ...><foo/></xsl:variable>
+ * Note that the root node of such trees is an xmlDocPtr in Libxslt.
+ */
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCopyOf: result is a result tree fragment\n"));
+#endif
+ list = res->nodesetval;
+ if ((list != NULL) && (list->nodeTab != NULL) &&
+ (list->nodeTab[0] != NULL) &&
+ (IS_XSLT_REAL_NODE(list->nodeTab[0])))
+ {
+ xsltCopyTreeList(ctxt, inst,
+ list->nodeTab[0]->children, ctxt->insert, 0, 0);
+ }
+ } else {
+ xmlChar *value = NULL;
+ /*
+ * Convert to a string.
+ */
+ value = xmlXPathCastToString(res);
+ if (value == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "Internal error in xsltCopyOf(): "
+ "failed to cast an XPath object to string.\n");
+ ctxt->state = XSLT_STATE_STOPPED;
+ } else {
+ if (value[0] != 0) {
+ /*
+ * Append content as text node.
+ */
+ xsltCopyTextString(ctxt, ctxt->insert, value, 0);
+ }
+ xmlFree(value);
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltCopyOf: result %s\n", res->stringval));
+#endif
+ }
+ }
+ } else {
+ ctxt->state = XSLT_STATE_STOPPED;
+ }
+
+ if (res != NULL)
+ xmlXPathFreeObject(res);
+}
+
+/**
+ * xsltValueOf:
+ * @ctxt: a XSLT process context
+ * @node: the node in the source tree.
+ * @inst: the xslt value-of node
+ * @castedComp: precomputed information
+ *
+ * Process the xslt value-of node on the source node
+ */
+void
+xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr inst, xsltStylePreCompPtr castedComp)
+{
+#ifdef XSLT_REFACTORED
+ xsltStyleItemValueOfPtr comp = (xsltStyleItemValueOfPtr) castedComp;
+#else
+ xsltStylePreCompPtr comp = castedComp;
+#endif
+ xmlXPathObjectPtr res = NULL;
+ xmlChar *value = NULL;
+
+ if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
+ return;
+
+ if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
+ xsltTransformError(ctxt, NULL, inst,
+ "Internal error in xsltValueOf(): "
+ "The XSLT 'value-of' instruction was not compiled.\n");
+ return;
+ }
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltValueOf: select %s\n", comp->select));
+#endif
+
+ res = xsltPreCompEval(ctxt, node, comp);
+
+ /*
+ * Cast the XPath object to string.
+ */
+ if (res != NULL) {
+ value = xmlXPathCastToString(res);
+ if (value == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "Internal error in xsltValueOf(): "
+ "failed to cast an XPath object to string.\n");
+ ctxt->state = XSLT_STATE_STOPPED;
+ goto error;
+ }
+ if (value[0] != 0) {
+ xsltCopyTextString(ctxt, ctxt->insert, value, comp->noescape);
+ }
+ } else {
+ xsltTransformError(ctxt, NULL, inst,
+ "XPath evaluation returned no result.\n");
+ ctxt->state = XSLT_STATE_STOPPED;
+ goto error;
+ }
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if (value) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltValueOf: result '%s'\n", value));
+ }
+#endif
+
+error:
+ if (value != NULL)
+ xmlFree(value);
+ if (res != NULL)
+ xmlXPathFreeObject(res);
+}
+
+/**
+ * xsltNumber:
+ * @ctxt: a XSLT process context
+ * @node: the node in the source tree.
+ * @inst: the xslt number node
+ * @castedComp: precomputed information
+ *
+ * Process the xslt number node on the source node
+ */
+void
+xsltNumber(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr inst, xsltStylePreCompPtr castedComp)
+{
+#ifdef XSLT_REFACTORED
+ xsltStyleItemNumberPtr comp = (xsltStyleItemNumberPtr) castedComp;
+#else
+ xsltStylePreCompPtr comp = castedComp;
+#endif
+ xmlXPathContextPtr xpctxt;
+ xmlNsPtr *oldXPNamespaces;
+ int oldXPNsNr;
+
+ if (comp == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:number : compilation failed\n");
+ return;
+ }
+
+ if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
+ return;
+
+ comp->numdata.doc = inst->doc;
+ comp->numdata.node = inst;
+
+ xpctxt = ctxt->xpathCtxt;
+ oldXPNsNr = xpctxt->nsNr;
+ oldXPNamespaces = xpctxt->namespaces;
+
+#ifdef XSLT_REFACTORED
+ if (comp->inScopeNs != NULL) {
+ xpctxt->namespaces = comp->inScopeNs->list;
+ xpctxt->nsNr = comp->inScopeNs->xpathNumber;
+ } else {
+ xpctxt->namespaces = NULL;
+ xpctxt->nsNr = 0;
+ }
+#else
+ xpctxt->namespaces = comp->nsList;
+ xpctxt->nsNr = comp->nsNr;
+#endif
+
+ xsltNumberFormat(ctxt, &comp->numdata, node);
+
+ xpctxt->nsNr = oldXPNsNr;
+ xpctxt->namespaces = oldXPNamespaces;
+}
+
+/**
+ * xsltApplyImports:
+ * @ctxt: an XSLT transformation context
+ * @contextNode: the current node in the source tree.
+ * @inst: the element node of the XSLT 'apply-imports' instruction
+ * @comp: the compiled instruction
+ *
+ * Process the XSLT apply-imports element.
+ */
+void
+xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp ATTRIBUTE_UNUSED)
+{
+ xsltTemplatePtr templ;
+
+ if ((ctxt == NULL) || (inst == NULL))
+ return;
+
+ if (comp == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "Internal error in xsltApplyImports(): "
+ "The XSLT 'apply-imports' instruction was not compiled.\n");
+ return;
+ }
+ /*
+ * NOTE that ctxt->currentTemplateRule and ctxt->templ is not the
+ * same; the former is the "Current Template Rule" as defined by the
+ * XSLT spec, the latter is simply the template struct being
+ * currently processed.
+ */
+ if (ctxt->currentTemplateRule == NULL) {
+ /*
+ * SPEC XSLT 2.0:
+ * "[ERR XTDE0560] It is a non-recoverable dynamic error if
+ * xsl:apply-imports or xsl:next-match is evaluated when the
+ * current template rule is null."
+ */
+ xsltTransformError(ctxt, NULL, inst,
+ "It is an error to call 'apply-imports' "
+ "when there's no current template rule.\n");
+ return;
+ }
+ /*
+ * TODO: Check if this is correct.
+ */
+ templ = xsltGetTemplate(ctxt, contextNode,
+ ctxt->currentTemplateRule->style);
+
+ if (templ != NULL) {
+ xsltTemplatePtr oldCurTemplRule = ctxt->currentTemplateRule;
+ /*
+ * Set the current template rule.
+ */
+ ctxt->currentTemplateRule = templ;
+ /*
+ * URGENT TODO: Need xsl:with-param be handled somehow here?
+ */
+ xsltApplyXSLTTemplate(ctxt, contextNode, templ->content,
+ templ, NULL);
+
+ ctxt->currentTemplateRule = oldCurTemplRule;
+ }
+ else {
+ /* Use built-in templates. */
+ xsltDefaultProcessOneNode(ctxt, contextNode, NULL);
+ }
+}
+
+/**
+ * xsltCallTemplate:
+ * @ctxt: a XSLT transformation context
+ * @node: the "current node" in the source tree
+ * @inst: the XSLT 'call-template' instruction
+ * @castedComp: the compiled information of the instruction
+ *
+ * Processes the XSLT call-template instruction on the source node.
+ */
+void
+xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr inst, xsltStylePreCompPtr castedComp)
+{
+#ifdef XSLT_REFACTORED
+ xsltStyleItemCallTemplatePtr comp =
+ (xsltStyleItemCallTemplatePtr) castedComp;
+#else
+ xsltStylePreCompPtr comp = castedComp;
+#endif
+ xsltStackElemPtr withParams = NULL;
+
+ if (ctxt->insert == NULL)
+ return;
+ if (comp == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "The XSLT 'call-template' instruction was not compiled.\n");
+ return;
+ }
+
+ /*
+ * The template must have been precomputed
+ */
+ if (comp->templ == NULL) {
+ comp->templ = xsltFindTemplate(ctxt, comp->name, comp->ns);
+ if (comp->templ == NULL) {
+ if (comp->ns != NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "The called template '{%s}%s' was not found.\n",
+ comp->ns, comp->name);
+ } else {
+ xsltTransformError(ctxt, NULL, inst,
+ "The called template '%s' was not found.\n",
+ comp->name);
+ }
+ return;
+ }
+ }
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if ((comp != NULL) && (comp->name != NULL))
+ XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
+ "call-template: name %s\n", comp->name));
+#endif
+
+ if (inst->children) {
+ xmlNodePtr cur;
+ xsltStackElemPtr param;
+
+ cur = inst->children;
+ while (cur != NULL) {
+#ifdef WITH_DEBUGGER
+ if (ctxt->debugStatus != XSLT_DEBUG_NONE)
+ xslHandleDebugger(cur, node, comp->templ, ctxt);
+#endif
+ if (ctxt->state == XSLT_STATE_STOPPED) break;
+ /*
+ * TODO: The "with-param"s could be part of the "call-template"
+ * structure. Avoid to "search" for params dynamically
+ * in the XML tree every time.
+ */
+ if (IS_XSLT_ELEM(cur)) {
+ if (IS_XSLT_NAME(cur, "with-param")) {
+ param = xsltParseStylesheetCallerParam(ctxt, cur);
+ if (param != NULL) {
+ param->next = withParams;
+ withParams = param;
+ }
+ } else {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsl:call-template: misplaced xsl:%s\n", cur->name);
+ }
+ } else {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsl:call-template: misplaced %s element\n", cur->name);
+ }
+ cur = cur->next;
+ }
+ }
+ /*
+ * Create a new frame using the params first
+ */
+ xsltApplyXSLTTemplate(ctxt, node, comp->templ->content, comp->templ,
+ withParams);
+ if (withParams != NULL)
+ xsltFreeStackElemList(withParams);
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if ((comp != NULL) && (comp->name != NULL))
+ XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
+ "call-template returned: name %s\n", comp->name));
+#endif
+}
+
+/**
+ * xsltApplyTemplates:
+ * @ctxt: a XSLT transformation context
+ * @node: the 'current node' in the source tree
+ * @inst: the element node of an XSLT 'apply-templates' instruction
+ * @castedComp: the compiled instruction
+ *
+ * Processes the XSLT 'apply-templates' instruction on the current node.
+ */
+void
+xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node,
+ xmlNodePtr inst, xsltStylePreCompPtr castedComp)
+{
+#ifdef XSLT_REFACTORED
+ xsltStyleItemApplyTemplatesPtr comp =
+ (xsltStyleItemApplyTemplatesPtr) castedComp;
+#else
+ xsltStylePreCompPtr comp = castedComp;
+#endif
+ int i;
+ xmlNodePtr cur, delNode = NULL, oldContextNode;
+ xmlNodeSetPtr list = NULL, oldList;
+ xsltStackElemPtr withParams = NULL;
+ int oldXPProximityPosition, oldXPContextSize;
+ const xmlChar *oldMode, *oldModeURI;
+ xmlDocPtr oldXPDoc;
+ xsltDocumentPtr oldDocInfo;
+ xmlXPathContextPtr xpctxt;
+
+ if (comp == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:apply-templates : compilation failed\n");
+ return;
+ }
+ if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
+ return;
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if ((node != NULL) && (node->name != NULL))
+ XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplyTemplates: node: '%s'\n", node->name));
+#endif
+
+ xpctxt = ctxt->xpathCtxt;
+ /*
+ * Save context states.
+ */
+ oldContextNode = ctxt->node;
+ oldMode = ctxt->mode;
+ oldModeURI = ctxt->modeURI;
+ oldDocInfo = ctxt->document;
+ oldList = ctxt->nodeList;
+
+ /*
+ * The xpath context size and proximity position, as
+ * well as the xpath and context documents, may be changed
+ * so we save their initial state and will restore on exit
+ */
+ oldXPContextSize = xpctxt->contextSize;
+ oldXPProximityPosition = xpctxt->proximityPosition;
+ oldXPDoc = xpctxt->doc;
+
+ /*
+ * Set up contexts.
+ */
+ ctxt->mode = comp->mode;
+ ctxt->modeURI = comp->modeURI;
+
+ if (comp->select != NULL) {
+ xmlXPathObjectPtr res = NULL;
+
+ if (comp->comp == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:apply-templates : compilation failed\n");
+ goto error;
+ }
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplyTemplates: select %s\n", comp->select));
+#endif
+
+ res = xsltPreCompEval(ctxt, node, comp);
+
+ if (res != NULL) {
+ if (res->type == XPATH_NODESET) {
+ list = res->nodesetval; /* consume the node set */
+ res->nodesetval = NULL;
+ } else {
+ xsltTransformError(ctxt, NULL, inst,
+ "The 'select' expression did not evaluate to a "
+ "node set.\n");
+ ctxt->state = XSLT_STATE_STOPPED;
+ xmlXPathFreeObject(res);
+ goto error;
+ }
+ xmlXPathFreeObject(res);
+ /*
+ * Note: An xsl:apply-templates with a 'select' attribute,
+ * can change the current source doc.
+ */
+ } else {
+ xsltTransformError(ctxt, NULL, inst,
+ "Failed to evaluate the 'select' expression.\n");
+ ctxt->state = XSLT_STATE_STOPPED;
+ goto error;
+ }
+ if (list == NULL) {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplyTemplates: select didn't evaluate to a node list\n"));
+#endif
+ goto exit;
+ }
+ /*
+ *
+ * NOTE: Previously a document info (xsltDocument) was
+ * created and attached to the Result Tree Fragment.
+ * But such a document info is created on demand in
+ * xsltKeyFunction() (functions.c), so we need to create
+ * it here beforehand.
+ * In order to take care of potential keys we need to
+ * do some extra work for the case when a Result Tree Fragment
+ * is converted into a nodeset (e.g. exslt:node-set()) :
+ * We attach a "pseudo-doc" (xsltDocument) to _private.
+ * This xsltDocument, together with the keyset, will be freed
+ * when the Result Tree Fragment is freed.
+ *
+ */
+#if 0
+ if ((ctxt->nbKeys > 0) &&
+ (list->nodeNr != 0) &&
+ (list->nodeTab[0]->doc != NULL) &&
+ XSLT_IS_RES_TREE_FRAG(list->nodeTab[0]->doc))
+ {
+ /*
+ * NOTE that it's also OK if @effectiveDocInfo will be
+ * set to NULL.
+ */
+ isRTF = 1;
+ effectiveDocInfo = list->nodeTab[0]->doc->_private;
+ }
+#endif
+ } else {
+ /*
+ * Build an XPath node set with the children
+ */
+ list = xmlXPathNodeSetCreate(NULL);
+ if (list == NULL)
+ goto error;
+ if (node->type != XML_NAMESPACE_DECL)
+ cur = node->children;
+ else
+ cur = NULL;
+ while (cur != NULL) {
+ switch (cur->type) {
+ case XML_TEXT_NODE:
+ if ((IS_BLANK_NODE(cur)) &&
+ (cur->parent != NULL) &&
+ (cur->parent->type == XML_ELEMENT_NODE) &&
+ (ctxt->style->stripSpaces != NULL)) {
+ const xmlChar *val;
+
+ if (cur->parent->ns != NULL) {
+ val = (const xmlChar *)
+ xmlHashLookup2(ctxt->style->stripSpaces,
+ cur->parent->name,
+ cur->parent->ns->href);
+ if (val == NULL) {
+ val = (const xmlChar *)
+ xmlHashLookup2(ctxt->style->stripSpaces,
+ BAD_CAST "*",
+ cur->parent->ns->href);
+ }
+ } else {
+ val = (const xmlChar *)
+ xmlHashLookup2(ctxt->style->stripSpaces,
+ cur->parent->name, NULL);
+ }
+ if ((val != NULL) &&
+ (xmlStrEqual(val, (xmlChar *) "strip"))) {
+ delNode = cur;
+ break;
+ }
+ }
+ /* no break on purpose */
+ case XML_ELEMENT_NODE:
+ case XML_DOCUMENT_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_PI_NODE:
+ case XML_COMMENT_NODE:
+ xmlXPathNodeSetAddUnique(list, cur);
+ break;
+ case XML_DTD_NODE:
+ /* Unlink the DTD, it's still reachable
+ * using doc->intSubset */
+ if (cur->next != NULL)
+ cur->next->prev = cur->prev;
+ if (cur->prev != NULL)
+ cur->prev->next = cur->next;
+ break;
+ case XML_NAMESPACE_DECL:
+ break;
+ default:
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplyTemplates: skipping cur type %d\n",
+ cur->type));
+#endif
+ delNode = cur;
+ }
+ cur = cur->next;
+ if (delNode != NULL) {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplyTemplates: removing ignorable blank cur\n"));
+#endif
+ xmlUnlinkNode(delNode);
+ xmlFreeNode(delNode);
+ delNode = NULL;
+ }
+ }
+ }
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ if (list != NULL)
+ XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplyTemplates: list of %d nodes\n", list->nodeNr));
+#endif
+
+ if ((list == NULL) || (list->nodeNr == 0))
+ goto exit;
+
+ /*
+ * Set the context's node set and size; this is also needed for
+ * for xsltDoSortFunction().
+ */
+ ctxt->nodeList = list;
+ /*
+ * Process xsl:with-param and xsl:sort instructions.
+ * (The code became so verbose just to avoid the
+ * xmlNodePtr sorts[XSLT_MAX_SORT] if there's no xsl:sort)
+ * BUG TODO: We are not using namespaced potentially defined on the
+ * xsl:sort or xsl:with-param elements; XPath expression might fail.
+ */
+ if (inst->children) {
+ xsltStackElemPtr param;
+
+ cur = inst->children;
+ while (cur) {
+
+#ifdef WITH_DEBUGGER
+ if (ctxt->debugStatus != XSLT_DEBUG_NONE)
+ xslHandleDebugger(cur, node, NULL, ctxt);
+#endif
+ if (ctxt->state == XSLT_STATE_STOPPED)
+ break;
+ if (cur->type == XML_TEXT_NODE) {
+ cur = cur->next;
+ continue;
+ }
+ if (! IS_XSLT_ELEM(cur))
+ break;
+ if (IS_XSLT_NAME(cur, "with-param")) {
+ param = xsltParseStylesheetCallerParam(ctxt, cur);
+ if (param != NULL) {
+ param->next = withParams;
+ withParams = param;
+ }
+ }
+ if (IS_XSLT_NAME(cur, "sort")) {
+ xsltTemplatePtr oldCurTempRule =
+ ctxt->currentTemplateRule;
+ int nbsorts = 0;
+ xmlNodePtr sorts[XSLT_MAX_SORT];
+
+ sorts[nbsorts++] = cur;
+
+ while (cur) {
+
+#ifdef WITH_DEBUGGER
+ if (ctxt->debugStatus != XSLT_DEBUG_NONE)
+ xslHandleDebugger(cur, node, NULL, ctxt);
+#endif
+ if (ctxt->state == XSLT_STATE_STOPPED)
+ break;
+
+ if (cur->type == XML_TEXT_NODE) {
+ cur = cur->next;
+ continue;
+ }
+
+ if (! IS_XSLT_ELEM(cur))
+ break;
+ if (IS_XSLT_NAME(cur, "with-param")) {
+ param = xsltParseStylesheetCallerParam(ctxt, cur);
+ if (param != NULL) {
+ param->next = withParams;
+ withParams = param;
+ }
+ }
+ if (IS_XSLT_NAME(cur, "sort")) {
+ if (nbsorts >= XSLT_MAX_SORT) {
+ xsltTransformError(ctxt, NULL, cur,
+ "The number (%d) of xsl:sort instructions exceeds the "
+ "maximum allowed by this processor's settings.\n",
+ nbsorts);
+ ctxt->state = XSLT_STATE_STOPPED;
+ break;
+ } else {
+ sorts[nbsorts++] = cur;
+ }
+ }
+ cur = cur->next;
+ }
+ /*
+ * The "current template rule" is cleared for xsl:sort.
+ */
+ ctxt->currentTemplateRule = NULL;
+ /*
+ * Sort.
+ */
+ xsltDoSortFunction(ctxt, sorts, nbsorts);
+ ctxt->currentTemplateRule = oldCurTempRule;
+ break;
+ }
+ cur = cur->next;
+ }
+ }
+ xpctxt->contextSize = list->nodeNr;
+ /*
+ * Apply templates for all selected source nodes.
+ */
+ for (i = 0; i < list->nodeNr; i++) {
+ cur = list->nodeTab[i];
+ /*
+ * The node becomes the "current node".
+ */
+ ctxt->node = cur;
+ /*
+ * An xsl:apply-templates can change the current context doc.
+ * OPTIMIZE TODO: Get rid of the need to set the context doc.
+ */
+ if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL))
+ xpctxt->doc = cur->doc;
+
+ xpctxt->proximityPosition = i + 1;
+ /*
+ * Find and apply a template for this node.
+ */
+ xsltProcessOneNode(ctxt, cur, withParams);
+ }
+
+exit:
+error:
+ /*
+ * Free the parameter list.
+ */
+ if (withParams != NULL)
+ xsltFreeStackElemList(withParams);
+ if (list != NULL)
+ xmlXPathFreeNodeSet(list);
+ /*
+ * Restore context states.
+ */
+ xpctxt->doc = oldXPDoc;
+ xpctxt->contextSize = oldXPContextSize;
+ xpctxt->proximityPosition = oldXPProximityPosition;
+
+ ctxt->document = oldDocInfo;
+ ctxt->nodeList = oldList;
+ ctxt->node = oldContextNode;
+ ctxt->mode = oldMode;
+ ctxt->modeURI = oldModeURI;
+}
+
+
+/**
+ * xsltChoose:
+ * @ctxt: a XSLT process context
+ * @contextNode: the current node in the source tree
+ * @inst: the xsl:choose instruction
+ * @comp: compiled information of the instruction
+ *
+ * Processes the xsl:choose instruction on the source node.
+ */
+void
+xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
+ xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED)
+{
+ xmlNodePtr cur;
+
+ if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL))
+ return;
+
+ /*
+ * TODO: Content model checks should be done only at compilation
+ * time.
+ */
+ cur = inst->children;
+ if (cur == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:choose: The instruction has no content.\n");
+ return;
+ }
+
+#ifdef XSLT_REFACTORED
+ /*
+ * We don't check the content model during transformation.
+ */
+#else
+ if ((! IS_XSLT_ELEM(cur)) || (! IS_XSLT_NAME(cur, "when"))) {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:choose: xsl:when expected first\n");
+ return;
+ }
+#endif
+
+ {
+ int testRes = 0, res = 0;
+
+#ifdef XSLT_REFACTORED
+ xsltStyleItemWhenPtr wcomp = NULL;
+#else
+ xsltStylePreCompPtr wcomp = NULL;
+#endif
+
+ /*
+ * Process xsl:when ---------------------------------------------------
+ */
+ while (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "when")) {
+ wcomp = cur->psvi;
+
+ if ((wcomp == NULL) || (wcomp->test == NULL) ||
+ (wcomp->comp == NULL))
+ {
+ xsltTransformError(ctxt, NULL, cur,
+ "Internal error in xsltChoose(): "
+ "The XSLT 'when' instruction was not compiled.\n");
+ goto error;
+ }
+
+
+#ifdef WITH_DEBUGGER
+ if (xslDebugStatus != XSLT_DEBUG_NONE) {
+ /*
+ * TODO: Isn't comp->templ always NULL for xsl:choose?
+ */
+ xslHandleDebugger(cur, contextNode, NULL, ctxt);
+ }
+#endif
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltChoose: test %s\n", wcomp->test));
+#endif
+
+#ifdef XSLT_FAST_IF
+ res = xsltPreCompEvalToBoolean(ctxt, contextNode, wcomp);
+
+ if (res == -1) {
+ ctxt->state = XSLT_STATE_STOPPED;
+ goto error;
+ }
+ testRes = (res == 1) ? 1 : 0;
+
+#else /* XSLT_FAST_IF */
+
+ res = xsltPreCompEval(ctxt, cotextNode, wcomp);
+
+ if (res != NULL) {
+ if (res->type != XPATH_BOOLEAN)
+ res = xmlXPathConvertBoolean(res);
+ if (res->type == XPATH_BOOLEAN)
+ testRes = res->boolval;
+ else {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltChoose: test didn't evaluate to a boolean\n"));
+#endif
+ goto error;
+ }
+ xmlXPathFreeObject(res);
+ res = NULL;
+ } else {
+ ctxt->state = XSLT_STATE_STOPPED;
+ goto error;
+ }
+
+#endif /* else of XSLT_FAST_IF */
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltChoose: test evaluate to %d\n", testRes));
+#endif
+ if (testRes)
+ goto test_is_true;
+
+ cur = cur->next;
+ }
+
+ /*
+ * Process xsl:otherwise ----------------------------------------------
+ */
+ if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "otherwise")) {
+
+#ifdef WITH_DEBUGGER
+ if (xslDebugStatus != XSLT_DEBUG_NONE)
+ xslHandleDebugger(cur, contextNode, NULL, ctxt);
+#endif
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
+ "evaluating xsl:otherwise\n"));
+#endif
+ goto test_is_true;
+ }
+ goto exit;
+
+test_is_true:
+
+ goto process_sequence;
+ }
+
+process_sequence:
+
+ /*
+ * Instantiate the sequence constructor.
+ */
+ xsltApplySequenceConstructor(ctxt, ctxt->node, cur->children,
+ NULL);
+
+exit:
+error:
+ return;
+}
+
+/**
+ * xsltIf:
+ * @ctxt: a XSLT process context
+ * @contextNode: the current node in the source tree
+ * @inst: the xsl:if instruction
+ * @castedComp: compiled information of the instruction
+ *
+ * Processes the xsl:if instruction on the source node.
+ */
+void
+xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
+ xmlNodePtr inst, xsltStylePreCompPtr castedComp)
+{
+ int res = 0;
+
+#ifdef XSLT_REFACTORED
+ xsltStyleItemIfPtr comp = (xsltStyleItemIfPtr) castedComp;
+#else
+ xsltStylePreCompPtr comp = castedComp;
+#endif
+
+ if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL))
+ return;
+ if ((comp == NULL) || (comp->test == NULL) || (comp->comp == NULL)) {
+ xsltTransformError(ctxt, NULL, inst,
+ "Internal error in xsltIf(): "
+ "The XSLT 'if' instruction was not compiled.\n");
+ return;
+ }
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltIf: test %s\n", comp->test));
+#endif
+
+#ifdef XSLT_FAST_IF
+ {
+ xmlDocPtr oldLocalFragmentTop = ctxt->localRVT;
+
+ res = xsltPreCompEvalToBoolean(ctxt, contextNode, comp);
+
+ /*
+ * Cleanup fragments created during evaluation of the
+ * "select" expression.
+ */
+ if (oldLocalFragmentTop != ctxt->localRVT)
+ xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
+ }
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltIf: test evaluate to %d\n", res));
+#endif
+
+ if (res == -1) {
+ ctxt->state = XSLT_STATE_STOPPED;
+ goto error;
+ }
+ if (res == 1) {
+ /*
+ * Instantiate the sequence constructor of xsl:if.
+ */
+ xsltApplySequenceConstructor(ctxt,
+ contextNode, inst->children, NULL);
+ }
+
+#else /* XSLT_FAST_IF */
+ {
+ /*
+ * OLD CODE:
+ */
+ xmlXPathObjectPtr xpobj = xsltPreCompEval(ctxt, contextNode, comp);
+ if (xpobj != NULL) {
+ if (xpobj->type != XPATH_BOOLEAN)
+ xpobj = xmlXPathConvertBoolean(xpobj);
+ if (xpobj->type == XPATH_BOOLEAN) {
+ res = xpobj->boolval;
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltIf: test evaluate to %d\n", res));
+#endif
+ if (res) {
+ xsltApplySequenceConstructor(ctxt,
+ contextNode, inst->children, NULL);
+ }
+ } else {
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt, XSLT_TRACE_IF,
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltIf: test didn't evaluate to a boolean\n"));
+#endif
+ ctxt->state = XSLT_STATE_STOPPED;
+ }
+ xmlXPathFreeObject(xpobj);
+ } else {
+ ctxt->state = XSLT_STATE_STOPPED;
+ }
+ }
+#endif /* else of XSLT_FAST_IF */
+
+error:
+ return;
+}
+
+/**
+ * xsltForEach:
+ * @ctxt: an XSLT transformation context
+ * @contextNode: the "current node" in the source tree
+ * @inst: the element node of the xsl:for-each instruction
+ * @castedComp: the compiled information of the instruction
+ *
+ * Process the xslt for-each node on the source node
+ */
+void
+xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
+ xmlNodePtr inst, xsltStylePreCompPtr castedComp)
+{
+#ifdef XSLT_REFACTORED
+ xsltStyleItemForEachPtr comp = (xsltStyleItemForEachPtr) castedComp;
+#else
+ xsltStylePreCompPtr comp = castedComp;
+#endif
+ int i;
+ xmlXPathObjectPtr res = NULL;
+ xmlNodePtr cur, curInst;
+ xmlNodeSetPtr list = NULL;
+ xmlNodeSetPtr oldList;
+ int oldXPProximityPosition, oldXPContextSize;
+ xmlNodePtr oldContextNode;
+ xsltTemplatePtr oldCurTemplRule;
+ xmlDocPtr oldXPDoc;
+ xsltDocumentPtr oldDocInfo;
+ xmlXPathContextPtr xpctxt;
+
+ if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltForEach(): Bad arguments.\n");
+ return;
+ }
+
+ if (comp == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "Internal error in xsltForEach(): "
+ "The XSLT 'for-each' instruction was not compiled.\n");
+ return;
+ }
+ if ((comp->select == NULL) || (comp->comp == NULL)) {
+ xsltTransformError(ctxt, NULL, inst,
+ "Internal error in xsltForEach(): "
+ "The selecting expression of the XSLT 'for-each' "
+ "instruction was not compiled correctly.\n");
+ return;
+ }
+ xpctxt = ctxt->xpathCtxt;
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltForEach: select %s\n", comp->select));
+#endif
+
+ /*
+ * Save context states.
+ */
+ oldDocInfo = ctxt->document;
+ oldList = ctxt->nodeList;
+ oldContextNode = ctxt->node;
+ /*
+ * The "current template rule" is cleared for the instantiation of
+ * xsl:for-each.
+ */
+ oldCurTemplRule = ctxt->currentTemplateRule;
+ ctxt->currentTemplateRule = NULL;
+
+ oldXPDoc = xpctxt->doc;
+ oldXPProximityPosition = xpctxt->proximityPosition;
+ oldXPContextSize = xpctxt->contextSize;
+
+ /*
+ * Evaluate the 'select' expression.
+ */
+ res = xsltPreCompEval(ctxt, contextNode, comp);
+
+ if (res != NULL) {
+ if (res->type == XPATH_NODESET)
+ list = res->nodesetval;
+ else {
+ xsltTransformError(ctxt, NULL, inst,
+ "The 'select' expression does not evaluate to a node set.\n");
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltForEach: select didn't evaluate to a node list\n"));
+#endif
+ goto error;
+ }
+ } else {
+ xsltTransformError(ctxt, NULL, inst,
+ "Failed to evaluate the 'select' expression.\n");
+ ctxt->state = XSLT_STATE_STOPPED;
+ goto error;
+ }
+
+ if ((list == NULL) || (list->nodeNr <= 0))
+ goto exit;
+
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltForEach: select evaluates to %d nodes\n", list->nodeNr));
+#endif
+
+ /*
+ * Set the list; this has to be done already here for xsltDoSortFunction().
+ */
+ ctxt->nodeList = list;
+ /*
+ * Handle xsl:sort instructions and skip them for further processing.
+ * BUG TODO: We are not using namespaced potentially defined on the
+ * xsl:sort element; XPath expression might fail.
+ */
+ curInst = inst->children;
+ if (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) {
+ int nbsorts = 0;
+ xmlNodePtr sorts[XSLT_MAX_SORT];
+
+ sorts[nbsorts++] = curInst;
+
+#ifdef WITH_DEBUGGER
+ if (xslDebugStatus != XSLT_DEBUG_NONE)
+ xslHandleDebugger(curInst, contextNode, NULL, ctxt);
+#endif
+
+ curInst = curInst->next;
+ while (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) {
+ if (nbsorts >= XSLT_MAX_SORT) {
+ xsltTransformError(ctxt, NULL, curInst,
+ "The number of xsl:sort instructions exceeds the "
+ "maximum (%d) allowed by this processor.\n",
+ XSLT_MAX_SORT);
+ goto error;
+ } else {
+ sorts[nbsorts++] = curInst;
+ }
+
+#ifdef WITH_DEBUGGER
+ if (xslDebugStatus != XSLT_DEBUG_NONE)
+ xslHandleDebugger(curInst, contextNode, NULL, ctxt);
+#endif
+ curInst = curInst->next;
+ }
+ xsltDoSortFunction(ctxt, sorts, nbsorts);
+ }
+ xpctxt->contextSize = list->nodeNr;
+ /*
+ * Instantiate the sequence constructor for each selected node.
+ */
+ for (i = 0; i < list->nodeNr; i++) {
+ cur = list->nodeTab[i];
+ /*
+ * The selected node becomes the "current node".
+ */
+ ctxt->node = cur;
+ /*
+ * An xsl:for-each can change the current context doc.
+ * OPTIMIZE TODO: Get rid of the need to set the context doc.
+ */
+ if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL))
+ xpctxt->doc = cur->doc;
+
+ xpctxt->proximityPosition = i + 1;
+
+ xsltApplySequenceConstructor(ctxt, cur, curInst, NULL);
+ }
+
+exit:
+error:
+ if (res != NULL)
+ xmlXPathFreeObject(res);
+ /*
+ * Restore old states.
+ */
+ ctxt->document = oldDocInfo;
+ ctxt->nodeList = oldList;
+ ctxt->node = oldContextNode;
+ ctxt->currentTemplateRule = oldCurTemplRule;
+
+ xpctxt->doc = oldXPDoc;
+ xpctxt->contextSize = oldXPContextSize;
+ xpctxt->proximityPosition = oldXPProximityPosition;
+}
+
+/************************************************************************
+ * *
+ * Generic interface *
+ * *
+ ************************************************************************/
+
+#ifdef XSLT_GENERATE_HTML_DOCTYPE
+typedef struct xsltHTMLVersion {
+ const char *version;
+ const char *public;
+ const char *system;
+} xsltHTMLVersion;
+
+static xsltHTMLVersion xsltHTMLVersions[] = {
+ { "4.01frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
+ "http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd"},
+ { "4.01strict", "-//W3C//DTD HTML 4.01//EN",
+ "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"},
+ { "4.01trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
+ "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
+ { "4.01", "-//W3C//DTD HTML 4.01 Transitional//EN",
+ "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
+ { "4.0strict", "-//W3C//DTD HTML 4.01//EN",
+ "http://www.w3.org/TR/html4/strict.dtd"},
+ { "4.0trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
+ "http://www.w3.org/TR/html4/loose.dtd"},
+ { "4.0frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
+ "http://www.w3.org/TR/html4/frameset.dtd"},
+ { "4.0", "-//W3C//DTD HTML 4.01 Transitional//EN",
+ "http://www.w3.org/TR/html4/loose.dtd"},
+ { "3.2", "-//W3C//DTD HTML 3.2//EN", NULL }
+};
+
+/**
+ * xsltGetHTMLIDs:
+ * @version: the version string
+ * @publicID: used to return the public ID
+ * @systemID: used to return the system ID
+ *
+ * Returns -1 if not found, 0 otherwise and the system and public
+ * Identifier for this given verion of HTML
+ */
+static int
+xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID,
+ const xmlChar **systemID) {
+ unsigned int i;
+ if (version == NULL)
+ return(-1);
+ for (i = 0;i < (sizeof(xsltHTMLVersions)/sizeof(xsltHTMLVersions[1]));
+ i++) {
+ if (!xmlStrcasecmp(version,
+ (const xmlChar *) xsltHTMLVersions[i].version)) {
+ if (publicID != NULL)
+ *publicID = (const xmlChar *) xsltHTMLVersions[i].public;
+ if (systemID != NULL)
+ *systemID = (const xmlChar *) xsltHTMLVersions[i].system;
+ return(0);
+ }
+ }
+ return(-1);
+}
+#endif
+
+/**
+ * xsltApplyStripSpaces:
+ * @ctxt: a XSLT process context
+ * @node: the root of the XML tree
+ *
+ * Strip the unwanted ignorable spaces from the input tree
+ */
+void
+xsltApplyStripSpaces(xsltTransformContextPtr ctxt, xmlNodePtr node) {
+ xmlNodePtr current;
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ int nb = 0;
+#endif
+
+
+ current = node;
+ while (current != NULL) {
+ /*
+ * Cleanup children empty nodes if asked for
+ */
+ if ((IS_XSLT_REAL_NODE(current)) &&
+ (current->children != NULL) &&
+ (xsltFindElemSpaceHandling(ctxt, current))) {
+ xmlNodePtr delete = NULL, cur = current->children;
+
+ while (cur != NULL) {
+ if (IS_BLANK_NODE(cur))
+ delete = cur;
+
+ cur = cur->next;
+ if (delete != NULL) {
+ xmlUnlinkNode(delete);
+ xmlFreeNode(delete);
+ delete = NULL;
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ nb++;
+#endif
+ }
+ }
+ }
+
+ /*
+ * Skip to next node in document order.
+ */
+ if (node->type == XML_ENTITY_REF_NODE) {
+ /* process deep in entities */
+ xsltApplyStripSpaces(ctxt, node->children);
+ }
+ if ((current->children != NULL) &&
+ (current->type != XML_ENTITY_REF_NODE)) {
+ current = current->children;
+ } else if (current->next != NULL) {
+ current = current->next;
+ } else {
+ do {
+ current = current->parent;
+ if (current == NULL)
+ break;
+ if (current == node)
+ goto done;
+ if (current->next != NULL) {
+ current = current->next;
+ break;
+ }
+ } while (current != NULL);
+ }
+ }
+
+done:
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ XSLT_TRACE(ctxt,XSLT_TRACE_STRIP_SPACES,xsltGenericDebug(xsltGenericDebugContext,
+ "xsltApplyStripSpaces: removed %d ignorable blank node\n", nb));
+#endif
+ return;
+}
+
+static int
+xsltCountKeys(xsltTransformContextPtr ctxt)
+{
+ xsltStylesheetPtr style;
+ xsltKeyDefPtr keyd;
+
+ if (ctxt == NULL)
+ return(-1);
+
+ /*
+ * Do we have those nastly templates with a key() in the match pattern?
+ */
+ ctxt->hasTemplKeyPatterns = 0;
+ style = ctxt->style;
+ while (style != NULL) {
+ if (style->keyMatch != NULL) {
+ ctxt->hasTemplKeyPatterns = 1;
+ break;
+ }
+ style = xsltNextImport(style);
+ }
+ /*
+ * Count number of key declarations.
+ */
+ ctxt->nbKeys = 0;
+ style = ctxt->style;
+ while (style != NULL) {
+ keyd = style->keys;
+ while (keyd) {
+ ctxt->nbKeys++;
+ keyd = keyd->next;
+ }
+ style = xsltNextImport(style);
+ }
+ return(ctxt->nbKeys);
+}
+
+/**
+ * xsltApplyStylesheetInternal:
+ * @style: a parsed XSLT stylesheet
+ * @doc: a parsed XML document
+ * @params: a NULL terminated array of parameters names/values tuples
+ * @output: the targetted output
+ * @profile: profile FILE * output or NULL
+ * @user: user provided parameter
+ *
+ * Apply the stylesheet to the document
+ * NOTE: This may lead to a non-wellformed output XML wise !
+ *
+ * Returns the result document or NULL in case of error
+ */
+static xmlDocPtr
+xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc,
+ const char **params, const char *output,
+ FILE * profile, xsltTransformContextPtr userCtxt)
+{
+ xmlDocPtr res = NULL;
+ xsltTransformContextPtr ctxt = NULL;
+ xmlNodePtr root, node;
+ const xmlChar *method;
+ const xmlChar *doctypePublic;
+ const xmlChar *doctypeSystem;
+ const xmlChar *version;
+ const xmlChar *encoding;
+ xsltStackElemPtr variables;
+ xsltStackElemPtr vptr;
+
+ xsltInitGlobals();
+
+ if ((style == NULL) || (doc == NULL))
+ return (NULL);
+
+ if (style->internalized == 0) {
+#ifdef WITH_XSLT_DEBUG
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Stylesheet was not fully internalized !\n");
+#endif
+ }
+ if (doc->intSubset != NULL) {
+ /*
+ * Avoid hitting the DTD when scanning nodes
+ * but keep it linked as doc->intSubset
+ */
+ xmlNodePtr cur = (xmlNodePtr) doc->intSubset;
+ if (cur->next != NULL)
+ cur->next->prev = cur->prev;
+ if (cur->prev != NULL)
+ cur->prev->next = cur->next;
+ if (doc->children == cur)
+ doc->children = cur->next;
+ if (doc->last == cur)
+ doc->last = cur->prev;
+ cur->prev = cur->next = NULL;
+ }
+
+ /*
+ * Check for XPath document order availability
+ */
+ root = xmlDocGetRootElement(doc);
+ if (root != NULL) {
+ if (((long) root->content) >= 0 && (xslDebugStatus == XSLT_DEBUG_NONE))
+ xmlXPathOrderDocElems(doc);
+ }
+
+ if (userCtxt != NULL)
+ ctxt = userCtxt;
+ else
+ ctxt = xsltNewTransformContext(style, doc);
+
+ if (ctxt == NULL)
+ return (NULL);
+
+ ctxt->initialContextDoc = doc;
+ ctxt->initialContextNode = (xmlNodePtr) doc;
+
+ if (profile != NULL)
+ ctxt->profile = 1;
+
+ if (output != NULL)
+ ctxt->outputFile = output;
+ else
+ ctxt->outputFile = NULL;
+
+ /*
+ * internalize the modes if needed
+ */
+ if (ctxt->dict != NULL) {
+ if (ctxt->mode != NULL)
+ ctxt->mode = xmlDictLookup(ctxt->dict, ctxt->mode, -1);
+ if (ctxt->modeURI != NULL)
+ ctxt->modeURI = xmlDictLookup(ctxt->dict, ctxt->modeURI, -1);
+ }
+
+ XSLT_GET_IMPORT_PTR(method, style, method)
+ XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
+ XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
+ XSLT_GET_IMPORT_PTR(version, style, version)
+ XSLT_GET_IMPORT_PTR(encoding, style, encoding)
+
+ if ((method != NULL) &&
+ (!xmlStrEqual(method, (const xmlChar *) "xml")))
+ {
+ if (xmlStrEqual(method, (const xmlChar *) "html")) {
+ ctxt->type = XSLT_OUTPUT_HTML;
+ if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
+ res = htmlNewDoc(doctypeSystem, doctypePublic);
+ } else {
+ if (version == NULL) {
+ xmlDtdPtr dtd;
+
+ res = htmlNewDoc(NULL, NULL);
+ /*
+ * Make sure no DTD node is generated in this case
+ */
+ if (res != NULL) {
+ dtd = xmlGetIntSubset(res);
+ if (dtd != NULL) {
+ xmlUnlinkNode((xmlNodePtr) dtd);
+ xmlFreeDtd(dtd);
+ }
+ res->intSubset = NULL;
+ res->extSubset = NULL;
+ }
+ } else {
+
+#ifdef XSLT_GENERATE_HTML_DOCTYPE
+ xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
+#endif
+ res = htmlNewDoc(doctypeSystem, doctypePublic);
+ }
+ }
+ if (res == NULL)
+ goto error;
+ res->dict = ctxt->dict;
+ xmlDictReference(res->dict);
+
+#ifdef WITH_XSLT_DEBUG
+ xsltGenericDebug(xsltGenericDebugContext,
+ "reusing transformation dict for output\n");
+#endif
+ } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
+ xsltTransformError(ctxt, NULL, (xmlNodePtr) doc,
+ "xsltApplyStylesheetInternal: unsupported method xhtml, using html\n");
+ ctxt->type = XSLT_OUTPUT_HTML;
+ res = htmlNewDoc(doctypeSystem, doctypePublic);
+ if (res == NULL)
+ goto error;
+ res->dict = ctxt->dict;
+ xmlDictReference(res->dict);
+
+#ifdef WITH_XSLT_DEBUG
+ xsltGenericDebug(xsltGenericDebugContext,
+ "reusing transformation dict for output\n");
+#endif
+ } else if (xmlStrEqual(method, (const xmlChar *) "text")) {
+ ctxt->type = XSLT_OUTPUT_TEXT;
+ res = xmlNewDoc(style->version);
+ if (res == NULL)
+ goto error;
+ res->dict = ctxt->dict;
+ xmlDictReference(res->dict);
+
+#ifdef WITH_XSLT_DEBUG
+ xsltGenericDebug(xsltGenericDebugContext,
+ "reusing transformation dict for output\n");
+#endif
+ } else {
+ xsltTransformError(ctxt, NULL, (xmlNodePtr) doc,
+ "xsltApplyStylesheetInternal: unsupported method (%s)\n",
+ method);
+ goto error;
+ }
+ } else {
+ ctxt->type = XSLT_OUTPUT_XML;
+ res = xmlNewDoc(style->version);
+ if (res == NULL)
+ goto error;
+ res->dict = ctxt->dict;
+ xmlDictReference(ctxt->dict);
+#ifdef WITH_XSLT_DEBUG
+ xsltGenericDebug(xsltGenericDebugContext,
+ "reusing transformation dict for output\n");
+#endif
+ }
+ res->charset = XML_CHAR_ENCODING_UTF8;
+ if (encoding != NULL)
+ res->encoding = xmlStrdup(encoding);
+ variables = style->variables;
+
+ /*
+ * Start the evaluation, evaluate the params, the stylesheets globals
+ * and start by processing the top node.
+ */
+ if (xsltNeedElemSpaceHandling(ctxt))
+ xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc));
+ /*
+ * Evaluate global params and user-provided params.
+ */
+ ctxt->node = (xmlNodePtr) doc;
+ if (ctxt->globalVars == NULL)
+ ctxt->globalVars = xmlHashCreate(20);
+ if (params != NULL) {
+ xsltEvalUserParams(ctxt, params);
+ }
+
+ /* need to be called before evaluating global variables */
+ xsltCountKeys(ctxt);
+
+ xsltEvalGlobalVariables(ctxt);
+
+ /* Clean up any unused RVTs. */
+ xsltReleaseLocalRVTs(ctxt, NULL);
+
+ ctxt->node = (xmlNodePtr) doc;
+ ctxt->output = res;
+ ctxt->insert = (xmlNodePtr) res;
+ ctxt->varsBase = ctxt->varsNr - 1;
+
+ ctxt->xpathCtxt->contextSize = 1;
+ ctxt->xpathCtxt->proximityPosition = 1;
+ ctxt->xpathCtxt->node = NULL; /* TODO: Set the context node here? */
+ /*
+ * Start processing the source tree -----------------------------------
+ */
+ xsltProcessOneNode(ctxt, ctxt->node, NULL);
+ /*
+ * Remove all remaining vars from the stack.
+ */
+ xsltLocalVariablePop(ctxt, 0, -2);
+ xsltShutdownCtxtExts(ctxt);
+
+ xsltCleanupTemplates(style); /* TODO: <- style should be read only */
+
+ /*
+ * Now cleanup our variables so stylesheet can be re-used
+ *
+ * TODO: this is not needed anymore global variables are copied
+ * and not evaluated directly anymore, keep this as a check
+ */
+ if (style->variables != variables) {
+ vptr = style->variables;
+ while (vptr->next != variables)
+ vptr = vptr->next;
+ vptr->next = NULL;
+ xsltFreeStackElemList(style->variables);
+ style->variables = variables;
+ }
+ vptr = style->variables;
+ while (vptr != NULL) {
+ if (vptr->computed) {
+ if (vptr->value != NULL) {
+ xmlXPathFreeObject(vptr->value);
+ vptr->value = NULL;
+ vptr->computed = 0;
+ }
+ }
+ vptr = vptr->next;
+ }
+#if 0
+ /*
+ * code disabled by wmb; awaiting kb's review
+ * problem is that global variable(s) may contain xpath objects
+ * from doc associated with RVT, so can't be freed at this point.
+ * xsltFreeTransformContext includes a call to xsltFreeRVTs, so
+ * I assume this shouldn't be required at this point.
+ */
+ /*
+ * Free all remaining tree fragments.
+ */
+ xsltFreeRVTs(ctxt);
+#endif
+ /*
+ * Do some post processing work depending on the generated output
+ */
+ root = xmlDocGetRootElement(res);
+ if (root != NULL) {
+ const xmlChar *doctype = NULL;
+
+ if ((root->ns != NULL) && (root->ns->prefix != NULL))
+ doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name);
+ if (doctype == NULL)
+ doctype = root->name;
+
+ /*
+ * Apply the default selection of the method
+ */
+ if ((method == NULL) &&
+ (root->ns == NULL) &&
+ (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) {
+ xmlNodePtr tmp;
+
+ tmp = res->children;
+ while ((tmp != NULL) && (tmp != root)) {
+ if (tmp->type == XML_ELEMENT_NODE)
+ break;
+ if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp)))
+ break;
+ tmp = tmp->next;
+ }
+ if (tmp == root) {
+ ctxt->type = XSLT_OUTPUT_HTML;
+ /*
+ * REVISIT TODO: XML_HTML_DOCUMENT_NODE is set after the
+ * transformation on the doc, but functions like
+ */
+ res->type = XML_HTML_DOCUMENT_NODE;
+ if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
+ res->intSubset = xmlCreateIntSubset(res, doctype,
+ doctypePublic,
+ doctypeSystem);
+#ifdef XSLT_GENERATE_HTML_DOCTYPE
+ } else if (version != NULL) {
+ xsltGetHTMLIDs(version, &doctypePublic,
+ &doctypeSystem);
+ if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
+ res->intSubset =
+ xmlCreateIntSubset(res, doctype,
+ doctypePublic,
+ doctypeSystem);
+#endif
+ }
+ }
+
+ }
+ if (ctxt->type == XSLT_OUTPUT_XML) {
+ XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
+ XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
+ if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
+ xmlNodePtr last;
+ /* Need a small "hack" here to assure DTD comes before
+ possible comment nodes */
+ node = res->children;
+ last = res->last;
+ res->children = NULL;
+ res->last = NULL;
+ res->intSubset = xmlCreateIntSubset(res, doctype,
+ doctypePublic,
+ doctypeSystem);
+ if (res->children != NULL) {
+ res->children->next = node;
+ node->prev = res->children;
+ res->last = last;
+ } else {
+ res->children = node;
+ res->last = last;
+ }
+ }
+ }
+ }
+ xmlXPathFreeNodeSet(ctxt->nodeList);
+ if (profile != NULL) {
+ xsltSaveProfiling(ctxt, profile);
+ }
+
+ /*
+ * Be pedantic.
+ */
+ if ((ctxt != NULL) && (ctxt->state != XSLT_STATE_OK)) {
+ xmlFreeDoc(res);
+ res = NULL;
+ }
+ if ((res != NULL) && (ctxt != NULL) && (output != NULL)) {
+ int ret;
+
+ ret = xsltCheckWrite(ctxt->sec, ctxt, (const xmlChar *) output);
+ if (ret == 0) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "xsltApplyStylesheet: forbidden to save to %s\n",
+ output);
+ } else if (ret < 0) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "xsltApplyStylesheet: saving to %s may not be possible\n",
+ output);
+ }
+ }
+
+#ifdef XSLT_DEBUG_PROFILE_CACHE
+ printf("# Cache:\n");
+ printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs);
+ printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars);
+#endif
+
+ if ((ctxt != NULL) && (userCtxt == NULL))
+ xsltFreeTransformContext(ctxt);
+
+ return (res);
+
+error:
+ if (res != NULL)
+ xmlFreeDoc(res);
+
+#ifdef XSLT_DEBUG_PROFILE_CACHE
+ printf("# Cache:\n");
+ printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs);
+ printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars);
+#endif
+
+ if ((ctxt != NULL) && (userCtxt == NULL))
+ xsltFreeTransformContext(ctxt);
+ return (NULL);
+}
+
+/**
+ * xsltApplyStylesheet:
+ * @style: a parsed XSLT stylesheet
+ * @doc: a parsed XML document
+ * @params: a NULL terminated arry of parameters names/values tuples
+ *
+ * Apply the stylesheet to the document
+ * NOTE: This may lead to a non-wellformed output XML wise !
+ *
+ * Returns the result document or NULL in case of error
+ */
+xmlDocPtr
+xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
+ const char **params)
+{
+ return (xsltApplyStylesheetInternal(style, doc, params, NULL, NULL, NULL));
+}
+
+/**
+ * xsltProfileStylesheet:
+ * @style: a parsed XSLT stylesheet
+ * @doc: a parsed XML document
+ * @params: a NULL terminated arry of parameters names/values tuples
+ * @output: a FILE * for the profiling output
+ *
+ * Apply the stylesheet to the document and dump the profiling to
+ * the given output.
+ *
+ * Returns the result document or NULL in case of error
+ */
+xmlDocPtr
+xsltProfileStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
+ const char **params, FILE * output)
+{
+ xmlDocPtr res;
+
+ res = xsltApplyStylesheetInternal(style, doc, params, NULL, output, NULL);
+ return (res);
+}
+
+/**
+ * xsltApplyStylesheetUser:
+ * @style: a parsed XSLT stylesheet
+ * @doc: a parsed XML document
+ * @params: a NULL terminated array of parameters names/values tuples
+ * @output: the targetted output
+ * @profile: profile FILE * output or NULL
+ * @userCtxt: user provided transform context
+ *
+ * Apply the stylesheet to the document and allow the user to provide
+ * its own transformation context.
+ *
+ * Returns the result document or NULL in case of error
+ */
+xmlDocPtr
+xsltApplyStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc,
+ const char **params, const char *output,
+ FILE * profile, xsltTransformContextPtr userCtxt)
+{
+ xmlDocPtr res;
+
+ res = xsltApplyStylesheetInternal(style, doc, params, output,
+ profile, userCtxt);
+ return (res);
+}
+
+/**
+ * xsltRunStylesheetUser:
+ * @style: a parsed XSLT stylesheet
+ * @doc: a parsed XML document
+ * @params: a NULL terminated array of parameters names/values tuples
+ * @output: the URL/filename ot the generated resource if available
+ * @SAX: a SAX handler for progressive callback output (not implemented yet)
+ * @IObuf: an output buffer for progressive output (not implemented yet)
+ * @profile: profile FILE * output or NULL
+ * @userCtxt: user provided transform context
+ *
+ * Apply the stylesheet to the document and generate the output according
+ * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf.
+ *
+ * NOTE: This may lead to a non-wellformed output XML wise !
+ * NOTE: This may also result in multiple files being generated
+ * NOTE: using IObuf, the result encoding used will be the one used for
+ * creating the output buffer, use the following macro to read it
+ * from the stylesheet
+ * XSLT_GET_IMPORT_PTR(encoding, style, encoding)
+ * NOTE: using SAX, any encoding specified in the stylesheet will be lost
+ * since the interface uses only UTF8
+ *
+ * Returns the number of by written to the main resource or -1 in case of
+ * error.
+ */
+int
+xsltRunStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc,
+ const char **params, const char *output,
+ xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf,
+ FILE * profile, xsltTransformContextPtr userCtxt)
+{
+ xmlDocPtr tmp;
+ int ret;
+
+ if ((output == NULL) && (SAX == NULL) && (IObuf == NULL))
+ return (-1);
+ if ((SAX != NULL) && (IObuf != NULL))
+ return (-1);
+
+ /* unsupported yet */
+ if (SAX != NULL) {
+ XSLT_TODO /* xsltRunStylesheet xmlSAXHandlerPtr SAX */
+ return (-1);
+ }
+
+ tmp = xsltApplyStylesheetInternal(style, doc, params, output, profile,
+ userCtxt);
+ if (tmp == NULL) {
+ xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
+ "xsltRunStylesheet : run failed\n");
+ return (-1);
+ }
+ if (IObuf != NULL) {
+ /* TODO: incomplete, IObuf output not progressive */
+ ret = xsltSaveResultTo(IObuf, tmp, style);
+ } else {
+ ret = xsltSaveResultToFilename(output, tmp, style, 0);
+ }
+ xmlFreeDoc(tmp);
+ return (ret);
+}
+
+/**
+ * xsltRunStylesheet:
+ * @style: a parsed XSLT stylesheet
+ * @doc: a parsed XML document
+ * @params: a NULL terminated array of parameters names/values tuples
+ * @output: the URL/filename ot the generated resource if available
+ * @SAX: a SAX handler for progressive callback output (not implemented yet)
+ * @IObuf: an output buffer for progressive output (not implemented yet)
+ *
+ * Apply the stylesheet to the document and generate the output according
+ * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf.
+ *
+ * NOTE: This may lead to a non-wellformed output XML wise !
+ * NOTE: This may also result in multiple files being generated
+ * NOTE: using IObuf, the result encoding used will be the one used for
+ * creating the output buffer, use the following macro to read it
+ * from the stylesheet
+ * XSLT_GET_IMPORT_PTR(encoding, style, encoding)
+ * NOTE: using SAX, any encoding specified in the stylesheet will be lost
+ * since the interface uses only UTF8
+ *
+ * Returns the number of bytes written to the main resource or -1 in case of
+ * error.
+ */
+int
+xsltRunStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
+ const char **params, const char *output,
+ xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf)
+{
+ return(xsltRunStylesheetUser(style, doc, params, output, SAX, IObuf,
+ NULL, NULL));
+}
+
+/**
+ * xsltRegisterAllElement:
+ * @ctxt: the XPath context
+ *
+ * Registers all default XSLT elements in this context
+ */
+void
+xsltRegisterAllElement(xsltTransformContextPtr ctxt)
+{
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-templates",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltApplyTemplates);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-imports",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltApplyImports);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "call-template",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltCallTemplate);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "element",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltElement);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "attribute",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltAttribute);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "text",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltText);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "processing-instruction",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltProcessingInstruction);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "comment",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltComment);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "copy",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltCopy);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "value-of",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltValueOf);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "number",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltNumber);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "for-each",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltForEach);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "if",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltIf);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "choose",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltChoose);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "sort",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltSort);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "copy-of",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltCopyOf);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "message",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltMessage);
+
+ /*
+ * Those don't have callable entry points but are registered anyway
+ */
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "variable",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltDebug);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "param",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltDebug);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "with-param",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltDebug);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "decimal-format",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltDebug);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "when",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltDebug);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "otherwise",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltDebug);
+ xsltRegisterExtElement(ctxt, (const xmlChar *) "fallback",
+ XSLT_NAMESPACE,
+ (xsltTransformFunction) xsltDebug);
+
+}
diff --git a/libxslt/transform.h b/libxslt/transform.h
new file mode 100644
index 0000000..2cfbec2
--- /dev/null
+++ b/libxslt/transform.h
@@ -0,0 +1,207 @@
+/*
+ * Summary: the XSLT engine transformation part.
+ * Description: This module implements the bulk of the actual
+ * transformation processing. Most of the xsl: element
+ * constructs are implemented in this module.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_TRANSFORM_H__
+#define __XML_XSLT_TRANSFORM_H__
+
+#include <libxml/parser.h>
+#include <libxml/xmlIO.h>
+#include "xsltexports.h"
+#include <libxslt/xsltInternals.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * XInclude default processing.
+ */
+XSLTPUBFUN void XSLTCALL
+ xsltSetXIncludeDefault (int xinclude);
+XSLTPUBFUN int XSLTCALL
+ xsltGetXIncludeDefault (void);
+
+/**
+ * Export context to users.
+ */
+XSLTPUBFUN xsltTransformContextPtr XSLTCALL
+ xsltNewTransformContext (xsltStylesheetPtr style,
+ xmlDocPtr doc);
+
+XSLTPUBFUN void XSLTCALL
+ xsltFreeTransformContext(xsltTransformContextPtr ctxt);
+
+XSLTPUBFUN xmlDocPtr XSLTCALL
+ xsltApplyStylesheetUser (xsltStylesheetPtr style,
+ xmlDocPtr doc,
+ const char **params,
+ const char *output,
+ FILE * profile,
+ xsltTransformContextPtr userCtxt);
+XSLTPUBFUN void XSLTCALL
+ xsltProcessOneNode (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xsltStackElemPtr params);
+/**
+ * Private Interfaces.
+ */
+XSLTPUBFUN void XSLTCALL
+ xsltApplyStripSpaces (xsltTransformContextPtr ctxt,
+ xmlNodePtr node);
+XSLTPUBFUN xmlDocPtr XSLTCALL
+ xsltApplyStylesheet (xsltStylesheetPtr style,
+ xmlDocPtr doc,
+ const char **params);
+XSLTPUBFUN xmlDocPtr XSLTCALL
+ xsltProfileStylesheet (xsltStylesheetPtr style,
+ xmlDocPtr doc,
+ const char **params,
+ FILE * output);
+XSLTPUBFUN int XSLTCALL
+ xsltRunStylesheet (xsltStylesheetPtr style,
+ xmlDocPtr doc,
+ const char **params,
+ const char *output,
+ xmlSAXHandlerPtr SAX,
+ xmlOutputBufferPtr IObuf);
+XSLTPUBFUN int XSLTCALL
+ xsltRunStylesheetUser (xsltStylesheetPtr style,
+ xmlDocPtr doc,
+ const char **params,
+ const char *output,
+ xmlSAXHandlerPtr SAX,
+ xmlOutputBufferPtr IObuf,
+ FILE * profile,
+ xsltTransformContextPtr userCtxt);
+XSLTPUBFUN void XSLTCALL
+ xsltApplyOneTemplate (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr list,
+ xsltTemplatePtr templ,
+ xsltStackElemPtr params);
+XSLTPUBFUN void XSLTCALL
+ xsltDocumentElem (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltSort (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltCopy (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltText (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltElement (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltComment (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltAttribute (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltProcessingInstruction(xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltCopyOf (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltValueOf (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltNumber (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltApplyImports (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltCallTemplate (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltApplyTemplates (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltChoose (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltIf (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltForEach (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltStylePreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+ xsltRegisterAllElement (xsltTransformContextPtr ctxt);
+
+XSLTPUBFUN xmlNodePtr XSLTCALL
+ xsltCopyTextString (xsltTransformContextPtr ctxt,
+ xmlNodePtr target,
+ const xmlChar *string,
+ int noescape);
+
+/* Following 2 functions needed for libexslt/functions.c */
+XSLTPUBFUN void XSLTCALL
+ xsltLocalVariablePop (xsltTransformContextPtr ctxt,
+ int limitNr,
+ int level);
+XSLTPUBFUN int XSLTCALL
+ xsltLocalVariablePush (xsltTransformContextPtr ctxt,
+ xsltStackElemPtr variable,
+ int level);
+/*
+ * Hook for the debugger if activated.
+ */
+XSLTPUBFUN void XSLTCALL
+ xslHandleDebugger (xmlNodePtr cur,
+ xmlNodePtr node,
+ xsltTemplatePtr templ,
+ xsltTransformContextPtr ctxt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_TRANSFORM_H__ */
+
diff --git a/libxslt/trio.h b/libxslt/trio.h
new file mode 100644
index 0000000..941bdd0
--- /dev/null
+++ b/libxslt/trio.h
@@ -0,0 +1,216 @@
+/*************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 1998 Bjorn Reese and Daniel Stenberg.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
+ * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
+ *
+ *************************************************************************
+ *
+ * http://ctrio.sourceforge.net/
+ *
+ ************************************************************************/
+
+#ifndef TRIO_TRIO_H
+#define TRIO_TRIO_H
+
+#if !defined(WITHOUT_TRIO)
+
+/*
+ * Use autoconf defines if present. Packages using trio must define
+ * HAVE_CONFIG_H as a compiler option themselves.
+ */
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#include "triodef.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#if defined(TRIO_COMPILER_ANCIENT)
+# include <varargs.h>
+#else
+# include <stdarg.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Error codes.
+ *
+ * Remember to add a textual description to trio_strerror.
+ */
+enum {
+ TRIO_EOF = 1,
+ TRIO_EINVAL = 2,
+ TRIO_ETOOMANY = 3,
+ TRIO_EDBLREF = 4,
+ TRIO_EGAP = 5,
+ TRIO_ENOMEM = 6,
+ TRIO_ERANGE = 7,
+ TRIO_ERRNO = 8,
+ TRIO_ECUSTOM = 9
+};
+
+/* Error macros */
+#define TRIO_ERROR_CODE(x) ((-(x)) & 0x00FF)
+#define TRIO_ERROR_POSITION(x) ((-(x)) >> 8)
+#define TRIO_ERROR_NAME(x) trio_strerror(x)
+
+typedef int (*trio_outstream_t) TRIO_PROTO((trio_pointer_t, int));
+typedef int (*trio_instream_t) TRIO_PROTO((trio_pointer_t));
+
+TRIO_CONST char *trio_strerror TRIO_PROTO((int));
+
+/*************************************************************************
+ * Print Functions
+ */
+
+int trio_printf TRIO_PROTO((TRIO_CONST char *format, ...));
+int trio_vprintf TRIO_PROTO((TRIO_CONST char *format, va_list args));
+int trio_printfv TRIO_PROTO((TRIO_CONST char *format, void **args));
+
+int trio_fprintf TRIO_PROTO((FILE *file, TRIO_CONST char *format, ...));
+int trio_vfprintf TRIO_PROTO((FILE *file, TRIO_CONST char *format, va_list args));
+int trio_fprintfv TRIO_PROTO((FILE *file, TRIO_CONST char *format, void **args));
+
+int trio_dprintf TRIO_PROTO((int fd, TRIO_CONST char *format, ...));
+int trio_vdprintf TRIO_PROTO((int fd, TRIO_CONST char *format, va_list args));
+int trio_dprintfv TRIO_PROTO((int fd, TRIO_CONST char *format, void **args));
+
+int trio_cprintf TRIO_PROTO((trio_outstream_t stream, trio_pointer_t closure,
+ TRIO_CONST char *format, ...));
+int trio_vcprintf TRIO_PROTO((trio_outstream_t stream, trio_pointer_t closure,
+ TRIO_CONST char *format, va_list args));
+int trio_cprintfv TRIO_PROTO((trio_outstream_t stream, trio_pointer_t closure,
+ TRIO_CONST char *format, void **args));
+
+int trio_sprintf TRIO_PROTO((char *buffer, TRIO_CONST char *format, ...));
+int trio_vsprintf TRIO_PROTO((char *buffer, TRIO_CONST char *format, va_list args));
+int trio_sprintfv TRIO_PROTO((char *buffer, TRIO_CONST char *format, void **args));
+
+int trio_snprintf TRIO_PROTO((char *buffer, size_t max, TRIO_CONST char *format, ...));
+int trio_vsnprintf TRIO_PROTO((char *buffer, size_t bufferSize, TRIO_CONST char *format,
+ va_list args));
+int trio_snprintfv TRIO_PROTO((char *buffer, size_t bufferSize, TRIO_CONST char *format,
+ void **args));
+
+int trio_snprintfcat TRIO_PROTO((char *buffer, size_t max, TRIO_CONST char *format, ...));
+int trio_vsnprintfcat TRIO_PROTO((char *buffer, size_t bufferSize, TRIO_CONST char *format,
+ va_list args));
+
+char *trio_aprintf TRIO_PROTO((TRIO_CONST char *format, ...));
+char *trio_vaprintf TRIO_PROTO((TRIO_CONST char *format, va_list args));
+
+int trio_asprintf TRIO_PROTO((char **ret, TRIO_CONST char *format, ...));
+int trio_vasprintf TRIO_PROTO((char **ret, TRIO_CONST char *format, va_list args));
+
+/*************************************************************************
+ * Scan Functions
+ */
+int trio_scanf TRIO_PROTO((TRIO_CONST char *format, ...));
+int trio_vscanf TRIO_PROTO((TRIO_CONST char *format, va_list args));
+int trio_scanfv TRIO_PROTO((TRIO_CONST char *format, void **args));
+
+int trio_fscanf TRIO_PROTO((FILE *file, TRIO_CONST char *format, ...));
+int trio_vfscanf TRIO_PROTO((FILE *file, TRIO_CONST char *format, va_list args));
+int trio_fscanfv TRIO_PROTO((FILE *file, TRIO_CONST char *format, void **args));
+
+int trio_dscanf TRIO_PROTO((int fd, TRIO_CONST char *format, ...));
+int trio_vdscanf TRIO_PROTO((int fd, TRIO_CONST char *format, va_list args));
+int trio_dscanfv TRIO_PROTO((int fd, TRIO_CONST char *format, void **args));
+
+int trio_cscanf TRIO_PROTO((trio_instream_t stream, trio_pointer_t closure,
+ TRIO_CONST char *format, ...));
+int trio_vcscanf TRIO_PROTO((trio_instream_t stream, trio_pointer_t closure,
+ TRIO_CONST char *format, va_list args));
+int trio_cscanfv TRIO_PROTO((trio_instream_t stream, trio_pointer_t closure,
+ TRIO_CONST char *format, void **args));
+
+int trio_sscanf TRIO_PROTO((TRIO_CONST char *buffer, TRIO_CONST char *format, ...));
+int trio_vsscanf TRIO_PROTO((TRIO_CONST char *buffer, TRIO_CONST char *format, va_list args));
+int trio_sscanfv TRIO_PROTO((TRIO_CONST char *buffer, TRIO_CONST char *format, void **args));
+
+/*************************************************************************
+ * Locale Functions
+ */
+void trio_locale_set_decimal_point TRIO_PROTO((char *decimalPoint));
+void trio_locale_set_thousand_separator TRIO_PROTO((char *thousandSeparator));
+void trio_locale_set_grouping TRIO_PROTO((char *grouping));
+
+/*************************************************************************
+ * Renaming
+ */
+#ifdef TRIO_REPLACE_STDIO
+/* Replace the <stdio.h> functions */
+#ifndef HAVE_PRINTF
+# define printf trio_printf
+#endif
+#ifndef HAVE_VPRINTF
+# define vprintf trio_vprintf
+#endif
+#ifndef HAVE_FPRINTF
+# define fprintf trio_fprintf
+#endif
+#ifndef HAVE_VFPRINTF
+# define vfprintf trio_vfprintf
+#endif
+#ifndef HAVE_SPRINTF
+# define sprintf trio_sprintf
+#endif
+#ifndef HAVE_VSPRINTF
+# define vsprintf trio_vsprintf
+#endif
+#ifndef HAVE_SNPRINTF
+# define snprintf trio_snprintf
+#endif
+#ifndef HAVE_VSNPRINTF
+# define vsnprintf trio_vsnprintf
+#endif
+#ifndef HAVE_SCANF
+# define scanf trio_scanf
+#endif
+#ifndef HAVE_VSCANF
+# define vscanf trio_vscanf
+#endif
+#ifndef HAVE_FSCANF
+# define fscanf trio_fscanf
+#endif
+#ifndef HAVE_VFSCANF
+# define vfscanf trio_vfscanf
+#endif
+#ifndef HAVE_SSCANF
+# define sscanf trio_sscanf
+#endif
+#ifndef HAVE_VSSCANF
+# define vsscanf trio_vsscanf
+#endif
+/* These aren't stdio functions, but we make them look similar */
+#define dprintf trio_dprintf
+#define vdprintf trio_vdprintf
+#define aprintf trio_aprintf
+#define vaprintf trio_vaprintf
+#define asprintf trio_asprintf
+#define vasprintf trio_vasprintf
+#define dscanf trio_dscanf
+#define vdscanf trio_vdscanf
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* WITHOUT_TRIO */
+
+#endif /* TRIO_TRIO_H */
diff --git a/libxslt/triodef.h b/libxslt/triodef.h
new file mode 100644
index 0000000..0fd32fb
--- /dev/null
+++ b/libxslt/triodef.h
@@ -0,0 +1,220 @@
+/*************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2001 Bjorn Reese <breese@users.sourceforge.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
+ * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
+ *
+ ************************************************************************/
+
+#ifndef TRIO_TRIODEF_H
+#define TRIO_TRIODEF_H
+
+/*************************************************************************
+ * Platform and compiler support detection
+ */
+#if defined(__GNUC__)
+# define TRIO_COMPILER_GCC
+#elif defined(__SUNPRO_C)
+# define TRIO_COMPILER_SUNPRO
+#elif defined(__SUNPRO_CC)
+# define TRIO_COMPILER_SUNPRO
+# define __SUNPRO_C __SUNPRO_CC
+#elif defined(__xlC__) || defined(__IBMC__) || defined(__IBMCPP__)
+# define TRIO_COMPILER_XLC
+#elif defined(_AIX) && !defined(__GNUC__)
+# define TRIO_COMPILER_XLC /* Workaround for old xlc */
+#elif defined(__DECC) || defined(__DECCXX)
+# define TRIO_COMPILER_DECC
+#elif defined(__osf__) && defined(__LANGUAGE_C__)
+# define TRIO_COMPILER_DECC /* Workaround for old DEC C compilers */
+#elif defined(_MSC_VER)
+# define TRIO_COMPILER_MSVC
+#elif defined(__BORLANDC__)
+# define TRIO_COMPILER_BCB
+#endif
+
+#if defined(VMS) || defined(__VMS)
+/*
+ * VMS is placed first to avoid identifying the platform as Unix
+ * based on the DECC compiler later on.
+ */
+# define TRIO_PLATFORM_VMS
+#elif defined(unix) || defined(__unix) || defined(__unix__)
+# define TRIO_PLATFORM_UNIX
+#elif defined(TRIO_COMPILER_XLC) || defined(_AIX)
+# define TRIO_PLATFORM_UNIX
+#elif defined(TRIO_COMPILER_DECC) || defined(__osf___)
+# define TRIO_PLATFORM_UNIX
+#elif defined(__NetBSD__)
+# define TRIO_PLATFORM_UNIX
+#elif defined(__QNX__)
+# define TRIO_PLATFORM_UNIX
+# define TRIO_PLATFORM_QNX
+#elif defined(__CYGWIN__)
+# define TRIO_PLATFORM_UNIX
+#elif defined(AMIGA) && defined(TRIO_COMPILER_GCC)
+# define TRIO_PLATFORM_UNIX
+#elif defined(TRIO_COMPILER_MSVC) || defined(WIN32) || defined(_WIN32)
+# define TRIO_PLATFORM_WIN32
+#elif defined(mpeix) || defined(__mpexl)
+# define TRIO_PLATFORM_MPEIX
+#endif
+
+#if defined(_AIX)
+# define TRIO_PLATFORM_AIX
+#elif defined(__hpux)
+# define TRIO_PLATFORM_HPUX
+#elif defined(sun) || defined(__sun__)
+# if defined(__SVR4) || defined(__svr4__)
+# define TRIO_PLATFORM_SOLARIS
+# else
+# define TRIO_PLATFORM_SUNOS
+# endif
+#endif
+
+#if defined(__STDC__) || defined(TRIO_COMPILER_MSVC) || defined(TRIO_COMPILER_BCB)
+# define TRIO_COMPILER_SUPPORTS_C89
+# if defined(__STDC_VERSION__)
+# define TRIO_COMPILER_SUPPORTS_C90
+# if (__STDC_VERSION__ >= 199409L)
+# define TRIO_COMPILER_SUPPORTS_C94
+# endif
+# if (__STDC_VERSION__ >= 199901L)
+# define TRIO_COMPILER_SUPPORTS_C99
+# endif
+# elif defined(TRIO_COMPILER_SUNPRO)
+# if (__SUNPRO_C >= 0x420)
+# define TRIO_COMPILER_SUPPORTS_C94
+# endif
+# endif
+#endif
+
+#if defined(_XOPEN_SOURCE)
+# if defined(_XOPEN_SOURCE_EXTENDED)
+# define TRIO_COMPILER_SUPPORTS_UNIX95
+# endif
+# if (_XOPEN_VERSION >= 500)
+# define TRIO_COMPILER_SUPPORTS_UNIX98
+# endif
+# if (_XOPEN_VERSION >= 600)
+# define TRIO_COMPILER_SUPPORTS_UNIX01
+# endif
+#endif
+
+/*************************************************************************
+ * Generic defines
+ */
+
+#if !defined(TRIO_PUBLIC)
+# define TRIO_PUBLIC
+#endif
+#if !defined(TRIO_PRIVATE)
+# define TRIO_PRIVATE static
+#endif
+
+#if !(defined(TRIO_COMPILER_SUPPORTS_C89) || defined(__cplusplus))
+# define TRIO_COMPILER_ANCIENT
+#endif
+
+#if defined(TRIO_COMPILER_ANCIENT)
+# define TRIO_CONST
+# define TRIO_VOLATILE
+# define TRIO_SIGNED
+typedef double trio_long_double_t;
+typedef char * trio_pointer_t;
+# define TRIO_SUFFIX_LONG(x) x
+# define TRIO_PROTO(x) ()
+# define TRIO_NOARGS
+# define TRIO_ARGS1(list,a1) list a1;
+# define TRIO_ARGS2(list,a1,a2) list a1; a2;
+# define TRIO_ARGS3(list,a1,a2,a3) list a1; a2; a3;
+# define TRIO_ARGS4(list,a1,a2,a3,a4) list a1; a2; a3; a4;
+# define TRIO_ARGS5(list,a1,a2,a3,a4,a5) list a1; a2; a3; a4; a5;
+# define TRIO_ARGS6(list,a1,a2,a3,a4,a5,a6) list a1; a2; a3; a4; a5; a6;
+# define TRIO_VARGS2(list,a1,a2) list a1; a2
+# define TRIO_VARGS3(list,a1,a2,a3) list a1; a2; a3
+# define TRIO_VARGS4(list,a1,a2,a3,a4) list a1; a2; a3; a4
+# define TRIO_VARGS5(list,a1,a2,a3,a4,a5) list a1; a2; a3; a4; a5
+# define TRIO_VA_DECL va_dcl
+# define TRIO_VA_START(x,y) va_start(x)
+# define TRIO_VA_END(x) va_end(x)
+#else /* ANSI C */
+# define TRIO_CONST const
+# define TRIO_VOLATILE volatile
+# define TRIO_SIGNED signed
+typedef long double trio_long_double_t;
+typedef void * trio_pointer_t;
+# define TRIO_SUFFIX_LONG(x) x ## L
+# define TRIO_PROTO(x) x
+# define TRIO_NOARGS void
+# define TRIO_ARGS1(list,a1) (a1)
+# define TRIO_ARGS2(list,a1,a2) (a1,a2)
+# define TRIO_ARGS3(list,a1,a2,a3) (a1,a2,a3)
+# define TRIO_ARGS4(list,a1,a2,a3,a4) (a1,a2,a3,a4)
+# define TRIO_ARGS5(list,a1,a2,a3,a4,a5) (a1,a2,a3,a4,a5)
+# define TRIO_ARGS6(list,a1,a2,a3,a4,a5,a6) (a1,a2,a3,a4,a5,a6)
+# define TRIO_VARGS2 TRIO_ARGS2
+# define TRIO_VARGS3 TRIO_ARGS3
+# define TRIO_VARGS4 TRIO_ARGS4
+# define TRIO_VARGS5 TRIO_ARGS5
+# define TRIO_VA_DECL ...
+# define TRIO_VA_START(x,y) va_start(x,y)
+# define TRIO_VA_END(x) va_end(x)
+#endif
+
+#if defined(TRIO_COMPILER_SUPPORTS_C99) || defined(__cplusplus)
+# define TRIO_INLINE inline
+#elif defined(TRIO_COMPILER_GCC)
+# define TRIO_INLINE __inline__
+#elif defined(TRIO_COMPILER_MSVC)
+# define TRIO_INLINE _inline
+#elif defined(TRIO_COMPILER_BCB)
+# define TRIO_INLINE __inline
+#else
+# define TRIO_INLINE
+#endif
+
+/*************************************************************************
+ * Workarounds
+ */
+
+#if defined(TRIO_PLATFORM_VMS)
+/*
+ * Computations done with constants at compile time can trigger these
+ * even when compiling with IEEE enabled.
+ */
+# pragma message disable (UNDERFLOW, FLOATOVERFL)
+
+# if (__CRTL_VER < 80000000)
+/*
+ * Although the compiler supports C99 language constructs, the C
+ * run-time library does not contain all C99 functions.
+ *
+ * This was the case for 70300022. Update the 80000000 value when
+ * it has been accurately determined what version of the library
+ * supports C99.
+ */
+# if defined(TRIO_COMPILER_SUPPORTS_C99)
+# undef TRIO_COMPILER_SUPPORTS_C99
+# endif
+# endif
+#endif
+
+/*
+ * Not all preprocessors supports the LL token.
+ */
+#if defined(TRIO_COMPILER_BCB)
+#else
+# define TRIO_COMPILER_SUPPORTS_LL
+#endif
+
+#endif /* TRIO_TRIODEF_H */
diff --git a/libxslt/variables.c b/libxslt/variables.c
new file mode 100644
index 0000000..e1a80ee
--- /dev/null
+++ b/libxslt/variables.c
@@ -0,0 +1,2365 @@
+/*
+ * variables.c: Implementation of the variable storage and lookup
+ *
+ * Reference:
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/valid.h>
+#include <libxml/hash.h>
+#include <libxml/xmlerror.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include <libxml/parserInternals.h>
+#include <libxml/dict.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "xsltutils.h"
+#include "variables.h"
+#include "transform.h"
+#include "imports.h"
+#include "preproc.h"
+#include "keys.h"
+
+#ifdef WITH_XSLT_DEBUG
+ #define WITH_XSLT_DEBUG_VARIABLE
+#endif
+
+#ifdef XSLT_REFACTORED
+const xmlChar *xsltDocFragFake = (const xmlChar *) " fake node libxslt";
+#endif
+
+const xmlChar *xsltComputingGlobalVarMarker =
+ (const xmlChar *) " var/param being computed";
+
+#define XSLT_VAR_GLOBAL (1<<0)
+#define XSLT_VAR_IN_SELECT (1<<1)
+#define XSLT_TCTXT_VARIABLE(c) ((xsltStackElemPtr) (c)->contextVariable)
+
+/************************************************************************
+ * *
+ * Result Value Tree (Result Tree Fragment) interfaces *
+ * *
+ ************************************************************************/
+/**
+ * xsltCreateRVT:
+ * @ctxt: an XSLT transformation context
+ *
+ * Creates a Result Value Tree
+ * (the XSLT 1.0 term for this is "Result Tree Fragment")
+ *
+ * Returns the result value tree or NULL in case of API or internal errors.
+ */
+xmlDocPtr
+xsltCreateRVT(xsltTransformContextPtr ctxt)
+{
+ xmlDocPtr container;
+
+ /*
+ * Question: Why is this function public?
+ * Answer: It is called by the EXSLT module.
+ */
+ if (ctxt == NULL)
+ return(NULL);
+
+ /*
+ * Reuse a RTF from the cache if available.
+ */
+ if (ctxt->cache->RVT) {
+ container = ctxt->cache->RVT;
+ ctxt->cache->RVT = (xmlDocPtr) container->next;
+ /* clear the internal pointers */
+ container->next = NULL;
+ container->prev = NULL;
+ if (ctxt->cache->nbRVT > 0)
+ ctxt->cache->nbRVT--;
+#ifdef XSLT_DEBUG_PROFILE_CACHE
+ ctxt->cache->dbgReusedRVTs++;
+#endif
+ return(container);
+ }
+
+ container = xmlNewDoc(NULL);
+ if (container == NULL)
+ return(NULL);
+ container->dict = ctxt->dict;
+ xmlDictReference(container->dict);
+ XSLT_MARK_RES_TREE_FRAG(container);
+ container->doc = container;
+ container->parent = NULL;
+ return(container);
+}
+
+/**
+ * xsltRegisterTmpRVT:
+ * @ctxt: an XSLT transformation context
+ * @RVT: a result value tree (Result Tree Fragment)
+ *
+ * Registers the result value tree (XSLT 1.0 term: Result Tree Fragment)
+ * in the garbage collector.
+ * The fragment will be freed at the exit of the currently
+ * instantiated xsl:template.
+ * Obsolete; this function might produce massive memory overhead,
+ * since the fragment is only freed when the current xsl:template
+ * exits. Use xsltRegisterLocalRVT() instead.
+ *
+ * Returns 0 in case of success and -1 in case of API or internal errors.
+ */
+int
+xsltRegisterTmpRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
+{
+ if ((ctxt == NULL) || (RVT == NULL))
+ return(-1);
+
+ RVT->prev = NULL;
+ RVT->psvi = XSLT_RVT_VARIABLE;
+
+ /*
+ * We'll restrict the lifetime of user-created fragments
+ * insinde an xsl:variable and xsl:param to the lifetime of the
+ * var/param itself.
+ */
+ if (ctxt->contextVariable != NULL) {
+ RVT->next = (xmlNodePtr) XSLT_TCTXT_VARIABLE(ctxt)->fragment;
+ XSLT_TCTXT_VARIABLE(ctxt)->fragment = RVT;
+ return(0);
+ }
+
+ RVT->next = (xmlNodePtr) ctxt->tmpRVT;
+ if (ctxt->tmpRVT != NULL)
+ ctxt->tmpRVT->prev = (xmlNodePtr) RVT;
+ ctxt->tmpRVT = RVT;
+ return(0);
+}
+
+/**
+ * xsltRegisterLocalRVT:
+ * @ctxt: an XSLT transformation context
+ * @RVT: a result value tree (Result Tree Fragment; xmlDocPtr)
+ *
+ * Registers a result value tree (XSLT 1.0 term: Result Tree Fragment)
+ * in the RVT garbage collector.
+ * The fragment will be freed when the instruction which created the
+ * fragment exits.
+ *
+ * Returns 0 in case of success and -1 in case of API or internal errors.
+ */
+int
+xsltRegisterLocalRVT(xsltTransformContextPtr ctxt,
+ xmlDocPtr RVT)
+{
+ if ((ctxt == NULL) || (RVT == NULL))
+ return(-1);
+
+ RVT->prev = NULL;
+
+ /*
+ * When evaluating "select" expressions of xsl:variable
+ * and xsl:param, we need to bind newly created tree fragments
+ * to the variable itself; otherwise the fragment will be
+ * freed before we leave the scope of a var.
+ */
+ if ((ctxt->contextVariable != NULL) &&
+ (XSLT_TCTXT_VARIABLE(ctxt)->flags & XSLT_VAR_IN_SELECT))
+ {
+ RVT->psvi = XSLT_RVT_VARIABLE;
+ RVT->next = (xmlNodePtr) XSLT_TCTXT_VARIABLE(ctxt)->fragment;
+ XSLT_TCTXT_VARIABLE(ctxt)->fragment = RVT;
+ return(0);
+ }
+ /*
+ * Store the fragment in the scope of the current instruction.
+ * If not reference by a returning instruction (like EXSLT's function),
+ * then this fragment will be freed, when the instruction exits.
+ */
+ RVT->psvi = XSLT_RVT_LOCAL;
+ RVT->next = (xmlNodePtr) ctxt->localRVT;
+ if (ctxt->localRVT != NULL)
+ ctxt->localRVT->prev = (xmlNodePtr) RVT;
+ ctxt->localRVT = RVT;
+ return(0);
+}
+
+/**
+ * xsltExtensionInstructionResultFinalize:
+ * @ctxt: an XSLT transformation context
+ *
+ * Finalizes the data (e.g. result tree fragments) created
+ * within a value-returning process (e.g. EXSLT's function).
+ * Tree fragments marked as being returned by a function are
+ * set to normal state, which means that the fragment garbage
+ * collector will free them after the function-calling process exits.
+ *
+ * Returns 0 in case of success and -1 in case of API or internal errors.
+ *
+ * This function is unsupported in newer releases of libxslt.
+ */
+int
+xsltExtensionInstructionResultFinalize(xsltTransformContextPtr ctxt)
+{
+ xmlGenericError(xmlGenericErrorContext,
+ "xsltExtensionInstructionResultFinalize is unsupported "
+ "in this release of libxslt.\n");
+ return(-1);
+}
+
+/**
+ * xsltExtensionInstructionResultRegister:
+ * @ctxt: an XSLT transformation context
+ * @obj: an XPath object to be inspected for result tree fragments
+ *
+ * Marks the result of a value-returning extension instruction
+ * in order to avoid it being garbage collected before the
+ * extension instruction exits.
+ * Note that one still has to additionally register any newly created
+ * tree fragments (via xsltCreateRVT()) with xsltRegisterLocalRVT().
+ *
+ * Returns 0 in case of success and -1 in case of error.
+ *
+ * It isn't necessary to call this function in newer releases of
+ * libxslt.
+ */
+int
+xsltExtensionInstructionResultRegister(xsltTransformContextPtr ctxt,
+ xmlXPathObjectPtr obj)
+{
+ return(0);
+}
+
+/**
+ * xsltFlagRVTs:
+ * @ctxt: an XSLT transformation context
+ * @obj: an XPath object to be inspected for result tree fragments
+ * @val: the flag value
+ *
+ * Updates ownership information of RVTs in @obj according to @val.
+ *
+ * @val = XSLT_RVT_FUNC_RESULT for the result of an extension function, so its
+ * RVTs won't be destroyed after leaving the returning scope.
+ * @val = XSLT_RVT_LOCAL for the result of an extension function to reset
+ * the state of its RVTs after it was returned to a new scope.
+ * @val = XSLT_RVT_GLOBAL for parts of global variables.
+ *
+ * Returns 0 in case of success and -1 in case of error.
+ */
+int
+xsltFlagRVTs(xsltTransformContextPtr ctxt, xmlXPathObjectPtr obj, void *val) {
+ int i;
+ xmlNodePtr cur;
+ xmlDocPtr doc;
+
+ if ((ctxt == NULL) || (obj == NULL))
+ return(-1);
+
+ /*
+ * OPTIMIZE TODO: If no local variables/params and no local tree
+ * fragments were created, then we don't need to analyse the XPath
+ * objects for tree fragments.
+ */
+
+ if ((obj->type != XPATH_NODESET) && (obj->type != XPATH_XSLT_TREE))
+ return(0);
+ if ((obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0))
+ return(0);
+
+ for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+ cur = obj->nodesetval->nodeTab[i];
+ if (cur->type == XML_NAMESPACE_DECL) {
+ /*
+ * The XPath module sets the owner element of a ns-node on
+ * the ns->next field.
+ */
+ if ((((xmlNsPtr) cur)->next != NULL) &&
+ (((xmlNsPtr) cur)->next->type == XML_ELEMENT_NODE))
+ {
+ cur = (xmlNodePtr) ((xmlNsPtr) cur)->next;
+ doc = cur->doc;
+ } else {
+ xsltTransformError(ctxt, NULL, ctxt->inst,
+ "Internal error in xsltFlagRVTs(): "
+ "Cannot retrieve the doc of a namespace node.\n");
+ return(-1);
+ }
+ } else {
+ doc = cur->doc;
+ }
+ if (doc == NULL) {
+ xsltTransformError(ctxt, NULL, ctxt->inst,
+ "Internal error in xsltFlagRVTs(): "
+ "Cannot retrieve the doc of a node.\n");
+ return(-1);
+ }
+ if (doc->name && (doc->name[0] == ' ') &&
+ doc->psvi != XSLT_RVT_GLOBAL) {
+ /*
+ * This is a result tree fragment.
+ * We store ownership information in the @psvi field.
+ * TODO: How do we know if this is a doc acquired via the
+ * document() function?
+ */
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "Flagging RVT %p: %p -> %p\n", doc, doc->psvi, val));
+#endif
+
+ if (val == XSLT_RVT_LOCAL) {
+ if (doc->psvi != XSLT_RVT_FUNC_RESULT) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xsltFlagRVTs: Invalid transition %p => LOCAL\n",
+ doc->psvi);
+ return(-1);
+ }
+
+ xsltRegisterLocalRVT(ctxt, doc);
+ } else if (val == XSLT_RVT_GLOBAL) {
+ if (doc->psvi != XSLT_RVT_LOCAL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xsltFlagRVTs: Invalid transition %p => GLOBAL\n",
+ doc->psvi);
+ doc->psvi = XSLT_RVT_GLOBAL;
+ return(-1);
+ }
+
+ /* Will be registered as persistant in xsltReleaseLocalRVTs. */
+ doc->psvi = XSLT_RVT_GLOBAL;
+ } else if (val == XSLT_RVT_FUNC_RESULT) {
+ doc->psvi = val;
+ }
+ }
+ }
+
+ return(0);
+}
+
+/**
+ * xsltReleaseRVT:
+ * @ctxt: an XSLT transformation context
+ * @RVT: a result value tree (Result Tree Fragment)
+ *
+ * Either frees the RVT (which is an xmlDoc) or stores
+ * it in the context's cache for later reuse.
+ */
+void
+xsltReleaseRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
+{
+ if (RVT == NULL)
+ return;
+
+ if (ctxt && (ctxt->cache->nbRVT < 40)) {
+ /*
+ * Store the Result Tree Fragment.
+ * Free the document info.
+ */
+ if (RVT->_private != NULL) {
+ xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private);
+ xmlFree(RVT->_private);
+ RVT->_private = NULL;
+ }
+ /*
+ * Clear the document tree.
+ * REVISIT TODO: Do we expect ID/IDREF tables to be existent?
+ */
+ if (RVT->children != NULL) {
+ xmlFreeNodeList(RVT->children);
+ RVT->children = NULL;
+ RVT->last = NULL;
+ }
+ if (RVT->ids != NULL) {
+ xmlFreeIDTable((xmlIDTablePtr) RVT->ids);
+ RVT->ids = NULL;
+ }
+ if (RVT->refs != NULL) {
+ xmlFreeRefTable((xmlRefTablePtr) RVT->refs);
+ RVT->refs = NULL;
+ }
+
+ /*
+ * Reset the ownership information.
+ */
+ RVT->psvi = NULL;
+
+ RVT->next = (xmlNodePtr) ctxt->cache->RVT;
+ ctxt->cache->RVT = RVT;
+
+ ctxt->cache->nbRVT++;
+
+#ifdef XSLT_DEBUG_PROFILE_CACHE
+ ctxt->cache->dbgCachedRVTs++;
+#endif
+ return;
+ }
+ /*
+ * Free it.
+ */
+ if (RVT->_private != NULL) {
+ xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private);
+ xmlFree(RVT->_private);
+ }
+ xmlFreeDoc(RVT);
+}
+
+/**
+ * xsltRegisterPersistRVT:
+ * @ctxt: an XSLT transformation context
+ * @RVT: a result value tree (Result Tree Fragment)
+ *
+ * Register the result value tree (XSLT 1.0 term: Result Tree Fragment)
+ * in the fragment garbage collector.
+ * The fragment will be freed when the transformation context is
+ * freed.
+ *
+ * Returns 0 in case of success and -1 in case of error.
+ */
+int
+xsltRegisterPersistRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
+{
+ if ((ctxt == NULL) || (RVT == NULL)) return(-1);
+
+ RVT->psvi = XSLT_RVT_GLOBAL;
+ RVT->prev = NULL;
+ RVT->next = (xmlNodePtr) ctxt->persistRVT;
+ if (ctxt->persistRVT != NULL)
+ ctxt->persistRVT->prev = (xmlNodePtr) RVT;
+ ctxt->persistRVT = RVT;
+ return(0);
+}
+
+/**
+ * xsltFreeRVTs:
+ * @ctxt: an XSLT transformation context
+ *
+ * Frees all registered result value trees (Result Tree Fragments)
+ * of the transformation. Internal function; should not be called
+ * by user-code.
+ */
+void
+xsltFreeRVTs(xsltTransformContextPtr ctxt)
+{
+ xmlDocPtr cur, next;
+
+ if (ctxt == NULL)
+ return;
+ /*
+ * Local fragments.
+ */
+ cur = ctxt->localRVT;
+ while (cur != NULL) {
+ next = (xmlDocPtr) cur->next;
+ if (cur->_private != NULL) {
+ xsltFreeDocumentKeys(cur->_private);
+ xmlFree(cur->_private);
+ }
+ xmlFreeDoc(cur);
+ cur = next;
+ }
+ ctxt->localRVT = NULL;
+ /*
+ * User-created per-template fragments.
+ */
+ cur = ctxt->tmpRVT;
+ while (cur != NULL) {
+ next = (xmlDocPtr) cur->next;
+ if (cur->_private != NULL) {
+ xsltFreeDocumentKeys(cur->_private);
+ xmlFree(cur->_private);
+ }
+ xmlFreeDoc(cur);
+ cur = next;
+ }
+ ctxt->tmpRVT = NULL;
+ /*
+ * Global fragments.
+ */
+ cur = ctxt->persistRVT;
+ while (cur != NULL) {
+ next = (xmlDocPtr) cur->next;
+ if (cur->_private != NULL) {
+ xsltFreeDocumentKeys(cur->_private);
+ xmlFree(cur->_private);
+ }
+ xmlFreeDoc(cur);
+ cur = next;
+ }
+ ctxt->persistRVT = NULL;
+}
+
+/************************************************************************
+ * *
+ * Module interfaces *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltNewStackElem:
+ *
+ * Create a new XSLT ParserContext
+ *
+ * Returns the newly allocated xsltParserStackElem or NULL in case of error
+ */
+static xsltStackElemPtr
+xsltNewStackElem(xsltTransformContextPtr ctxt)
+{
+ xsltStackElemPtr ret;
+ /*
+ * Reuse a stack item from the cache if available.
+ */
+ if (ctxt && ctxt->cache->stackItems) {
+ ret = ctxt->cache->stackItems;
+ ctxt->cache->stackItems = ret->next;
+ ret->next = NULL;
+ ctxt->cache->nbStackItems--;
+#ifdef XSLT_DEBUG_PROFILE_CACHE
+ ctxt->cache->dbgReusedVars++;
+#endif
+ return(ret);
+ }
+ ret = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem));
+ if (ret == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltNewStackElem : malloc failed\n");
+ return(NULL);
+ }
+ memset(ret, 0, sizeof(xsltStackElem));
+ ret->context = ctxt;
+ return(ret);
+}
+
+/**
+ * xsltCopyStackElem:
+ * @elem: an XSLT stack element
+ *
+ * Makes a copy of the stack element
+ *
+ * Returns the copy of NULL
+ */
+static xsltStackElemPtr
+xsltCopyStackElem(xsltStackElemPtr elem) {
+ xsltStackElemPtr cur;
+
+ cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem));
+ if (cur == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCopyStackElem : malloc failed\n");
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltStackElem));
+ cur->context = elem->context;
+ cur->name = elem->name;
+ cur->nameURI = elem->nameURI;
+ cur->select = elem->select;
+ cur->tree = elem->tree;
+ cur->comp = elem->comp;
+ return(cur);
+}
+
+/**
+ * xsltFreeStackElem:
+ * @elem: an XSLT stack element
+ *
+ * Free up the memory allocated by @elem
+ */
+static void
+xsltFreeStackElem(xsltStackElemPtr elem) {
+ if (elem == NULL)
+ return;
+ if (elem->value != NULL)
+ xmlXPathFreeObject(elem->value);
+ /*
+ * Release the list of temporary Result Tree Fragments.
+ */
+ if (elem->context) {
+ xmlDocPtr cur;
+
+ while (elem->fragment != NULL) {
+ cur = elem->fragment;
+ elem->fragment = (xmlDocPtr) cur->next;
+
+ if (cur->psvi == XSLT_RVT_VARIABLE) {
+ xsltReleaseRVT((xsltTransformContextPtr) elem->context,
+ cur);
+ } else if (cur->psvi != XSLT_RVT_FUNC_RESULT) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xsltFreeStackElem: Unexpected RVT flag %p\n",
+ cur->psvi);
+ }
+ }
+ }
+ /*
+ * Cache or free the variable structure.
+ */
+ if (elem->context && (elem->context->cache->nbStackItems < 50)) {
+ /*
+ * Store the item in the cache.
+ */
+ xsltTransformContextPtr ctxt = elem->context;
+ memset(elem, 0, sizeof(xsltStackElem));
+ elem->context = ctxt;
+ elem->next = ctxt->cache->stackItems;
+ ctxt->cache->stackItems = elem;
+ ctxt->cache->nbStackItems++;
+#ifdef XSLT_DEBUG_PROFILE_CACHE
+ ctxt->cache->dbgCachedVars++;
+#endif
+ return;
+ }
+ xmlFree(elem);
+}
+
+/**
+ * xsltFreeStackElemList:
+ * @elem: an XSLT stack element
+ *
+ * Free up the memory allocated by @elem
+ */
+void
+xsltFreeStackElemList(xsltStackElemPtr elem) {
+ xsltStackElemPtr next;
+
+ while (elem != NULL) {
+ next = elem->next;
+ xsltFreeStackElem(elem);
+ elem = next;
+ }
+}
+
+/**
+ * xsltStackLookup:
+ * @ctxt: an XSLT transformation context
+ * @name: the local part of the name
+ * @nameURI: the URI part of the name
+ *
+ * Locate an element in the stack based on its name.
+ */
+#if 0 /* TODO: Those seem to have been used for debugging. */
+static int stack_addr = 0;
+static int stack_cmp = 0;
+#endif
+
+static xsltStackElemPtr
+xsltStackLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
+ const xmlChar *nameURI) {
+ int i;
+ xsltStackElemPtr cur;
+
+ if ((ctxt == NULL) || (name == NULL) || (ctxt->varsNr == 0))
+ return(NULL);
+
+ /*
+ * Do the lookup from the top of the stack, but
+ * don't use params being computed in a call-param
+ * First lookup expects the variable name and URI to
+ * come from the disctionnary and hence pointer comparison.
+ */
+ for (i = ctxt->varsNr; i > ctxt->varsBase; i--) {
+ cur = ctxt->varsTab[i-1];
+ while (cur != NULL) {
+ if ((cur->name == name) && (cur->nameURI == nameURI)) {
+#if 0
+ stack_addr++;
+#endif
+ return(cur);
+ }
+ cur = cur->next;
+ }
+ }
+
+ /*
+ * Redo the lookup with interned string compares
+ * to avoid string compares.
+ */
+ name = xmlDictLookup(ctxt->dict, name, -1);
+ if (nameURI != NULL)
+ nameURI = xmlDictLookup(ctxt->dict, nameURI, -1);
+
+ for (i = ctxt->varsNr; i > ctxt->varsBase; i--) {
+ cur = ctxt->varsTab[i-1];
+ while (cur != NULL) {
+ if ((cur->name == name) && (cur->nameURI == nameURI)) {
+#if 0
+ stack_cmp++;
+#endif
+ return(cur);
+ }
+ cur = cur->next;
+ }
+ }
+
+ return(NULL);
+}
+
+#ifdef XSLT_REFACTORED
+#else
+
+/**
+ * xsltCheckStackElem:
+ * @ctxt: xn XSLT transformation context
+ * @name: the variable name
+ * @nameURI: the variable namespace URI
+ *
+ * Checks whether a variable or param is already defined.
+ *
+ * URGENT TODO: Checks for redefinition of vars/params should be
+ * done only at compilation time.
+ *
+ * Returns 1 if variable is present, 2 if param is present, 3 if this
+ * is an inherited param, 0 if not found, -1 in case of failure.
+ */
+static int
+xsltCheckStackElem(xsltTransformContextPtr ctxt, const xmlChar *name,
+ const xmlChar *nameURI) {
+ xsltStackElemPtr cur;
+
+ if ((ctxt == NULL) || (name == NULL))
+ return(-1);
+
+ cur = xsltStackLookup(ctxt, name, nameURI);
+ if (cur == NULL)
+ return(0);
+ if (cur->comp != NULL) {
+ if (cur->comp->type == XSLT_FUNC_WITHPARAM)
+ return(3);
+ else if (cur->comp->type == XSLT_FUNC_PARAM)
+ return(2);
+ }
+
+ return(1);
+}
+
+#endif /* XSLT_REFACTORED */
+
+/**
+ * xsltAddStackElem:
+ * @ctxt: xn XSLT transformation context
+ * @elem: a stack element
+ *
+ * Push an element (or list) onto the stack.
+ * In case of a list, each member will be pushed into
+ * a seperate slot; i.e. there's always 1 stack entry for
+ * 1 stack element.
+ *
+ * Returns 0 in case of success, -1 in case of failure.
+ */
+static int
+xsltAddStackElem(xsltTransformContextPtr ctxt, xsltStackElemPtr elem)
+{
+ if ((ctxt == NULL) || (elem == NULL))
+ return(-1);
+
+ do {
+ if (ctxt->varsMax == 0) {
+ ctxt->varsMax = 10;
+ ctxt->varsTab =
+ (xsltStackElemPtr *) xmlMalloc(ctxt->varsMax *
+ sizeof(ctxt->varsTab[0]));
+ if (ctxt->varsTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
+ return (-1);
+ }
+ }
+ if (ctxt->varsNr >= ctxt->varsMax) {
+ ctxt->varsMax *= 2;
+ ctxt->varsTab =
+ (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab,
+ ctxt->varsMax *
+ sizeof(ctxt->varsTab[0]));
+ if (ctxt->varsTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
+ return (-1);
+ }
+ }
+ ctxt->varsTab[ctxt->varsNr++] = elem;
+ ctxt->vars = elem;
+
+ elem = elem->next;
+ } while (elem != NULL);
+
+ return(0);
+}
+
+/**
+ * xsltAddStackElemList:
+ * @ctxt: xn XSLT transformation context
+ * @elems: a stack element list
+ *
+ * Push an element list onto the stack.
+ *
+ * Returns 0 in case of success, -1 in case of failure.
+ */
+int
+xsltAddStackElemList(xsltTransformContextPtr ctxt, xsltStackElemPtr elems)
+{
+ return(xsltAddStackElem(ctxt, elems));
+}
+
+/************************************************************************
+ * *
+ * Module interfaces *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltEvalVariable:
+ * @ctxt: the XSLT transformation context
+ * @variable: the variable or parameter item
+ * @comp: the compiled XSLT instruction
+ *
+ * Evaluate a variable value.
+ *
+ * Returns the XPath Object value or NULL in case of error
+ */
+static xmlXPathObjectPtr
+xsltEvalVariable(xsltTransformContextPtr ctxt, xsltStackElemPtr variable,
+ xsltStylePreCompPtr castedComp)
+{
+#ifdef XSLT_REFACTORED
+ xsltStyleItemVariablePtr comp =
+ (xsltStyleItemVariablePtr) castedComp;
+#else
+ xsltStylePreCompPtr comp = castedComp;
+#endif
+ xmlXPathObjectPtr result = NULL;
+ xmlNodePtr oldInst;
+
+ if ((ctxt == NULL) || (variable == NULL))
+ return(NULL);
+
+ /*
+ * A variable or parameter are evaluated on demand; thus the
+ * context (of XSLT and XPath) need to be temporarily adjusted and
+ * restored on exit.
+ */
+ oldInst = ctxt->inst;
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "Evaluating variable '%s'\n", variable->name));
+#endif
+ if (variable->select != NULL) {
+ xmlXPathCompExprPtr xpExpr = NULL;
+ xmlDocPtr oldXPDoc;
+ xmlNodePtr oldXPContextNode;
+ int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
+ xmlNsPtr *oldXPNamespaces;
+ xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
+ xsltStackElemPtr oldVar = ctxt->contextVariable;
+
+ if ((comp != NULL) && (comp->comp != NULL)) {
+ xpExpr = comp->comp;
+ } else {
+ xpExpr = xmlXPathCompile(variable->select);
+ }
+ if (xpExpr == NULL)
+ return(NULL);
+ /*
+ * Save context states.
+ */
+ oldXPDoc = xpctxt->doc;
+ oldXPContextNode = xpctxt->node;
+ oldXPProximityPosition = xpctxt->proximityPosition;
+ oldXPContextSize = xpctxt->contextSize;
+ oldXPNamespaces = xpctxt->namespaces;
+ oldXPNsNr = xpctxt->nsNr;
+
+ xpctxt->node = ctxt->node;
+ /*
+ * OPTIMIZE TODO: Lame try to set the context doc.
+ * Get rid of this somehow in xpath.c.
+ */
+ if ((ctxt->node->type != XML_NAMESPACE_DECL) &&
+ ctxt->node->doc)
+ xpctxt->doc = ctxt->node->doc;
+ /*
+ * BUG TODO: The proximity position and the context size will
+ * potentially be wrong.
+ * Example:
+ * <xsl:template select="foo">
+ * <xsl:variable name="pos" select="position()"/>
+ * <xsl:for-each select="bar">
+ * <xsl:value-of select="$pos"/>
+ * </xsl:for-each>
+ * </xsl:template>
+ * Here the proximity position and context size are changed
+ * to the context of <xsl:for-each select="bar">, but
+ * the variable needs to be evaluated in the context of
+ * <xsl:template select="foo">.
+ */
+ if (comp != NULL) {
+
+#ifdef XSLT_REFACTORED
+ if (comp->inScopeNs != NULL) {
+ xpctxt->namespaces = comp->inScopeNs->list;
+ xpctxt->nsNr = comp->inScopeNs->xpathNumber;
+ } else {
+ xpctxt->namespaces = NULL;
+ xpctxt->nsNr = 0;
+ }
+#else
+ xpctxt->namespaces = comp->nsList;
+ xpctxt->nsNr = comp->nsNr;
+#endif
+ } else {
+ xpctxt->namespaces = NULL;
+ xpctxt->nsNr = 0;
+ }
+
+ /*
+ * We need to mark that we are "selecting" a var's value;
+ * if any tree fragments are created inside the expression,
+ * then those need to be stored inside the variable; otherwise
+ * we'll eventually free still referenced fragments, before
+ * we leave the scope of the variable.
+ */
+ ctxt->contextVariable = variable;
+ variable->flags |= XSLT_VAR_IN_SELECT;
+
+ result = xmlXPathCompiledEval(xpExpr, xpctxt);
+
+ variable->flags ^= XSLT_VAR_IN_SELECT;
+ /*
+ * Restore Context states.
+ */
+ ctxt->contextVariable = oldVar;
+
+ xpctxt->doc = oldXPDoc;
+ xpctxt->node = oldXPContextNode;
+ xpctxt->contextSize = oldXPContextSize;
+ xpctxt->proximityPosition = oldXPProximityPosition;
+ xpctxt->namespaces = oldXPNamespaces;
+ xpctxt->nsNr = oldXPNsNr;
+
+ if ((comp == NULL) || (comp->comp == NULL))
+ xmlXPathFreeCompExpr(xpExpr);
+ if (result == NULL) {
+ xsltTransformError(ctxt, NULL,
+ (comp != NULL) ? comp->inst : NULL,
+ "Failed to evaluate the expression of variable '%s'.\n",
+ variable->name);
+ ctxt->state = XSLT_STATE_STOPPED;
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+#ifdef LIBXML_DEBUG_ENABLED
+ } else {
+ if ((xsltGenericDebugContext == stdout) ||
+ (xsltGenericDebugContext == stderr))
+ xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
+ result, 0);
+#endif
+#endif
+ }
+ } else {
+ if (variable->tree == NULL) {
+ result = xmlXPathNewCString("");
+ } else {
+ if (variable->tree) {
+ xmlDocPtr container;
+ xmlNodePtr oldInsert;
+ xmlDocPtr oldOutput;
+ xsltStackElemPtr oldVar = ctxt->contextVariable;
+
+ /*
+ * Generate a result tree fragment.
+ */
+ container = xsltCreateRVT(ctxt);
+ if (container == NULL)
+ goto error;
+ /*
+ * NOTE: Local Result Tree Fragments of params/variables
+ * are not registered globally anymore; the life-time
+ * is not directly dependant of the param/variable itself.
+ *
+ * OLD: xsltRegisterTmpRVT(ctxt, container);
+ */
+ /*
+ * Attach the Result Tree Fragment to the variable;
+ * when the variable is freed, it will also free
+ * the Result Tree Fragment.
+ */
+ variable->fragment = container;
+ container->psvi = XSLT_RVT_VARIABLE;
+
+ oldOutput = ctxt->output;
+ oldInsert = ctxt->insert;
+
+ ctxt->output = container;
+ ctxt->insert = (xmlNodePtr) container;
+ ctxt->contextVariable = variable;
+ /*
+ * Process the sequence constructor (variable->tree).
+ * The resulting tree will be held by @container.
+ */
+ xsltApplyOneTemplate(ctxt, ctxt->node, variable->tree,
+ NULL, NULL);
+
+ ctxt->contextVariable = oldVar;
+ ctxt->insert = oldInsert;
+ ctxt->output = oldOutput;
+
+ result = xmlXPathNewValueTree((xmlNodePtr) container);
+ }
+ if (result == NULL) {
+ result = xmlXPathNewCString("");
+ } else {
+ /*
+ * Freeing is not handled there anymore.
+ * QUESTION TODO: What does the above comment mean?
+ */
+ result->boolval = 0;
+ }
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+#ifdef LIBXML_DEBUG_ENABLED
+
+ if ((xsltGenericDebugContext == stdout) ||
+ (xsltGenericDebugContext == stderr))
+ xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
+ result, 0);
+#endif
+#endif
+ }
+ }
+
+error:
+ ctxt->inst = oldInst;
+ return(result);
+}
+
+/**
+ * xsltEvalGlobalVariable:
+ * @elem: the variable or parameter
+ * @ctxt: the XSLT transformation context
+ *
+ * Evaluates a the value of a global xsl:variable or
+ * xsl:param declaration.
+ *
+ * Returns the XPath Object value or NULL in case of error
+ */
+static xmlXPathObjectPtr
+xsltEvalGlobalVariable(xsltStackElemPtr elem, xsltTransformContextPtr ctxt)
+{
+ xmlXPathObjectPtr result = NULL;
+ xmlNodePtr oldInst;
+ const xmlChar* oldVarName;
+
+#ifdef XSLT_REFACTORED
+ xsltStyleBasicItemVariablePtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((ctxt == NULL) || (elem == NULL))
+ return(NULL);
+ if (elem->computed)
+ return(elem->value);
+
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "Evaluating global variable %s\n", elem->name));
+#endif
+
+#ifdef WITH_DEBUGGER
+ if ((ctxt->debugStatus != XSLT_DEBUG_NONE) &&
+ elem->comp && elem->comp->inst)
+ xslHandleDebugger(elem->comp->inst, NULL, NULL, ctxt);
+#endif
+
+ oldInst = ctxt->inst;
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleBasicItemVariablePtr) elem->comp;
+#else
+ comp = elem->comp;
+#endif
+ oldVarName = elem->name;
+ elem->name = xsltComputingGlobalVarMarker;
+ /*
+ * OPTIMIZE TODO: We should consider instantiating global vars/params
+ * on-demand. The vars/params don't need to be evaluated if never
+ * called; and in the case of global params, if values for such params
+ * are provided by the user.
+ */
+ if (elem->select != NULL) {
+ xmlXPathCompExprPtr xpExpr = NULL;
+ xmlDocPtr oldXPDoc;
+ xmlNodePtr oldXPContextNode;
+ int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
+ xmlNsPtr *oldXPNamespaces;
+ xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
+
+ if ((comp != NULL) && (comp->comp != NULL)) {
+ xpExpr = comp->comp;
+ } else {
+ xpExpr = xmlXPathCompile(elem->select);
+ }
+ if (xpExpr == NULL)
+ goto error;
+
+
+ if (comp != NULL)
+ ctxt->inst = comp->inst;
+ else
+ ctxt->inst = NULL;
+ /*
+ * SPEC XSLT 1.0:
+ * "At top-level, the expression or template specifying the
+ * variable value is evaluated with the same context as that used
+ * to process the root node of the source document: the current
+ * node is the root node of the source document and the current
+ * node list is a list containing just the root node of the source
+ * document."
+ */
+ /*
+ * Save context states.
+ */
+ oldXPDoc = xpctxt->doc;
+ oldXPContextNode = xpctxt->node;
+ oldXPProximityPosition = xpctxt->proximityPosition;
+ oldXPContextSize = xpctxt->contextSize;
+ oldXPNamespaces = xpctxt->namespaces;
+ oldXPNsNr = xpctxt->nsNr;
+
+ xpctxt->node = ctxt->initialContextNode;
+ xpctxt->doc = ctxt->initialContextDoc;
+ xpctxt->contextSize = 1;
+ xpctxt->proximityPosition = 1;
+
+ if (comp != NULL) {
+
+#ifdef XSLT_REFACTORED
+ if (comp->inScopeNs != NULL) {
+ xpctxt->namespaces = comp->inScopeNs->list;
+ xpctxt->nsNr = comp->inScopeNs->xpathNumber;
+ } else {
+ xpctxt->namespaces = NULL;
+ xpctxt->nsNr = 0;
+ }
+#else
+ xpctxt->namespaces = comp->nsList;
+ xpctxt->nsNr = comp->nsNr;
+#endif
+ } else {
+ xpctxt->namespaces = NULL;
+ xpctxt->nsNr = 0;
+ }
+
+ result = xmlXPathCompiledEval(xpExpr, xpctxt);
+
+ /*
+ * Restore Context states.
+ */
+ xpctxt->doc = oldXPDoc;
+ xpctxt->node = oldXPContextNode;
+ xpctxt->contextSize = oldXPContextSize;
+ xpctxt->proximityPosition = oldXPProximityPosition;
+ xpctxt->namespaces = oldXPNamespaces;
+ xpctxt->nsNr = oldXPNsNr;
+
+ if ((comp == NULL) || (comp->comp == NULL))
+ xmlXPathFreeCompExpr(xpExpr);
+ if (result == NULL) {
+ if (comp == NULL)
+ xsltTransformError(ctxt, NULL, NULL,
+ "Evaluating global variable %s failed\n", elem->name);
+ else
+ xsltTransformError(ctxt, NULL, comp->inst,
+ "Evaluating global variable %s failed\n", elem->name);
+ ctxt->state = XSLT_STATE_STOPPED;
+ goto error;
+ }
+
+ /*
+ * Mark all RVTs that are referenced from result as part
+ * of this variable so they won't be freed too early.
+ */
+ xsltFlagRVTs(ctxt, result, XSLT_RVT_GLOBAL);
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+#ifdef LIBXML_DEBUG_ENABLED
+ if ((xsltGenericDebugContext == stdout) ||
+ (xsltGenericDebugContext == stderr))
+ xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
+ result, 0);
+#endif
+#endif
+ } else {
+ if (elem->tree == NULL) {
+ result = xmlXPathNewCString("");
+ } else {
+ xmlDocPtr container;
+ xmlNodePtr oldInsert;
+ xmlDocPtr oldOutput, oldXPDoc;
+ /*
+ * Generate a result tree fragment.
+ */
+ container = xsltCreateRVT(ctxt);
+ if (container == NULL)
+ goto error;
+ /*
+ * Let the lifetime of the tree fragment be handled by
+ * the Libxslt's garbage collector.
+ */
+ xsltRegisterPersistRVT(ctxt, container);
+
+ oldOutput = ctxt->output;
+ oldInsert = ctxt->insert;
+
+ oldXPDoc = ctxt->xpathCtxt->doc;
+
+ ctxt->output = container;
+ ctxt->insert = (xmlNodePtr) container;
+
+ ctxt->xpathCtxt->doc = ctxt->initialContextDoc;
+ /*
+ * Process the sequence constructor.
+ */
+ xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, NULL, NULL);
+
+ ctxt->xpathCtxt->doc = oldXPDoc;
+
+ ctxt->insert = oldInsert;
+ ctxt->output = oldOutput;
+
+ result = xmlXPathNewValueTree((xmlNodePtr) container);
+ if (result == NULL) {
+ result = xmlXPathNewCString("");
+ } else {
+ result->boolval = 0; /* Freeing is not handled there anymore */
+ }
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+#ifdef LIBXML_DEBUG_ENABLED
+ if ((xsltGenericDebugContext == stdout) ||
+ (xsltGenericDebugContext == stderr))
+ xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
+ result, 0);
+#endif
+#endif
+ }
+ }
+
+error:
+ elem->name = oldVarName;
+ ctxt->inst = oldInst;
+ if (result != NULL) {
+ elem->value = result;
+ elem->computed = 1;
+ }
+ return(result);
+}
+
+/**
+ * xsltEvalGlobalVariables:
+ * @ctxt: the XSLT transformation context
+ *
+ * Evaluates all global variables and parameters of a stylesheet.
+ * For internal use only. This is called at start of a transformation.
+ *
+ * Returns 0 in case of success, -1 in case of error
+ */
+int
+xsltEvalGlobalVariables(xsltTransformContextPtr ctxt) {
+ xsltStackElemPtr elem;
+ xsltStylesheetPtr style;
+
+ if ((ctxt == NULL) || (ctxt->document == NULL))
+ return(-1);
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "Registering global variables\n"));
+#endif
+ /*
+ * Walk the list from the stylesheets and populate the hash table
+ */
+ style = ctxt->style;
+ while (style != NULL) {
+ elem = style->variables;
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ if ((style->doc != NULL) && (style->doc->URL != NULL)) {
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "Registering global variables from %s\n",
+ style->doc->URL));
+ }
+#endif
+
+ while (elem != NULL) {
+ xsltStackElemPtr def;
+
+ /*
+ * Global variables are stored in the variables pool.
+ */
+ def = (xsltStackElemPtr)
+ xmlHashLookup2(ctxt->globalVars,
+ elem->name, elem->nameURI);
+ if (def == NULL) {
+
+ def = xsltCopyStackElem(elem);
+ xmlHashAddEntry2(ctxt->globalVars,
+ elem->name, elem->nameURI, def);
+ } else if ((elem->comp != NULL) &&
+ (elem->comp->type == XSLT_FUNC_VARIABLE)) {
+ /*
+ * Redefinition of variables from a different stylesheet
+ * should not generate a message.
+ */
+ if ((elem->comp->inst != NULL) &&
+ (def->comp != NULL) && (def->comp->inst != NULL) &&
+ (elem->comp->inst->doc == def->comp->inst->doc))
+ {
+ xsltTransformError(ctxt, style, elem->comp->inst,
+ "Global variable %s already defined\n", elem->name);
+ if (style != NULL) style->errors++;
+ }
+ }
+ elem = elem->next;
+ }
+
+ style = xsltNextImport(style);
+ }
+
+ /*
+ * This part does the actual evaluation
+ */
+ xmlHashScan(ctxt->globalVars,
+ (xmlHashScanner) xsltEvalGlobalVariable, ctxt);
+
+ return(0);
+}
+
+/**
+ * xsltRegisterGlobalVariable:
+ * @style: the XSLT transformation context
+ * @name: the variable name
+ * @ns_uri: the variable namespace URI
+ * @sel: the expression which need to be evaluated to generate a value
+ * @tree: the subtree if sel is NULL
+ * @comp: the precompiled value
+ * @value: the string value if available
+ *
+ * Register a new variable value. If @value is NULL it unregisters
+ * the variable
+ *
+ * Returns 0 in case of success, -1 in case of error
+ */
+static int
+xsltRegisterGlobalVariable(xsltStylesheetPtr style, const xmlChar *name,
+ const xmlChar *ns_uri, const xmlChar *sel,
+ xmlNodePtr tree, xsltStylePreCompPtr comp,
+ const xmlChar *value) {
+ xsltStackElemPtr elem, tmp;
+ if (style == NULL)
+ return(-1);
+ if (name == NULL)
+ return(-1);
+ if (comp == NULL)
+ return(-1);
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ if (comp->type == XSLT_FUNC_PARAM)
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Defining global param %s\n", name);
+ else
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Defining global variable %s\n", name);
+#endif
+
+ elem = xsltNewStackElem(NULL);
+ if (elem == NULL)
+ return(-1);
+ elem->comp = comp;
+ elem->name = xmlDictLookup(style->dict, name, -1);
+ elem->select = xmlDictLookup(style->dict, sel, -1);
+ if (ns_uri)
+ elem->nameURI = xmlDictLookup(style->dict, ns_uri, -1);
+ elem->tree = tree;
+ tmp = style->variables;
+ if (tmp == NULL) {
+ elem->next = NULL;
+ style->variables = elem;
+ } else {
+ while (tmp != NULL) {
+ if ((elem->comp->type == XSLT_FUNC_VARIABLE) &&
+ (tmp->comp->type == XSLT_FUNC_VARIABLE) &&
+ (xmlStrEqual(elem->name, tmp->name)) &&
+ ((elem->nameURI == tmp->nameURI) ||
+ (xmlStrEqual(elem->nameURI, tmp->nameURI))))
+ {
+ xsltTransformError(NULL, style, comp->inst,
+ "redefinition of global variable %s\n", elem->name);
+ style->errors++;
+ }
+ if (tmp->next == NULL)
+ break;
+ tmp = tmp->next;
+ }
+ elem->next = NULL;
+ tmp->next = elem;
+ }
+ if (value != NULL) {
+ elem->computed = 1;
+ elem->value = xmlXPathNewString(value);
+ }
+ return(0);
+}
+
+/**
+ * xsltProcessUserParamInternal
+ *
+ * @ctxt: the XSLT transformation context
+ * @name: a null terminated parameter name
+ * @value: a null terminated value (may be an XPath expression)
+ * @eval: 0 to treat the value literally, else evaluate as XPath expression
+ *
+ * If @eval is 0 then @value is treated literally and is stored in the global
+ * parameter/variable table without any change.
+ *
+ * Uf @eval is 1 then @value is treated as an XPath expression and is
+ * evaluated. In this case, if you want to pass a string which will be
+ * interpreted literally then it must be enclosed in single or double quotes.
+ * If the string contains single quotes (double quotes) then it cannot be
+ * enclosed single quotes (double quotes). If the string which you want to
+ * be treated literally contains both single and double quotes (e.g. Meet
+ * at Joe's for "Twelfth Night" at 7 o'clock) then there is no suitable
+ * quoting character. You cannot use &apos; or &quot; inside the string
+ * because the replacement of character entities with their equivalents is
+ * done at a different stage of processing. The solution is to call
+ * xsltQuoteUserParams or xsltQuoteOneUserParam.
+ *
+ * This needs to be done on parsed stylesheets before starting to apply
+ * transformations. Normally this will be called (directly or indirectly)
+ * only from xsltEvalUserParams, xsltEvalOneUserParam, xsltQuoteUserParams,
+ * or xsltQuoteOneUserParam.
+ *
+ * Returns 0 in case of success, -1 in case of error
+ */
+
+static
+int
+xsltProcessUserParamInternal(xsltTransformContextPtr ctxt,
+ const xmlChar * name,
+ const xmlChar * value,
+ int eval) {
+
+ xsltStylesheetPtr style;
+ const xmlChar *prefix;
+ const xmlChar *href;
+ xmlXPathCompExprPtr xpExpr;
+ xmlXPathObjectPtr result;
+
+ xsltStackElemPtr elem;
+ int res;
+ void *res_ptr;
+
+ if (ctxt == NULL)
+ return(-1);
+ if (name == NULL)
+ return(0);
+ if (value == NULL)
+ return(0);
+
+ style = ctxt->style;
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "Evaluating user parameter %s=%s\n", name, value));
+#endif
+
+ /*
+ * Name lookup
+ */
+ href = NULL;
+
+ if (name[0] == '{') {
+ int len = 0;
+
+ while ((name[len] != 0) && (name[len] != '}')) len++;
+ if (name[len] == 0) {
+ xsltTransformError(ctxt, style, NULL,
+ "user param : malformed parameter name : %s\n", name);
+ } else {
+ href = xmlDictLookup(ctxt->dict, &name[1], len-1);
+ name = xmlDictLookup(ctxt->dict, &name[len + 1], -1);
+ }
+ }
+ else {
+ name = xsltSplitQName(ctxt->dict, name, &prefix);
+ if (prefix != NULL) {
+ xmlNsPtr ns;
+
+ ns = xmlSearchNs(style->doc, xmlDocGetRootElement(style->doc),
+ prefix);
+ if (ns == NULL) {
+ xsltTransformError(ctxt, style, NULL,
+ "user param : no namespace bound to prefix %s\n", prefix);
+ href = NULL;
+ } else {
+ href = ns->href;
+ }
+ }
+ }
+
+ if (name == NULL)
+ return (-1);
+
+ res_ptr = xmlHashLookup2(ctxt->globalVars, name, href);
+ if (res_ptr != 0) {
+ xsltTransformError(ctxt, style, NULL,
+ "Global parameter %s already defined\n", name);
+ }
+ if (ctxt->globalVars == NULL)
+ ctxt->globalVars = xmlHashCreate(20);
+
+ /*
+ * do not overwrite variables with parameters from the command line
+ */
+ while (style != NULL) {
+ elem = ctxt->style->variables;
+ while (elem != NULL) {
+ if ((elem->comp != NULL) &&
+ (elem->comp->type == XSLT_FUNC_VARIABLE) &&
+ (xmlStrEqual(elem->name, name)) &&
+ (xmlStrEqual(elem->nameURI, href))) {
+ return(0);
+ }
+ elem = elem->next;
+ }
+ style = xsltNextImport(style);
+ }
+ style = ctxt->style;
+ elem = NULL;
+
+ /*
+ * Do the evaluation if @eval is non-zero.
+ */
+
+ result = NULL;
+ if (eval != 0) {
+ xpExpr = xmlXPathCompile(value);
+ if (xpExpr != NULL) {
+ xmlDocPtr oldXPDoc;
+ xmlNodePtr oldXPContextNode;
+ int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
+ xmlNsPtr *oldXPNamespaces;
+ xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
+
+ /*
+ * Save context states.
+ */
+ oldXPDoc = xpctxt->doc;
+ oldXPContextNode = xpctxt->node;
+ oldXPProximityPosition = xpctxt->proximityPosition;
+ oldXPContextSize = xpctxt->contextSize;
+ oldXPNamespaces = xpctxt->namespaces;
+ oldXPNsNr = xpctxt->nsNr;
+
+ /*
+ * SPEC XSLT 1.0:
+ * "At top-level, the expression or template specifying the
+ * variable value is evaluated with the same context as that used
+ * to process the root node of the source document: the current
+ * node is the root node of the source document and the current
+ * node list is a list containing just the root node of the source
+ * document."
+ */
+ xpctxt->doc = ctxt->initialContextDoc;
+ xpctxt->node = ctxt->initialContextNode;
+ xpctxt->contextSize = 1;
+ xpctxt->proximityPosition = 1;
+ /*
+ * There is really no in scope namespace for parameters on the
+ * command line.
+ */
+ xpctxt->namespaces = NULL;
+ xpctxt->nsNr = 0;
+
+ result = xmlXPathCompiledEval(xpExpr, xpctxt);
+
+ /*
+ * Restore Context states.
+ */
+ xpctxt->doc = oldXPDoc;
+ xpctxt->node = oldXPContextNode;
+ xpctxt->contextSize = oldXPContextSize;
+ xpctxt->proximityPosition = oldXPProximityPosition;
+ xpctxt->namespaces = oldXPNamespaces;
+ xpctxt->nsNr = oldXPNsNr;
+
+ xmlXPathFreeCompExpr(xpExpr);
+ }
+ if (result == NULL) {
+ xsltTransformError(ctxt, style, NULL,
+ "Evaluating user parameter %s failed\n", name);
+ ctxt->state = XSLT_STATE_STOPPED;
+ return(-1);
+ }
+ }
+
+ /*
+ * If @eval is 0 then @value is to be taken literally and result is NULL
+ *
+ * If @eval is not 0, then @value is an XPath expression and has been
+ * successfully evaluated and result contains the resulting value and
+ * is not NULL.
+ *
+ * Now create an xsltStackElemPtr for insertion into the context's
+ * global variable/parameter hash table.
+ */
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+#ifdef LIBXML_DEBUG_ENABLED
+ if ((xsltGenericDebugContext == stdout) ||
+ (xsltGenericDebugContext == stderr))
+ xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
+ result, 0);
+#endif
+#endif
+
+ elem = xsltNewStackElem(NULL);
+ if (elem != NULL) {
+ elem->name = name;
+ elem->select = xmlDictLookup(ctxt->dict, value, -1);
+ if (href != NULL)
+ elem->nameURI = xmlDictLookup(ctxt->dict, href, -1);
+ elem->tree = NULL;
+ elem->computed = 1;
+ if (eval == 0) {
+ elem->value = xmlXPathNewString(value);
+ }
+ else {
+ elem->value = result;
+ }
+ }
+
+ /*
+ * Global parameters are stored in the XPath context variables pool.
+ */
+
+ res = xmlHashAddEntry2(ctxt->globalVars, name, href, elem);
+ if (res != 0) {
+ xsltFreeStackElem(elem);
+ xsltTransformError(ctxt, style, NULL,
+ "Global parameter %s already defined\n", name);
+ }
+ return(0);
+}
+
+/**
+ * xsltEvalUserParams:
+ *
+ * @ctxt: the XSLT transformation context
+ * @params: a NULL terminated array of parameters name/value tuples
+ *
+ * Evaluate the global variables of a stylesheet. This needs to be
+ * done on parsed stylesheets before starting to apply transformations.
+ * Each of the parameters is evaluated as an XPath expression and stored
+ * in the global variables/parameter hash table. If you want your
+ * parameter used literally, use xsltQuoteUserParams.
+ *
+ * Returns 0 in case of success, -1 in case of error
+ */
+
+int
+xsltEvalUserParams(xsltTransformContextPtr ctxt, const char **params) {
+ int indx = 0;
+ const xmlChar *name;
+ const xmlChar *value;
+
+ if (params == NULL)
+ return(0);
+ while (params[indx] != NULL) {
+ name = (const xmlChar *) params[indx++];
+ value = (const xmlChar *) params[indx++];
+ if (xsltEvalOneUserParam(ctxt, name, value) != 0)
+ return(-1);
+ }
+ return 0;
+}
+
+/**
+ * xsltQuoteUserParams:
+ *
+ * @ctxt: the XSLT transformation context
+ * @params: a NULL terminated arry of parameters names/values tuples
+ *
+ * Similar to xsltEvalUserParams, but the values are treated literally and
+ * are * *not* evaluated as XPath expressions. This should be done on parsed
+ * stylesheets before starting to apply transformations.
+ *
+ * Returns 0 in case of success, -1 in case of error.
+ */
+
+int
+xsltQuoteUserParams(xsltTransformContextPtr ctxt, const char **params) {
+ int indx = 0;
+ const xmlChar *name;
+ const xmlChar *value;
+
+ if (params == NULL)
+ return(0);
+ while (params[indx] != NULL) {
+ name = (const xmlChar *) params[indx++];
+ value = (const xmlChar *) params[indx++];
+ if (xsltQuoteOneUserParam(ctxt, name, value) != 0)
+ return(-1);
+ }
+ return 0;
+}
+
+/**
+ * xsltEvalOneUserParam:
+ * @ctxt: the XSLT transformation context
+ * @name: a null terminated string giving the name of the parameter
+ * @value: a null terminated string giving the XPath expression to be evaluated
+ *
+ * This is normally called from xsltEvalUserParams to process a single
+ * parameter from a list of parameters. The @value is evaluated as an
+ * XPath expression and the result is stored in the context's global
+ * variable/parameter hash table.
+ *
+ * To have a parameter treated literally (not as an XPath expression)
+ * use xsltQuoteUserParams (or xsltQuoteOneUserParam). For more
+ * details see description of xsltProcessOneUserParamInternal.
+ *
+ * Returns 0 in case of success, -1 in case of error.
+ */
+
+int
+xsltEvalOneUserParam(xsltTransformContextPtr ctxt,
+ const xmlChar * name,
+ const xmlChar * value) {
+ return xsltProcessUserParamInternal(ctxt, name, value,
+ 1 /* xpath eval ? */);
+}
+
+/**
+ * xsltQuoteOneUserParam:
+ * @ctxt: the XSLT transformation context
+ * @name: a null terminated string giving the name of the parameter
+ * @value: a null terminated string giving the parameter value
+ *
+ * This is normally called from xsltQuoteUserParams to process a single
+ * parameter from a list of parameters. The @value is stored in the
+ * context's global variable/parameter hash table.
+ *
+ * Returns 0 in case of success, -1 in case of error.
+ */
+
+int
+xsltQuoteOneUserParam(xsltTransformContextPtr ctxt,
+ const xmlChar * name,
+ const xmlChar * value) {
+ return xsltProcessUserParamInternal(ctxt, name, value,
+ 0 /* xpath eval ? */);
+}
+
+/**
+ * xsltBuildVariable:
+ * @ctxt: the XSLT transformation context
+ * @comp: the precompiled form
+ * @tree: the tree if select is NULL
+ *
+ * Computes a new variable value.
+ *
+ * Returns the xsltStackElemPtr or NULL in case of error
+ */
+static xsltStackElemPtr
+xsltBuildVariable(xsltTransformContextPtr ctxt,
+ xsltStylePreCompPtr castedComp,
+ xmlNodePtr tree)
+{
+#ifdef XSLT_REFACTORED
+ xsltStyleBasicItemVariablePtr comp =
+ (xsltStyleBasicItemVariablePtr) castedComp;
+#else
+ xsltStylePreCompPtr comp = castedComp;
+#endif
+ xsltStackElemPtr elem;
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "Building variable %s", comp->name));
+ if (comp->select != NULL)
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ " select %s", comp->select));
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, "\n"));
+#endif
+
+ elem = xsltNewStackElem(ctxt);
+ if (elem == NULL)
+ return(NULL);
+ elem->comp = (xsltStylePreCompPtr) comp;
+ elem->name = comp->name;
+ elem->select = comp->select;
+ elem->nameURI = comp->ns;
+ elem->tree = tree;
+ elem->value = xsltEvalVariable(ctxt, elem,
+ (xsltStylePreCompPtr) comp);
+ elem->computed = 1;
+ return(elem);
+}
+
+/**
+ * xsltRegisterVariable:
+ * @ctxt: the XSLT transformation context
+ * @comp: the compiled XSLT-variable (or param) instruction
+ * @tree: the tree if select is NULL
+ * @isParam: indicates if this is a parameter
+ *
+ * Computes and registers a new variable.
+ *
+ * Returns 0 in case of success, -1 in case of error
+ */
+static int
+xsltRegisterVariable(xsltTransformContextPtr ctxt,
+ xsltStylePreCompPtr castedComp,
+ xmlNodePtr tree, int isParam)
+{
+#ifdef XSLT_REFACTORED
+ xsltStyleBasicItemVariablePtr comp =
+ (xsltStyleBasicItemVariablePtr) castedComp;
+#else
+ xsltStylePreCompPtr comp = castedComp;
+ int present;
+#endif
+ xsltStackElemPtr variable;
+
+#ifdef XSLT_REFACTORED
+ /*
+ * REFACTORED NOTE: Redefinitions of vars/params are checked
+ * at compilation time in the refactored code.
+ * xsl:with-param parameters are checked in xsltApplyXSLTTemplate().
+ */
+#else
+ present = xsltCheckStackElem(ctxt, comp->name, comp->ns);
+ if (isParam == 0) {
+ if ((present != 0) && (present != 3)) {
+ /* TODO: report QName. */
+ xsltTransformError(ctxt, NULL, comp->inst,
+ "XSLT-variable: Redefinition of variable '%s'.\n", comp->name);
+ return(0);
+ }
+ } else if (present != 0) {
+ if ((present == 1) || (present == 2)) {
+ /* TODO: report QName. */
+ xsltTransformError(ctxt, NULL, comp->inst,
+ "XSLT-param: Redefinition of parameter '%s'.\n", comp->name);
+ return(0);
+ }
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "param %s defined by caller\n", comp->name));
+#endif
+ return(0);
+ }
+#endif /* else of XSLT_REFACTORED */
+
+ variable = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree);
+ xsltAddStackElem(ctxt, variable);
+ return(0);
+}
+
+/**
+ * xsltGlobalVariableLookup:
+ * @ctxt: the XSLT transformation context
+ * @name: the variable name
+ * @ns_uri: the variable namespace URI
+ *
+ * Search in the Variable array of the context for the given
+ * variable value.
+ *
+ * Returns the value or NULL if not found
+ */
+static xmlXPathObjectPtr
+xsltGlobalVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
+ const xmlChar *ns_uri) {
+ xsltStackElemPtr elem;
+ xmlXPathObjectPtr ret = NULL;
+
+ /*
+ * Lookup the global variables in XPath global variable hash table
+ */
+ if ((ctxt->xpathCtxt == NULL) || (ctxt->globalVars == NULL))
+ return(NULL);
+ elem = (xsltStackElemPtr)
+ xmlHashLookup2(ctxt->globalVars, name, ns_uri);
+ if (elem == NULL) {
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "global variable not found %s\n", name));
+#endif
+ return(NULL);
+ }
+ /*
+ * URGENT TODO: Move the detection of recursive definitions
+ * to compile-time.
+ */
+ if (elem->computed == 0) {
+ if (elem->name == xsltComputingGlobalVarMarker) {
+ xsltTransformError(ctxt, NULL, elem->comp->inst,
+ "Recursive definition of %s\n", name);
+ return(NULL);
+ }
+ ret = xsltEvalGlobalVariable(elem, ctxt);
+ } else
+ ret = elem->value;
+ return(xmlXPathObjectCopy(ret));
+}
+
+/**
+ * xsltVariableLookup:
+ * @ctxt: the XSLT transformation context
+ * @name: the variable name
+ * @ns_uri: the variable namespace URI
+ *
+ * Search in the Variable array of the context for the given
+ * variable value.
+ *
+ * Returns the value or NULL if not found
+ */
+xmlXPathObjectPtr
+xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
+ const xmlChar *ns_uri) {
+ xsltStackElemPtr elem;
+
+ if (ctxt == NULL)
+ return(NULL);
+
+ elem = xsltStackLookup(ctxt, name, ns_uri);
+ if (elem == NULL) {
+ return(xsltGlobalVariableLookup(ctxt, name, ns_uri));
+ }
+ if (elem->computed == 0) {
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "uncomputed variable %s\n", name));
+#endif
+ elem->value = xsltEvalVariable(ctxt, elem, NULL);
+ elem->computed = 1;
+ }
+ if (elem->value != NULL)
+ return(xmlXPathObjectCopy(elem->value));
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "variable not found %s\n", name));
+#endif
+ return(NULL);
+}
+
+/**
+ * xsltParseStylesheetCallerParam:
+ * @ctxt: the XSLT transformation context
+ * @inst: the xsl:with-param instruction element
+ *
+ * Processes an xsl:with-param instruction at transformation time.
+ * The value is compute, but not recorded.
+ * NOTE that this is also called with an *xsl:param* element
+ * from exsltFuncFunctionFunction().
+ *
+ * Returns the new xsltStackElemPtr or NULL
+ */
+
+xsltStackElemPtr
+xsltParseStylesheetCallerParam(xsltTransformContextPtr ctxt, xmlNodePtr inst)
+{
+#ifdef XSLT_REFACTORED
+ xsltStyleBasicItemVariablePtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+ xmlNodePtr tree = NULL; /* The first child node of the instruction or
+ the instruction itself. */
+ xsltStackElemPtr param = NULL;
+
+ if ((ctxt == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return(NULL);
+
+#ifdef XSLT_REFACTORED
+ comp = (xsltStyleBasicItemVariablePtr) inst->psvi;
+#else
+ comp = (xsltStylePreCompPtr) inst->psvi;
+#endif
+
+ if (comp == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "Internal error in xsltParseStylesheetCallerParam(): "
+ "The XSLT 'with-param' instruction was not compiled.\n");
+ return(NULL);
+ }
+ if (comp->name == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "Internal error in xsltParseStylesheetCallerParam(): "
+ "XSLT 'with-param': The attribute 'name' was not compiled.\n");
+ return(NULL);
+ }
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "Handling xsl:with-param %s\n", comp->name));
+#endif
+
+ if (comp->select == NULL) {
+ tree = inst->children;
+ } else {
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ " select %s\n", comp->select));
+#endif
+ tree = inst;
+ }
+
+ param = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree);
+
+ return(param);
+}
+
+/**
+ * xsltParseGlobalVariable:
+ * @style: the XSLT stylesheet
+ * @cur: the "variable" element
+ *
+ * Parses a global XSLT 'variable' declaration at compilation time
+ * and registers it
+ */
+void
+xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur)
+{
+#ifdef XSLT_REFACTORED
+ xsltStyleItemVariablePtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ /*
+ * Note that xsltStylePreCompute() will be called from
+ * xslt.c only.
+ */
+ comp = (xsltStyleItemVariablePtr) cur->psvi;
+#else
+ xsltStylePreCompute(style, cur);
+ comp = (xsltStylePreCompPtr) cur->psvi;
+#endif
+ if (comp == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:variable : compilation failed\n");
+ return;
+ }
+
+ if (comp->name == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:variable : missing name attribute\n");
+ return;
+ }
+
+ /*
+ * Parse the content (a sequence constructor) of xsl:variable.
+ */
+ if (cur->children != NULL) {
+#ifdef XSLT_REFACTORED
+ xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children);
+#else
+ xsltParseTemplateContent(style, cur);
+#endif
+ }
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Registering global variable %s\n", comp->name);
+#endif
+
+ xsltRegisterGlobalVariable(style, comp->name, comp->ns,
+ comp->select, cur->children, (xsltStylePreCompPtr) comp,
+ NULL);
+}
+
+/**
+ * xsltParseGlobalParam:
+ * @style: the XSLT stylesheet
+ * @cur: the "param" element
+ *
+ * parse an XSLT transformation param declaration and record
+ * its value.
+ */
+
+void
+xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemParamPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
+ return;
+
+#ifdef XSLT_REFACTORED
+ /*
+ * Note that xsltStylePreCompute() will be called from
+ * xslt.c only.
+ */
+ comp = (xsltStyleItemParamPtr) cur->psvi;
+#else
+ xsltStylePreCompute(style, cur);
+ comp = (xsltStylePreCompPtr) cur->psvi;
+#endif
+ if (comp == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:param : compilation failed\n");
+ return;
+ }
+
+ if (comp->name == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:param : missing name attribute\n");
+ return;
+ }
+
+ /*
+ * Parse the content (a sequence constructor) of xsl:param.
+ */
+ if (cur->children != NULL) {
+#ifdef XSLT_REFACTORED
+ xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children);
+#else
+ xsltParseTemplateContent(style, cur);
+#endif
+ }
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Registering global param %s\n", comp->name);
+#endif
+
+ xsltRegisterGlobalVariable(style, comp->name, comp->ns,
+ comp->select, cur->children, (xsltStylePreCompPtr) comp,
+ NULL);
+}
+
+/**
+ * xsltParseStylesheetVariable:
+ * @ctxt: the XSLT transformation context
+ * @inst: the xsl:variable instruction element
+ *
+ * Registers a local XSLT 'variable' instruction at transformation time
+ * and evaluates its value.
+ */
+void
+xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr inst)
+{
+#ifdef XSLT_REFACTORED
+ xsltStyleItemVariablePtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((inst == NULL) || (ctxt == NULL) || (inst->type != XML_ELEMENT_NODE))
+ return;
+
+ comp = inst->psvi;
+ if (comp == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "Internal error in xsltParseStylesheetVariable(): "
+ "The XSLT 'variable' instruction was not compiled.\n");
+ return;
+ }
+ if (comp->name == NULL) {
+ xsltTransformError(ctxt, NULL, inst,
+ "Internal error in xsltParseStylesheetVariable(): "
+ "The attribute 'name' was not compiled.\n");
+ return;
+ }
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "Registering variable '%s'\n", comp->name));
+#endif
+
+ xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, inst->children, 0);
+}
+
+/**
+ * xsltParseStylesheetParam:
+ * @ctxt: the XSLT transformation context
+ * @cur: the XSLT 'param' element
+ *
+ * Registers a local XSLT 'param' declaration at transformation time and
+ * evaluates its value.
+ */
+void
+xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur)
+{
+#ifdef XSLT_REFACTORED
+ xsltStyleItemParamPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+
+ if ((cur == NULL) || (ctxt == NULL) || (cur->type != XML_ELEMENT_NODE))
+ return;
+
+ comp = cur->psvi;
+ if ((comp == NULL) || (comp->name == NULL)) {
+ xsltTransformError(ctxt, NULL, cur,
+ "Internal error in xsltParseStylesheetParam(): "
+ "The XSLT 'param' declaration was not compiled correctly.\n");
+ return;
+ }
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "Registering param %s\n", comp->name));
+#endif
+
+ xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, cur->children, 1);
+}
+
+/**
+ * xsltFreeGlobalVariables:
+ * @ctxt: the XSLT transformation context
+ *
+ * Free up the data associated to the global variables
+ * its value.
+ */
+
+void
+xsltFreeGlobalVariables(xsltTransformContextPtr ctxt) {
+ xmlHashFree(ctxt->globalVars, (xmlHashDeallocator) xsltFreeStackElem);
+}
+
+/**
+ * xsltXPathVariableLookup:
+ * @ctxt: a void * but the the XSLT transformation context actually
+ * @name: the variable name
+ * @ns_uri: the variable namespace URI
+ *
+ * This is the entry point when a varibale is needed by the XPath
+ * interpretor.
+ *
+ * Returns the value or NULL if not found
+ */
+xmlXPathObjectPtr
+xsltXPathVariableLookup(void *ctxt, const xmlChar *name,
+ const xmlChar *ns_uri) {
+ xsltTransformContextPtr tctxt;
+ xmlXPathObjectPtr valueObj = NULL;
+
+ if ((ctxt == NULL) || (name == NULL))
+ return(NULL);
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(((xsltTransformContextPtr)ctxt),XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "Lookup variable '%s'\n", name));
+#endif
+
+ tctxt = (xsltTransformContextPtr) ctxt;
+ /*
+ * Local variables/params ---------------------------------------------
+ *
+ * Do the lookup from the top of the stack, but
+ * don't use params being computed in a call-param
+ * First lookup expects the variable name and URI to
+ * come from the disctionnary and hence pointer comparison.
+ */
+ if (tctxt->varsNr != 0) {
+ int i;
+ xsltStackElemPtr variable = NULL, cur;
+
+ for (i = tctxt->varsNr; i > tctxt->varsBase; i--) {
+ cur = tctxt->varsTab[i-1];
+ if ((cur->name == name) && (cur->nameURI == ns_uri)) {
+#if 0
+ stack_addr++;
+#endif
+ variable = cur;
+ goto local_variable_found;
+ }
+ cur = cur->next;
+ }
+ /*
+ * Redo the lookup with interned strings to avoid string comparison.
+ *
+ * OPTIMIZE TODO: The problem here is, that if we request a
+ * global variable, then this will be also executed.
+ */
+ {
+ const xmlChar *tmpName = name, *tmpNsName = ns_uri;
+
+ name = xmlDictLookup(tctxt->dict, name, -1);
+ if (ns_uri)
+ ns_uri = xmlDictLookup(tctxt->dict, ns_uri, -1);
+ if ((tmpName != name) || (tmpNsName != ns_uri)) {
+ for (i = tctxt->varsNr; i > tctxt->varsBase; i--) {
+ cur = tctxt->varsTab[i-1];
+ if ((cur->name == name) && (cur->nameURI == ns_uri)) {
+#if 0
+ stack_cmp++;
+#endif
+ variable = cur;
+ goto local_variable_found;
+ }
+ }
+ }
+ }
+
+local_variable_found:
+
+ if (variable) {
+ if (variable->computed == 0) {
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "uncomputed variable '%s'\n", name));
+#endif
+ variable->value = xsltEvalVariable(tctxt, variable, NULL);
+ variable->computed = 1;
+ }
+ if (variable->value != NULL) {
+ valueObj = xmlXPathObjectCopy(variable->value);
+ }
+ return(valueObj);
+ }
+ }
+ /*
+ * Global variables/params --------------------------------------------
+ */
+ if (tctxt->globalVars) {
+ valueObj = xsltGlobalVariableLookup(tctxt, name, ns_uri);
+ }
+
+ if (valueObj == NULL) {
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "variable not found '%s'\n", name));
+#endif
+
+ if (ns_uri) {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "Variable '{%s}%s' has not been declared.\n", ns_uri, name);
+ } else {
+ xsltTransformError(tctxt, NULL, tctxt->inst,
+ "Variable '%s' has not been declared.\n", name);
+ }
+ } else {
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+ XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
+ "found variable '%s'\n", name));
+#endif
+ }
+
+ return(valueObj);
+}
+
+
diff --git a/libxslt/variables.h b/libxslt/variables.h
new file mode 100644
index 0000000..f80eeab
--- /dev/null
+++ b/libxslt/variables.h
@@ -0,0 +1,100 @@
+/*
+ * Summary: interface for the variable matching and lookup.
+ * Description: interface for the variable matching and lookup.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_VARIABLES_H__
+#define __XML_XSLT_VARIABLES_H__
+
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+#include "functions.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * XSLT_REGISTER_VARIABLE_LOOKUP:
+ *
+ * Registering macro, not general purpose at all but used in different modules.
+ */
+
+#define XSLT_REGISTER_VARIABLE_LOOKUP(ctxt) \
+ xmlXPathRegisterVariableLookup((ctxt)->xpathCtxt, \
+ xsltXPathVariableLookup, (void *)(ctxt)); \
+ xsltRegisterAllFunctions((ctxt)->xpathCtxt); \
+ xsltRegisterAllElement(ctxt); \
+ (ctxt)->xpathCtxt->extra = ctxt
+
+/*
+ * Flags for memory management of RVTs
+ */
+
+#define XSLT_RVT_LOCAL ((void *)1)
+#define XSLT_RVT_VARIABLE ((void *)2)
+#define XSLT_RVT_FUNC_RESULT ((void *)3)
+#define XSLT_RVT_GLOBAL ((void *)4)
+
+/*
+ * Interfaces for the variable module.
+ */
+
+XSLTPUBFUN int XSLTCALL
+ xsltEvalGlobalVariables (xsltTransformContextPtr ctxt);
+XSLTPUBFUN int XSLTCALL
+ xsltEvalUserParams (xsltTransformContextPtr ctxt,
+ const char **params);
+XSLTPUBFUN int XSLTCALL
+ xsltQuoteUserParams (xsltTransformContextPtr ctxt,
+ const char **params);
+XSLTPUBFUN int XSLTCALL
+ xsltEvalOneUserParam (xsltTransformContextPtr ctxt,
+ const xmlChar * name,
+ const xmlChar * value);
+XSLTPUBFUN int XSLTCALL
+ xsltQuoteOneUserParam (xsltTransformContextPtr ctxt,
+ const xmlChar * name,
+ const xmlChar * value);
+
+XSLTPUBFUN void XSLTCALL
+ xsltParseGlobalVariable (xsltStylesheetPtr style,
+ xmlNodePtr cur);
+XSLTPUBFUN void XSLTCALL
+ xsltParseGlobalParam (xsltStylesheetPtr style,
+ xmlNodePtr cur);
+XSLTPUBFUN void XSLTCALL
+ xsltParseStylesheetVariable (xsltTransformContextPtr ctxt,
+ xmlNodePtr cur);
+XSLTPUBFUN void XSLTCALL
+ xsltParseStylesheetParam (xsltTransformContextPtr ctxt,
+ xmlNodePtr cur);
+XSLTPUBFUN xsltStackElemPtr XSLTCALL
+ xsltParseStylesheetCallerParam (xsltTransformContextPtr ctxt,
+ xmlNodePtr cur);
+XSLTPUBFUN int XSLTCALL
+ xsltAddStackElemList (xsltTransformContextPtr ctxt,
+ xsltStackElemPtr elems);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeGlobalVariables (xsltTransformContextPtr ctxt);
+XSLTPUBFUN xmlXPathObjectPtr XSLTCALL
+ xsltVariableLookup (xsltTransformContextPtr ctxt,
+ const xmlChar *name,
+ const xmlChar *ns_uri);
+XSLTPUBFUN xmlXPathObjectPtr XSLTCALL
+ xsltXPathVariableLookup (void *ctxt,
+ const xmlChar *name,
+ const xmlChar *ns_uri);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_VARIABLES_H__ */
+
diff --git a/libxslt/win32config.h b/libxslt/win32config.h
new file mode 100644
index 0000000..8fe7042
--- /dev/null
+++ b/libxslt/win32config.h
@@ -0,0 +1,129 @@
+/*
+ * Summary: Windows configuration header
+ * Description: Windows configuration header
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Igor Zlatkovic
+ */
+#ifndef __LIBXSLT_WIN32_CONFIG__
+#define __LIBXSLT_WIN32_CONFIG__
+
+#define HAVE_CTYPE_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STDARG_H 1
+#define HAVE_MALLOC_H 1
+#define HAVE_TIME_H 1
+#define HAVE_LOCALTIME 1
+#define HAVE_GMTIME 1
+#define HAVE_TIME 1
+#define HAVE_MATH_H 1
+#define HAVE_FCNTL_H 1
+
+#include <io.h>
+
+#define HAVE_ISINF
+#define HAVE_ISNAN
+
+#include <math.h>
+#if defined _MSC_VER || defined __MINGW32__
+/* MS C-runtime has functions which can be used in order to determine if
+ a given floating-point variable contains NaN, (+-)INF. These are
+ preferred, because floating-point technology is considered propriatary
+ by MS and we can assume that their functions know more about their
+ oddities than we do. */
+#include <float.h>
+/* Bjorn Reese figured a quite nice construct for isinf() using the
+ _fpclass() function. */
+#ifndef isinf
+#define isinf(d) ((_fpclass(d) == _FPCLASS_PINF) ? 1 \
+ : ((_fpclass(d) == _FPCLASS_NINF) ? -1 : 0))
+#endif
+/* _isnan(x) returns nonzero if (x == NaN) and zero otherwise. */
+#ifndef isnan
+#define isnan(d) (_isnan(d))
+#endif
+#else /* _MSC_VER */
+static int isinf (double d) {
+ int expon = 0;
+ double val = frexp (d, &expon);
+ if (expon == 1025) {
+ if (val == 0.5) {
+ return 1;
+ } else if (val == -0.5) {
+ return -1;
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+}
+static int isnan (double d) {
+ int expon = 0;
+ double val = frexp (d, &expon);
+ if (expon == 1025) {
+ if (val == 0.5) {
+ return 0;
+ } else if (val == -0.5) {
+ return 0;
+ } else {
+ return 1;
+ }
+ } else {
+ return 0;
+ }
+}
+#endif /* _MSC_VER */
+
+#include <direct.h>
+
+/* snprintf emulation taken from http://stackoverflow.com/a/8712996/1956010 */
+#if defined(_MSC_VER) && _MSC_VER < 1900
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#define snprintf c99_snprintf
+#define vsnprintf c99_vsnprintf
+
+__inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
+{
+ int count = -1;
+
+ if (size != 0)
+ count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
+ if (count == -1)
+ count = _vscprintf(format, ap);
+
+ return count;
+}
+
+__inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...)
+{
+ int count;
+ va_list ap;
+
+ va_start(ap, format);
+ count = c99_vsnprintf(outBuf, size, format, ap);
+ va_end(ap);
+
+ return count;
+}
+
+#endif /* defined(_MSC_VER) && _MSC_VER < 1900 */
+
+#define HAVE_SYS_STAT_H
+#define HAVE__STAT
+#define HAVE_STRING_H
+
+#include <libxml/xmlversion.h>
+
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED
+#endif
+
+#define _WINSOCKAPI_
+
+#endif /* __LIBXSLT_WIN32_CONFIG__ */
+
diff --git a/libxslt/xslt.c b/libxslt/xslt.c
new file mode 100644
index 0000000..d41a98d
--- /dev/null
+++ b/libxslt/xslt.c
@@ -0,0 +1,6990 @@
+/*
+ * xslt.c: Implemetation of an XSL Transformation 1.0 engine
+ *
+ * Reference:
+ * XSLT specification
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * Associating Style Sheets with XML documents
+ * http://www.w3.org/1999/06/REC-xml-stylesheet-19990629
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/valid.h>
+#include <libxml/hash.h>
+#include <libxml/uri.h>
+#include <libxml/xmlerror.h>
+#include <libxml/parserInternals.h>
+#include <libxml/xpathInternals.h>
+#include <libxml/xpath.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "pattern.h"
+#include "variables.h"
+#include "namespaces.h"
+#include "attributes.h"
+#include "xsltutils.h"
+#include "imports.h"
+#include "keys.h"
+#include "documents.h"
+#include "extensions.h"
+#include "preproc.h"
+#include "extra.h"
+#include "security.h"
+
+#ifdef WITH_XSLT_DEBUG
+#define WITH_XSLT_DEBUG_PARSING
+/* #define WITH_XSLT_DEBUG_BLANKS */
+#endif
+
+const char *xsltEngineVersion = LIBXSLT_VERSION_STRING LIBXSLT_VERSION_EXTRA;
+const int xsltLibxsltVersion = LIBXSLT_VERSION;
+const int xsltLibxmlVersion = LIBXML_VERSION;
+
+#ifdef XSLT_REFACTORED
+
+const xmlChar *xsltConstNamespaceNameXSLT = (const xmlChar *) XSLT_NAMESPACE;
+
+#define XSLT_ELEMENT_CATEGORY_XSLT 0
+#define XSLT_ELEMENT_CATEGORY_EXTENSION 1
+#define XSLT_ELEMENT_CATEGORY_LRE 2
+
+/*
+* xsltLiteralResultMarker:
+* Marker for Literal result elements, in order to avoid multiple attempts
+* to recognize such elements in the stylesheet's tree.
+* This marker is set on node->psvi during the initial traversal
+* of a stylesheet's node tree.
+*
+const xmlChar *xsltLiteralResultMarker =
+ (const xmlChar *) "Literal Result Element";
+*/
+
+/*
+* xsltXSLTTextMarker:
+* Marker for xsl:text elements. Used to recognize xsl:text elements
+* for post-processing of the stylesheet's tree, where those
+* elements are removed from the tree.
+*/
+const xmlChar *xsltXSLTTextMarker = (const xmlChar *) "XSLT Text Element";
+
+/*
+* xsltXSLTAttrMarker:
+* Marker for XSLT attribute on Literal Result Elements.
+*/
+const xmlChar *xsltXSLTAttrMarker = (const xmlChar *) "LRE XSLT Attr";
+
+#endif
+
+#ifdef XSLT_LOCALE_WINAPI
+extern xmlRMutexPtr xsltLocaleMutex;
+#endif
+/*
+ * Harmless but avoiding a problem when compiling against a
+ * libxml <= 2.3.11 without LIBXML_DEBUG_ENABLED
+ */
+#ifndef LIBXML_DEBUG_ENABLED
+double xmlXPathStringEvalNumber(const xmlChar *str);
+#endif
+/*
+ * Useful macros
+ */
+
+#ifdef IS_BLANK
+#undef IS_BLANK
+#endif
+#define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
+ ((c) == 0x0D))
+
+#ifdef IS_BLANK_NODE
+#undef IS_BLANK_NODE
+#endif
+#define IS_BLANK_NODE(n) \
+ (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
+
+/**
+ * xsltParseContentError:
+ *
+ * @style: the stylesheet
+ * @node: the node where the error occured
+ *
+ * Compile-time error function.
+ */
+static void
+xsltParseContentError(xsltStylesheetPtr style,
+ xmlNodePtr node)
+{
+ if ((style == NULL) || (node == NULL))
+ return;
+
+ if (IS_XSLT_ELEM(node))
+ xsltTransformError(NULL, style, node,
+ "The XSLT-element '%s' is not allowed at this position.\n",
+ node->name);
+ else
+ xsltTransformError(NULL, style, node,
+ "The element '%s' is not allowed at this position.\n",
+ node->name);
+ style->errors++;
+}
+
+#ifdef XSLT_REFACTORED
+#else
+/**
+ * exclPrefixPush:
+ * @style: the transformation stylesheet
+ * @value: the excluded namespace name to push on the stack
+ *
+ * Push an excluded namespace name on the stack
+ *
+ * Returns the new index in the stack or -1 if already present or
+ * in case of error
+ */
+static int
+exclPrefixPush(xsltStylesheetPtr style, xmlChar * value)
+{
+ int i;
+
+ if (style->exclPrefixMax == 0) {
+ style->exclPrefixMax = 4;
+ style->exclPrefixTab =
+ (xmlChar * *)xmlMalloc(style->exclPrefixMax *
+ sizeof(style->exclPrefixTab[0]));
+ if (style->exclPrefixTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
+ return (-1);
+ }
+ }
+ /* do not push duplicates */
+ for (i = 0;i < style->exclPrefixNr;i++) {
+ if (xmlStrEqual(style->exclPrefixTab[i], value))
+ return(-1);
+ }
+ if (style->exclPrefixNr >= style->exclPrefixMax) {
+ style->exclPrefixMax *= 2;
+ style->exclPrefixTab =
+ (xmlChar * *)xmlRealloc(style->exclPrefixTab,
+ style->exclPrefixMax *
+ sizeof(style->exclPrefixTab[0]));
+ if (style->exclPrefixTab == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
+ return (-1);
+ }
+ }
+ style->exclPrefixTab[style->exclPrefixNr] = value;
+ style->exclPrefix = value;
+ return (style->exclPrefixNr++);
+}
+/**
+ * exclPrefixPop:
+ * @style: the transformation stylesheet
+ *
+ * Pop an excluded prefix value from the stack
+ *
+ * Returns the stored excluded prefix value
+ */
+static xmlChar *
+exclPrefixPop(xsltStylesheetPtr style)
+{
+ xmlChar *ret;
+
+ if (style->exclPrefixNr <= 0)
+ return (0);
+ style->exclPrefixNr--;
+ if (style->exclPrefixNr > 0)
+ style->exclPrefix = style->exclPrefixTab[style->exclPrefixNr - 1];
+ else
+ style->exclPrefix = NULL;
+ ret = style->exclPrefixTab[style->exclPrefixNr];
+ style->exclPrefixTab[style->exclPrefixNr] = 0;
+ return (ret);
+}
+#endif
+
+/************************************************************************
+ * *
+ * Helper functions *
+ * *
+ ************************************************************************/
+
+static int initialized = 0;
+/**
+ * xsltInit:
+ *
+ * Initializes the processor (e.g. registers built-in extensions,
+ * etc.)
+ */
+void
+xsltInit (void) {
+ if (initialized == 0) {
+ initialized = 1;
+#ifdef XSLT_LOCALE_WINAPI
+ xsltLocaleMutex = xmlNewRMutex();
+#endif
+ xsltRegisterAllExtras();
+ }
+}
+
+/**
+ * xsltUninit:
+ *
+ * Uninitializes the processor.
+ */
+void
+xsltUninit (void) {
+#ifdef XSLT_LOCALE_WINAPI
+ xmlFreeRMutex(xsltLocaleMutex);
+ xsltLocaleMutex = NULL;
+#endif
+ initialized = 0;
+}
+
+/**
+ * xsltIsBlank:
+ * @str: a string
+ *
+ * Check if a string is ignorable
+ *
+ * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
+ */
+int
+xsltIsBlank(xmlChar *str) {
+ if (str == NULL)
+ return(1);
+ while (*str != 0) {
+ if (!(IS_BLANK(*str))) return(0);
+ str++;
+ }
+ return(1);
+}
+
+/************************************************************************
+ * *
+ * Routines to handle XSLT data structures *
+ * *
+ ************************************************************************/
+static xsltDecimalFormatPtr
+xsltNewDecimalFormat(xmlChar *name)
+{
+ xsltDecimalFormatPtr self;
+ /* UTF-8 for 0x2030 */
+ static const xmlChar permille[4] = {0xe2, 0x80, 0xb0, 0};
+
+ self = xmlMalloc(sizeof(xsltDecimalFormat));
+ if (self != NULL) {
+ self->next = NULL;
+ self->name = name;
+
+ /* Default values */
+ self->digit = xmlStrdup(BAD_CAST("#"));
+ self->patternSeparator = xmlStrdup(BAD_CAST(";"));
+ self->decimalPoint = xmlStrdup(BAD_CAST("."));
+ self->grouping = xmlStrdup(BAD_CAST(","));
+ self->percent = xmlStrdup(BAD_CAST("%"));
+ self->permille = xmlStrdup(BAD_CAST(permille));
+ self->zeroDigit = xmlStrdup(BAD_CAST("0"));
+ self->minusSign = xmlStrdup(BAD_CAST("-"));
+ self->infinity = xmlStrdup(BAD_CAST("Infinity"));
+ self->noNumber = xmlStrdup(BAD_CAST("NaN"));
+ }
+ return self;
+}
+
+static void
+xsltFreeDecimalFormat(xsltDecimalFormatPtr self)
+{
+ if (self != NULL) {
+ if (self->digit)
+ xmlFree(self->digit);
+ if (self->patternSeparator)
+ xmlFree(self->patternSeparator);
+ if (self->decimalPoint)
+ xmlFree(self->decimalPoint);
+ if (self->grouping)
+ xmlFree(self->grouping);
+ if (self->percent)
+ xmlFree(self->percent);
+ if (self->permille)
+ xmlFree(self->permille);
+ if (self->zeroDigit)
+ xmlFree(self->zeroDigit);
+ if (self->minusSign)
+ xmlFree(self->minusSign);
+ if (self->infinity)
+ xmlFree(self->infinity);
+ if (self->noNumber)
+ xmlFree(self->noNumber);
+ if (self->name)
+ xmlFree(self->name);
+ xmlFree(self);
+ }
+}
+
+static void
+xsltFreeDecimalFormatList(xsltStylesheetPtr self)
+{
+ xsltDecimalFormatPtr iter;
+ xsltDecimalFormatPtr tmp;
+
+ if (self == NULL)
+ return;
+
+ iter = self->decimalFormat;
+ while (iter != NULL) {
+ tmp = iter->next;
+ xsltFreeDecimalFormat(iter);
+ iter = tmp;
+ }
+}
+
+/**
+ * xsltDecimalFormatGetByName:
+ * @style: the XSLT stylesheet
+ * @name: the decimal-format name to find
+ *
+ * Find decimal-format by name
+ *
+ * Returns the xsltDecimalFormatPtr
+ */
+xsltDecimalFormatPtr
+xsltDecimalFormatGetByName(xsltStylesheetPtr style, xmlChar *name)
+{
+ xsltDecimalFormatPtr result = NULL;
+
+ if (name == NULL)
+ return style->decimalFormat;
+
+ while (style != NULL) {
+ for (result = style->decimalFormat->next;
+ result != NULL;
+ result = result->next) {
+ if (xmlStrEqual(name, result->name))
+ return result;
+ }
+ style = xsltNextImport(style);
+ }
+ return result;
+}
+
+
+/**
+ * xsltNewTemplate:
+ *
+ * Create a new XSLT Template
+ *
+ * Returns the newly allocated xsltTemplatePtr or NULL in case of error
+ */
+static xsltTemplatePtr
+xsltNewTemplate(void) {
+ xsltTemplatePtr cur;
+
+ cur = (xsltTemplatePtr) xmlMalloc(sizeof(xsltTemplate));
+ if (cur == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltNewTemplate : malloc failed\n");
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xsltTemplate));
+ cur->priority = XSLT_PAT_NO_PRIORITY;
+ return(cur);
+}
+
+/**
+ * xsltFreeTemplate:
+ * @template: an XSLT template
+ *
+ * Free up the memory allocated by @template
+ */
+static void
+xsltFreeTemplate(xsltTemplatePtr template) {
+ if (template == NULL)
+ return;
+ if (template->match) xmlFree(template->match);
+/*
+* NOTE: @name and @nameURI are put into the string dict now.
+* if (template->name) xmlFree(template->name);
+* if (template->nameURI) xmlFree(template->nameURI);
+*/
+/*
+ if (template->mode) xmlFree(template->mode);
+ if (template->modeURI) xmlFree(template->modeURI);
+ */
+ if (template->inheritedNs) xmlFree(template->inheritedNs);
+
+ /* free profiling data */
+ if (template->templCalledTab) xmlFree(template->templCalledTab);
+ if (template->templCountTab) xmlFree(template->templCountTab);
+
+ memset(template, -1, sizeof(xsltTemplate));
+ xmlFree(template);
+}
+
+/**
+ * xsltFreeTemplateList:
+ * @template: an XSLT template list
+ *
+ * Free up the memory allocated by all the elements of @template
+ */
+static void
+xsltFreeTemplateList(xsltTemplatePtr template) {
+ xsltTemplatePtr cur;
+
+ while (template != NULL) {
+ cur = template;
+ template = template->next;
+ xsltFreeTemplate(cur);
+ }
+}
+
+#ifdef XSLT_REFACTORED
+
+static void
+xsltFreeNsAliasList(xsltNsAliasPtr item)
+{
+ xsltNsAliasPtr tmp;
+
+ while (item) {
+ tmp = item;
+ item = item->next;
+ xmlFree(tmp);
+ }
+ return;
+}
+
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+static void
+xsltFreeNamespaceMap(xsltNsMapPtr item)
+{
+ xsltNsMapPtr tmp;
+
+ while (item) {
+ tmp = item;
+ item = item->next;
+ xmlFree(tmp);
+ }
+ return;
+}
+
+static xsltNsMapPtr
+xsltNewNamespaceMapItem(xsltCompilerCtxtPtr cctxt,
+ xmlDocPtr doc,
+ xmlNsPtr ns,
+ xmlNodePtr elem)
+{
+ xsltNsMapPtr ret;
+
+ if ((cctxt == NULL) || (doc == NULL) || (ns == NULL))
+ return(NULL);
+
+ ret = (xsltNsMapPtr) xmlMalloc(sizeof(xsltNsMap));
+ if (ret == NULL) {
+ xsltTransformError(NULL, cctxt->style, elem,
+ "Internal error: (xsltNewNamespaceMapItem) "
+ "memory allocation failed.\n");
+ return(NULL);
+ }
+ memset(ret, 0, sizeof(xsltNsMap));
+ ret->doc = doc;
+ ret->ns = ns;
+ ret->origNsName = ns->href;
+ /*
+ * Store the item at current stylesheet-level.
+ */
+ if (cctxt->psData->nsMap != NULL)
+ ret->next = cctxt->psData->nsMap;
+ cctxt->psData->nsMap = ret;
+
+ return(ret);
+}
+#endif /* XSLT_REFACTORED_XSLT_NSCOMP */
+
+/**
+ * xsltCompilerVarInfoFree:
+ * @cctxt: the compilation context
+ *
+ * Frees the list of information for vars/params.
+ */
+static void
+xsltCompilerVarInfoFree(xsltCompilerCtxtPtr cctxt)
+{
+ xsltVarInfoPtr ivar = cctxt->ivars, ivartmp;
+
+ while (ivar) {
+ ivartmp = ivar;
+ ivar = ivar->next;
+ xmlFree(ivartmp);
+ }
+}
+
+/**
+ * xsltCompilerCtxtFree:
+ *
+ * Free an XSLT compiler context.
+ */
+static void
+xsltCompilationCtxtFree(xsltCompilerCtxtPtr cctxt)
+{
+ if (cctxt == NULL)
+ return;
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Freeing compilation context\n");
+ xsltGenericDebug(xsltGenericDebugContext,
+ "### Max inodes: %d\n", cctxt->maxNodeInfos);
+ xsltGenericDebug(xsltGenericDebugContext,
+ "### Max LREs : %d\n", cctxt->maxLREs);
+#endif
+ /*
+ * Free node-infos.
+ */
+ if (cctxt->inodeList != NULL) {
+ xsltCompilerNodeInfoPtr tmp, cur = cctxt->inodeList;
+ while (cur != NULL) {
+ tmp = cur;
+ cur = cur->next;
+ xmlFree(tmp);
+ }
+ }
+ if (cctxt->tmpList != NULL)
+ xsltPointerListFree(cctxt->tmpList);
+#ifdef XSLT_REFACTORED_XPATHCOMP
+ if (cctxt->xpathCtxt != NULL)
+ xmlXPathFreeContext(cctxt->xpathCtxt);
+#endif
+ if (cctxt->nsAliases != NULL)
+ xsltFreeNsAliasList(cctxt->nsAliases);
+
+ if (cctxt->ivars)
+ xsltCompilerVarInfoFree(cctxt);
+
+ xmlFree(cctxt);
+}
+
+/**
+ * xsltCompilerCreate:
+ *
+ * Creates an XSLT compiler context.
+ *
+ * Returns the pointer to the created xsltCompilerCtxt or
+ * NULL in case of an internal error.
+ */
+static xsltCompilerCtxtPtr
+xsltCompilationCtxtCreate(xsltStylesheetPtr style) {
+ xsltCompilerCtxtPtr ret;
+
+ ret = (xsltCompilerCtxtPtr) xmlMalloc(sizeof(xsltCompilerCtxt));
+ if (ret == NULL) {
+ xsltTransformError(NULL, style, NULL,
+ "xsltCompilerCreate: allocation of compiler "
+ "context failed.\n");
+ return(NULL);
+ }
+ memset(ret, 0, sizeof(xsltCompilerCtxt));
+
+ ret->errSeverity = XSLT_ERROR_SEVERITY_ERROR;
+ ret->tmpList = xsltPointerListCreate(20);
+ if (ret->tmpList == NULL) {
+ goto internal_err;
+ }
+#ifdef XSLT_REFACTORED_XPATHCOMP
+ /*
+ * Create the XPath compilation context in order
+ * to speed up precompilation of XPath expressions.
+ */
+ ret->xpathCtxt = xmlXPathNewContext(NULL);
+ if (ret->xpathCtxt == NULL)
+ goto internal_err;
+#endif
+
+ return(ret);
+
+internal_err:
+ xsltCompilationCtxtFree(ret);
+ return(NULL);
+}
+
+static void
+xsltLREEffectiveNsNodesFree(xsltEffectiveNsPtr first)
+{
+ xsltEffectiveNsPtr tmp;
+
+ while (first != NULL) {
+ tmp = first;
+ first = first->nextInStore;
+ xmlFree(tmp);
+ }
+}
+
+static void
+xsltFreePrincipalStylesheetData(xsltPrincipalStylesheetDataPtr data)
+{
+ if (data == NULL)
+ return;
+
+ if (data->inScopeNamespaces != NULL) {
+ int i;
+ xsltNsListContainerPtr nsi;
+ xsltPointerListPtr list =
+ (xsltPointerListPtr) data->inScopeNamespaces;
+
+ for (i = 0; i < list->number; i++) {
+ /*
+ * REVISIT TODO: Free info of in-scope namespaces.
+ */
+ nsi = (xsltNsListContainerPtr) list->items[i];
+ if (nsi->list != NULL)
+ xmlFree(nsi->list);
+ xmlFree(nsi);
+ }
+ xsltPointerListFree(list);
+ data->inScopeNamespaces = NULL;
+ }
+
+ if (data->exclResultNamespaces != NULL) {
+ int i;
+ xsltPointerListPtr list = (xsltPointerListPtr)
+ data->exclResultNamespaces;
+
+ for (i = 0; i < list->number; i++)
+ xsltPointerListFree((xsltPointerListPtr) list->items[i]);
+
+ xsltPointerListFree(list);
+ data->exclResultNamespaces = NULL;
+ }
+
+ if (data->extElemNamespaces != NULL) {
+ xsltPointerListPtr list = (xsltPointerListPtr)
+ data->extElemNamespaces;
+ int i;
+
+ for (i = 0; i < list->number; i++)
+ xsltPointerListFree((xsltPointerListPtr) list->items[i]);
+
+ xsltPointerListFree(list);
+ data->extElemNamespaces = NULL;
+ }
+ if (data->effectiveNs) {
+ xsltLREEffectiveNsNodesFree(data->effectiveNs);
+ data->effectiveNs = NULL;
+ }
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+ xsltFreeNamespaceMap(data->nsMap);
+#endif
+ xmlFree(data);
+}
+
+static xsltPrincipalStylesheetDataPtr
+xsltNewPrincipalStylesheetData(void)
+{
+ xsltPrincipalStylesheetDataPtr ret;
+
+ ret = (xsltPrincipalStylesheetDataPtr)
+ xmlMalloc(sizeof(xsltPrincipalStylesheetData));
+ if (ret == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltNewPrincipalStylesheetData: memory allocation failed.\n");
+ return(NULL);
+ }
+ memset(ret, 0, sizeof(xsltPrincipalStylesheetData));
+
+ /*
+ * Global list of in-scope namespaces.
+ */
+ ret->inScopeNamespaces = xsltPointerListCreate(-1);
+ if (ret->inScopeNamespaces == NULL)
+ goto internal_err;
+ /*
+ * Global list of excluded result ns-decls.
+ */
+ ret->exclResultNamespaces = xsltPointerListCreate(-1);
+ if (ret->exclResultNamespaces == NULL)
+ goto internal_err;
+ /*
+ * Global list of extension instruction namespace names.
+ */
+ ret->extElemNamespaces = xsltPointerListCreate(-1);
+ if (ret->extElemNamespaces == NULL)
+ goto internal_err;
+
+ return(ret);
+
+internal_err:
+
+ return(NULL);
+}
+
+#endif
+
+/**
+ * xsltNewStylesheet:
+ *
+ * Create a new XSLT Stylesheet
+ *
+ * Returns the newly allocated xsltStylesheetPtr or NULL in case of error
+ */
+xsltStylesheetPtr
+xsltNewStylesheet(void) {
+ xsltStylesheetPtr ret = NULL;
+
+ ret = (xsltStylesheetPtr) xmlMalloc(sizeof(xsltStylesheet));
+ if (ret == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltNewStylesheet : malloc failed\n");
+ goto internal_err;
+ }
+ memset(ret, 0, sizeof(xsltStylesheet));
+
+ ret->omitXmlDeclaration = -1;
+ ret->standalone = -1;
+ ret->decimalFormat = xsltNewDecimalFormat(NULL);
+ ret->indent = -1;
+ ret->errors = 0;
+ ret->warnings = 0;
+ ret->exclPrefixNr = 0;
+ ret->exclPrefixMax = 0;
+ ret->exclPrefixTab = NULL;
+ ret->extInfos = NULL;
+ ret->extrasNr = 0;
+ ret->internalized = 1;
+ ret->literal_result = 0;
+ ret->forwards_compatible = 0;
+ ret->dict = xmlDictCreate();
+#ifdef WITH_XSLT_DEBUG
+ xsltGenericDebug(xsltGenericDebugContext,
+ "creating dictionary for stylesheet\n");
+#endif
+
+ xsltInit();
+
+ return(ret);
+
+internal_err:
+ if (ret != NULL)
+ xsltFreeStylesheet(ret);
+ return(NULL);
+}
+
+/**
+ * xsltAllocateExtra:
+ * @style: an XSLT stylesheet
+ *
+ * Allocate an extra runtime information slot statically while compiling
+ * the stylesheet and return its number
+ *
+ * Returns the number of the slot
+ */
+int
+xsltAllocateExtra(xsltStylesheetPtr style)
+{
+ return(style->extrasNr++);
+}
+
+/**
+ * xsltAllocateExtraCtxt:
+ * @ctxt: an XSLT transformation context
+ *
+ * Allocate an extra runtime information slot at run-time
+ * and return its number
+ * This make sure there is a slot ready in the transformation context
+ *
+ * Returns the number of the slot
+ */
+int
+xsltAllocateExtraCtxt(xsltTransformContextPtr ctxt)
+{
+ if (ctxt->extrasNr >= ctxt->extrasMax) {
+ int i;
+ if (ctxt->extrasNr == 0) {
+ ctxt->extrasMax = 20;
+ ctxt->extras = (xsltRuntimeExtraPtr)
+ xmlMalloc(ctxt->extrasMax * sizeof(xsltRuntimeExtra));
+ if (ctxt->extras == NULL) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "xsltAllocateExtraCtxt: out of memory\n");
+ return(0);
+ }
+ for (i = 0;i < ctxt->extrasMax;i++) {
+ ctxt->extras[i].info = NULL;
+ ctxt->extras[i].deallocate = NULL;
+ ctxt->extras[i].val.ptr = NULL;
+ }
+
+ } else {
+ xsltRuntimeExtraPtr tmp;
+
+ ctxt->extrasMax += 100;
+ tmp = (xsltRuntimeExtraPtr) xmlRealloc(ctxt->extras,
+ ctxt->extrasMax * sizeof(xsltRuntimeExtra));
+ if (tmp == NULL) {
+ xsltTransformError(ctxt, NULL, NULL,
+ "xsltAllocateExtraCtxt: out of memory\n");
+ return(0);
+ }
+ ctxt->extras = tmp;
+ for (i = ctxt->extrasNr;i < ctxt->extrasMax;i++) {
+ ctxt->extras[i].info = NULL;
+ ctxt->extras[i].deallocate = NULL;
+ ctxt->extras[i].val.ptr = NULL;
+ }
+ }
+ }
+ return(ctxt->extrasNr++);
+}
+
+/**
+ * xsltFreeStylesheetList:
+ * @style: an XSLT stylesheet list
+ *
+ * Free up the memory allocated by the list @style
+ */
+static void
+xsltFreeStylesheetList(xsltStylesheetPtr style) {
+ xsltStylesheetPtr next;
+
+ while (style != NULL) {
+ next = style->next;
+ xsltFreeStylesheet(style);
+ style = next;
+ }
+}
+
+/**
+ * xsltCleanupStylesheetTree:
+ *
+ * @doc: the document-node
+ * @node: the element where the stylesheet is rooted at
+ *
+ * Actually @node need not be the document-element, but
+ * currently Libxslt does not support embedded stylesheets.
+ *
+ * Returns 0 if OK, -1 on API or internal errors.
+ */
+static int
+xsltCleanupStylesheetTree(xmlDocPtr doc ATTRIBUTE_UNUSED,
+ xmlNodePtr rootElem ATTRIBUTE_UNUSED)
+{
+#if 0 /* TODO: Currently disabled, since probably not needed. */
+ xmlNodePtr cur;
+
+ if ((doc == NULL) || (rootElem == NULL) ||
+ (rootElem->type != XML_ELEMENT_NODE) ||
+ (doc != rootElem->doc))
+ return(-1);
+
+ /*
+ * Cleanup was suggested by Aleksey Sanin:
+ * Clear the PSVI field to avoid problems if the
+ * node-tree of the stylesheet is intended to be used for
+ * further processing by the user (e.g. for compiling it
+ * once again - although not recommended).
+ */
+
+ cur = rootElem;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE) {
+ /*
+ * Clear the PSVI field.
+ */
+ cur->psvi = NULL;
+ if (cur->children) {
+ cur = cur->children;
+ continue;
+ }
+ }
+
+leave_node:
+ if (cur == rootElem)
+ break;
+ if (cur->next != NULL)
+ cur = cur->next;
+ else {
+ cur = cur->parent;
+ if (cur == NULL)
+ break;
+ goto leave_node;
+ }
+ }
+#endif /* #if 0 */
+ return(0);
+}
+
+/**
+ * xsltFreeStylesheet:
+ * @style: an XSLT stylesheet
+ *
+ * Free up the memory allocated by @style
+ */
+void
+xsltFreeStylesheet(xsltStylesheetPtr style)
+{
+ if (style == NULL)
+ return;
+
+#ifdef XSLT_REFACTORED
+ /*
+ * Start with a cleanup of the main stylesheet's doc.
+ */
+ if ((style->principal == style) && (style->doc))
+ xsltCleanupStylesheetTree(style->doc,
+ xmlDocGetRootElement(style->doc));
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+ /*
+ * Restore changed ns-decls before freeing the document.
+ */
+ if ((style->doc != NULL) &&
+ XSLT_HAS_INTERNAL_NSMAP(style))
+ {
+ xsltRestoreDocumentNamespaces(XSLT_GET_INTERNAL_NSMAP(style),
+ style->doc);
+ }
+#endif /* XSLT_REFACTORED_XSLT_NSCOMP */
+#else
+ /*
+ * Start with a cleanup of the main stylesheet's doc.
+ */
+ if ((style->parent == NULL) && (style->doc))
+ xsltCleanupStylesheetTree(style->doc,
+ xmlDocGetRootElement(style->doc));
+#endif /* XSLT_REFACTORED */
+
+ xsltFreeKeys(style);
+ xsltFreeExts(style);
+ xsltFreeTemplateHashes(style);
+ xsltFreeDecimalFormatList(style);
+ xsltFreeTemplateList(style->templates);
+ xsltFreeAttributeSetsHashes(style);
+ xsltFreeNamespaceAliasHashes(style);
+ xsltFreeStylePreComps(style);
+ /*
+ * Free documents of all included stylsheet modules of this
+ * stylesheet level.
+ */
+ xsltFreeStyleDocuments(style);
+ /*
+ * TODO: Best time to shutdown extension stuff?
+ */
+ xsltShutdownExts(style);
+
+ if (style->variables != NULL)
+ xsltFreeStackElemList(style->variables);
+ if (style->cdataSection != NULL)
+ xmlHashFree(style->cdataSection, NULL);
+ if (style->stripSpaces != NULL)
+ xmlHashFree(style->stripSpaces, NULL);
+ if (style->nsHash != NULL)
+ xmlHashFree(style->nsHash, NULL);
+ if (style->exclPrefixTab != NULL)
+ xmlFree(style->exclPrefixTab);
+ if (style->method != NULL)
+ xmlFree(style->method);
+ if (style->methodURI != NULL)
+ xmlFree(style->methodURI);
+ if (style->version != NULL)
+ xmlFree(style->version);
+ if (style->encoding != NULL)
+ xmlFree(style->encoding);
+ if (style->doctypePublic != NULL)
+ xmlFree(style->doctypePublic);
+ if (style->doctypeSystem != NULL)
+ xmlFree(style->doctypeSystem);
+ if (style->mediaType != NULL)
+ xmlFree(style->mediaType);
+ if (style->attVTs)
+ xsltFreeAVTList(style->attVTs);
+ if (style->imports != NULL)
+ xsltFreeStylesheetList(style->imports);
+
+#ifdef XSLT_REFACTORED
+ /*
+ * If this is the principal stylesheet, then
+ * free its internal data.
+ */
+ if (style->principal == style) {
+ if (style->principalData) {
+ xsltFreePrincipalStylesheetData(style->principalData);
+ style->principalData = NULL;
+ }
+ }
+#endif
+ /*
+ * Better to free the main document of this stylesheet level
+ * at the end - so here.
+ */
+ if (style->doc != NULL) {
+ xmlFreeDoc(style->doc);
+ }
+
+#ifdef WITH_XSLT_DEBUG
+ xsltGenericDebug(xsltGenericDebugContext,
+ "freeing dictionary from stylesheet\n");
+#endif
+ xmlDictFree(style->dict);
+
+ memset(style, -1, sizeof(xsltStylesheet));
+ xmlFree(style);
+}
+
+/************************************************************************
+ * *
+ * Parsing of an XSLT Stylesheet *
+ * *
+ ************************************************************************/
+
+#ifdef XSLT_REFACTORED
+ /*
+ * This is now performed in an optimized way in xsltParseXSLTTemplate.
+ */
+#else
+/**
+ * xsltGetInheritedNsList:
+ * @style: the stylesheet
+ * @template: the template
+ * @node: the current node
+ *
+ * Search all the namespace applying to a given element except the ones
+ * from excluded output prefixes currently in scope. Initialize the
+ * template inheritedNs list with it.
+ *
+ * Returns the number of entries found
+ */
+static int
+xsltGetInheritedNsList(xsltStylesheetPtr style,
+ xsltTemplatePtr template,
+ xmlNodePtr node)
+{
+ xmlNsPtr cur;
+ xmlNsPtr *ret = NULL;
+ int nbns = 0;
+ int maxns = 10;
+ int i;
+
+ if ((style == NULL) || (template == NULL) || (node == NULL) ||
+ (template->inheritedNsNr != 0) || (template->inheritedNs != NULL))
+ return(0);
+ while (node != NULL) {
+ if (node->type == XML_ELEMENT_NODE) {
+ cur = node->nsDef;
+ while (cur != NULL) {
+ if (xmlStrEqual(cur->href, XSLT_NAMESPACE))
+ goto skip_ns;
+
+ if ((cur->prefix != NULL) &&
+ (xsltCheckExtPrefix(style, cur->prefix)))
+ goto skip_ns;
+ /*
+ * Check if this namespace was excluded.
+ * Note that at this point only the exclusions defined
+ * on the topmost stylesheet element are in the exclusion-list.
+ */
+ for (i = 0;i < style->exclPrefixNr;i++) {
+ if (xmlStrEqual(cur->href, style->exclPrefixTab[i]))
+ goto skip_ns;
+ }
+ if (ret == NULL) {
+ ret =
+ (xmlNsPtr *) xmlMalloc((maxns + 1) *
+ sizeof(xmlNsPtr));
+ if (ret == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xsltGetInheritedNsList : out of memory!\n");
+ return(0);
+ }
+ ret[nbns] = NULL;
+ }
+ /*
+ * Skip shadowed namespace bindings.
+ */
+ for (i = 0; i < nbns; i++) {
+ if ((cur->prefix == ret[i]->prefix) ||
+ (xmlStrEqual(cur->prefix, ret[i]->prefix)))
+ break;
+ }
+ if (i >= nbns) {
+ if (nbns >= maxns) {
+ maxns *= 2;
+ ret = (xmlNsPtr *) xmlRealloc(ret,
+ (maxns +
+ 1) *
+ sizeof(xmlNsPtr));
+ if (ret == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xsltGetInheritedNsList : realloc failed!\n");
+ return(0);
+ }
+ }
+ ret[nbns++] = cur;
+ ret[nbns] = NULL;
+ }
+skip_ns:
+ cur = cur->next;
+ }
+ }
+ node = node->parent;
+ }
+ if (nbns != 0) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "template has %d inherited namespaces\n", nbns);
+#endif
+ template->inheritedNsNr = nbns;
+ template->inheritedNs = ret;
+ }
+ return (nbns);
+}
+#endif /* else of XSLT_REFACTORED */
+
+/**
+ * xsltParseStylesheetOutput:
+ * @style: the XSLT stylesheet
+ * @cur: the "output" element
+ *
+ * parse an XSLT stylesheet output element and record
+ * information related to the stylesheet output
+ */
+
+void
+xsltParseStylesheetOutput(xsltStylesheetPtr style, xmlNodePtr cur)
+{
+ xmlChar *elements,
+ *prop;
+ xmlChar *element,
+ *end;
+
+ if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
+ return;
+
+ prop = xmlGetNsProp(cur, (const xmlChar *) "version", NULL);
+ if (prop != NULL) {
+ if (style->version != NULL)
+ xmlFree(style->version);
+ style->version = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *) "encoding", NULL);
+ if (prop != NULL) {
+ if (style->encoding != NULL)
+ xmlFree(style->encoding);
+ style->encoding = prop;
+ }
+
+ /* relaxed to support xt:document
+ * TODO KB: What does "relaxed to support xt:document" mean?
+ */
+ prop = xmlGetNsProp(cur, (const xmlChar *) "method", NULL);
+ if (prop != NULL) {
+ const xmlChar *URI;
+
+ if (style->method != NULL)
+ xmlFree(style->method);
+ style->method = NULL;
+ if (style->methodURI != NULL)
+ xmlFree(style->methodURI);
+ style->methodURI = NULL;
+
+ /*
+ * TODO: Don't use xsltGetQNameURI().
+ */
+ URI = xsltGetQNameURI(cur, &prop);
+ if (prop == NULL) {
+ if (style != NULL) style->errors++;
+ } else if (URI == NULL) {
+ if ((xmlStrEqual(prop, (const xmlChar *) "xml")) ||
+ (xmlStrEqual(prop, (const xmlChar *) "html")) ||
+ (xmlStrEqual(prop, (const xmlChar *) "text"))) {
+ style->method = prop;
+ } else {
+ xsltTransformError(NULL, style, cur,
+ "invalid value for method: %s\n", prop);
+ if (style != NULL) style->warnings++;
+ }
+ } else {
+ style->method = prop;
+ style->methodURI = xmlStrdup(URI);
+ }
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *) "doctype-system", NULL);
+ if (prop != NULL) {
+ if (style->doctypeSystem != NULL)
+ xmlFree(style->doctypeSystem);
+ style->doctypeSystem = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *) "doctype-public", NULL);
+ if (prop != NULL) {
+ if (style->doctypePublic != NULL)
+ xmlFree(style->doctypePublic);
+ style->doctypePublic = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *) "standalone", NULL);
+ if (prop != NULL) {
+ if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
+ style->standalone = 1;
+ } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
+ style->standalone = 0;
+ } else {
+ xsltTransformError(NULL, style, cur,
+ "invalid value for standalone: %s\n", prop);
+ style->errors++;
+ }
+ xmlFree(prop);
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *) "indent", NULL);
+ if (prop != NULL) {
+ if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
+ style->indent = 1;
+ } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
+ style->indent = 0;
+ } else {
+ xsltTransformError(NULL, style, cur,
+ "invalid value for indent: %s\n", prop);
+ style->errors++;
+ }
+ xmlFree(prop);
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *) "omit-xml-declaration", NULL);
+ if (prop != NULL) {
+ if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
+ style->omitXmlDeclaration = 1;
+ } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
+ style->omitXmlDeclaration = 0;
+ } else {
+ xsltTransformError(NULL, style, cur,
+ "invalid value for omit-xml-declaration: %s\n",
+ prop);
+ style->errors++;
+ }
+ xmlFree(prop);
+ }
+
+ elements = xmlGetNsProp(cur, (const xmlChar *) "cdata-section-elements",
+ NULL);
+ if (elements != NULL) {
+ if (style->cdataSection == NULL)
+ style->cdataSection = xmlHashCreate(10);
+ if (style->cdataSection == NULL)
+ return;
+
+ element = elements;
+ while (*element != 0) {
+ while (IS_BLANK(*element))
+ element++;
+ if (*element == 0)
+ break;
+ end = element;
+ while ((*end != 0) && (!IS_BLANK(*end)))
+ end++;
+ element = xmlStrndup(element, end - element);
+ if (element) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "add cdata section output element %s\n",
+ element);
+#endif
+ if (xmlValidateQName(BAD_CAST element, 0) != 0) {
+ xsltTransformError(NULL, style, cur,
+ "Attribute 'cdata-section-elements': The value "
+ "'%s' is not a valid QName.\n", element);
+ xmlFree(element);
+ style->errors++;
+ } else {
+ const xmlChar *URI;
+
+ /*
+ * TODO: Don't use xsltGetQNameURI().
+ */
+ URI = xsltGetQNameURI(cur, &element);
+ if (element == NULL) {
+ /*
+ * TODO: We'll report additionally an error
+ * via the stylesheet's error handling.
+ */
+ xsltTransformError(NULL, style, cur,
+ "Attribute 'cdata-section-elements': The value "
+ "'%s' is not a valid QName.\n", element);
+ style->errors++;
+ } else {
+ xmlNsPtr ns;
+
+ /*
+ * XSLT-1.0 "Each QName is expanded into an
+ * expanded-name using the namespace declarations in
+ * effect on the xsl:output element in which the QName
+ * occurs; if there is a default namespace, it is used
+ * for QNames that do not have a prefix"
+ * NOTE: Fix of bug #339570.
+ */
+ if (URI == NULL) {
+ ns = xmlSearchNs(style->doc, cur, NULL);
+ if (ns != NULL)
+ URI = ns->href;
+ }
+ xmlHashAddEntry2(style->cdataSection, element, URI,
+ (void *) "cdata");
+ xmlFree(element);
+ }
+ }
+ }
+ element = end;
+ }
+ xmlFree(elements);
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *) "media-type", NULL);
+ if (prop != NULL) {
+ if (style->mediaType)
+ xmlFree(style->mediaType);
+ style->mediaType = prop;
+ }
+ if (cur->children != NULL) {
+ xsltParseContentError(style, cur->children);
+ }
+}
+
+/**
+ * xsltParseStylesheetDecimalFormat:
+ * @style: the XSLT stylesheet
+ * @cur: the "decimal-format" element
+ *
+ * <!-- Category: top-level-element -->
+ * <xsl:decimal-format
+ * name = qname, decimal-separator = char, grouping-separator = char,
+ * infinity = string, minus-sign = char, NaN = string, percent = char
+ * per-mille = char, zero-digit = char, digit = char,
+ * pattern-separator = char />
+ *
+ * parse an XSLT stylesheet decimal-format element and
+ * and record the formatting characteristics
+ */
+static void
+xsltParseStylesheetDecimalFormat(xsltStylesheetPtr style, xmlNodePtr cur)
+{
+ xmlChar *prop;
+ xsltDecimalFormatPtr format;
+ xsltDecimalFormatPtr iter;
+
+ if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
+ return;
+
+ format = style->decimalFormat;
+
+ prop = xmlGetNsProp(cur, BAD_CAST("name"), NULL);
+ if (prop != NULL) {
+ format = xsltDecimalFormatGetByName(style, prop);
+ if (format != NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsltParseStylestyleDecimalFormat: %s already exists\n", prop);
+ if (style != NULL) style->warnings++;
+ return;
+ }
+ format = xsltNewDecimalFormat(prop);
+ if (format == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsltParseStylestyleDecimalFormat: failed creating new decimal-format\n");
+ if (style != NULL) style->errors++;
+ return;
+ }
+ /* Append new decimal-format structure */
+ for (iter = style->decimalFormat; iter->next; iter = iter->next)
+ ;
+ if (iter)
+ iter->next = format;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"decimal-separator", NULL);
+ if (prop != NULL) {
+ if (format->decimalPoint != NULL) xmlFree(format->decimalPoint);
+ format->decimalPoint = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"grouping-separator", NULL);
+ if (prop != NULL) {
+ if (format->grouping != NULL) xmlFree(format->grouping);
+ format->grouping = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"infinity", NULL);
+ if (prop != NULL) {
+ if (format->infinity != NULL) xmlFree(format->infinity);
+ format->infinity = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"minus-sign", NULL);
+ if (prop != NULL) {
+ if (format->minusSign != NULL) xmlFree(format->minusSign);
+ format->minusSign = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"NaN", NULL);
+ if (prop != NULL) {
+ if (format->noNumber != NULL) xmlFree(format->noNumber);
+ format->noNumber = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"percent", NULL);
+ if (prop != NULL) {
+ if (format->percent != NULL) xmlFree(format->percent);
+ format->percent = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"per-mille", NULL);
+ if (prop != NULL) {
+ if (format->permille != NULL) xmlFree(format->permille);
+ format->permille = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"zero-digit", NULL);
+ if (prop != NULL) {
+ if (format->zeroDigit != NULL) xmlFree(format->zeroDigit);
+ format->zeroDigit = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"digit", NULL);
+ if (prop != NULL) {
+ if (format->digit != NULL) xmlFree(format->digit);
+ format->digit = prop;
+ }
+
+ prop = xmlGetNsProp(cur, (const xmlChar *)"pattern-separator", NULL);
+ if (prop != NULL) {
+ if (format->patternSeparator != NULL) xmlFree(format->patternSeparator);
+ format->patternSeparator = prop;
+ }
+ if (cur->children != NULL) {
+ xsltParseContentError(style, cur->children);
+ }
+}
+
+/**
+ * xsltParseStylesheetPreserveSpace:
+ * @style: the XSLT stylesheet
+ * @cur: the "preserve-space" element
+ *
+ * parse an XSLT stylesheet preserve-space element and record
+ * elements needing preserving
+ */
+
+static void
+xsltParseStylesheetPreserveSpace(xsltStylesheetPtr style, xmlNodePtr cur) {
+ xmlChar *elements;
+ xmlChar *element, *end;
+
+ if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
+ return;
+
+ elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL);
+ if (elements == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsltParseStylesheetPreserveSpace: missing elements attribute\n");
+ if (style != NULL) style->warnings++;
+ return;
+ }
+
+ if (style->stripSpaces == NULL)
+ style->stripSpaces = xmlHashCreate(10);
+ if (style->stripSpaces == NULL)
+ return;
+
+ element = elements;
+ while (*element != 0) {
+ while (IS_BLANK(*element)) element++;
+ if (*element == 0)
+ break;
+ end = element;
+ while ((*end != 0) && (!IS_BLANK(*end))) end++;
+ element = xmlStrndup(element, end - element);
+ if (element) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "add preserved space element %s\n", element);
+#endif
+ if (xmlStrEqual(element, (const xmlChar *)"*")) {
+ style->stripAll = -1;
+ } else {
+ const xmlChar *URI;
+
+ /*
+ * TODO: Don't use xsltGetQNameURI().
+ */
+ URI = xsltGetQNameURI(cur, &element);
+
+ xmlHashAddEntry2(style->stripSpaces, element, URI,
+ (xmlChar *) "preserve");
+ }
+ xmlFree(element);
+ }
+ element = end;
+ }
+ xmlFree(elements);
+ if (cur->children != NULL) {
+ xsltParseContentError(style, cur->children);
+ }
+}
+
+#ifdef XSLT_REFACTORED
+#else
+/**
+ * xsltParseStylesheetExtPrefix:
+ * @style: the XSLT stylesheet
+ * @template: the "extension-element-prefixes" prefix
+ *
+ * parse an XSLT stylesheet's "extension-element-prefix" attribute value
+ * and register the namespaces of extension instruction.
+ * SPEC "A namespace is designated as an extension namespace by using
+ * an extension-element-prefixes attribute on:
+ * 1) an xsl:stylesheet element
+ * 2) an xsl:extension-element-prefixes attribute on a
+ * literal result element
+ * 3) an extension instruction."
+ */
+static void
+xsltParseStylesheetExtPrefix(xsltStylesheetPtr style, xmlNodePtr cur,
+ int isXsltElem) {
+ xmlChar *prefixes;
+ xmlChar *prefix, *end;
+
+ if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
+ return;
+
+ if (isXsltElem) {
+ /* For xsl:stylesheet/xsl:transform. */
+ prefixes = xmlGetNsProp(cur,
+ (const xmlChar *)"extension-element-prefixes", NULL);
+ } else {
+ /* For literal result elements and extension instructions. */
+ prefixes = xmlGetNsProp(cur,
+ (const xmlChar *)"extension-element-prefixes", XSLT_NAMESPACE);
+ }
+ if (prefixes == NULL) {
+ return;
+ }
+
+ prefix = prefixes;
+ while (*prefix != 0) {
+ while (IS_BLANK(*prefix)) prefix++;
+ if (*prefix == 0)
+ break;
+ end = prefix;
+ while ((*end != 0) && (!IS_BLANK(*end))) end++;
+ prefix = xmlStrndup(prefix, end - prefix);
+ if (prefix) {
+ xmlNsPtr ns;
+
+ if (xmlStrEqual(prefix, (const xmlChar *)"#default"))
+ ns = xmlSearchNs(style->doc, cur, NULL);
+ else
+ ns = xmlSearchNs(style->doc, cur, prefix);
+ if (ns == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:extension-element-prefix : undefined namespace %s\n",
+ prefix);
+ if (style != NULL) style->warnings++;
+ } else {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "add extension prefix %s\n", prefix);
+#endif
+ xsltRegisterExtPrefix(style, prefix, ns->href);
+ }
+ xmlFree(prefix);
+ }
+ prefix = end;
+ }
+ xmlFree(prefixes);
+}
+#endif /* else of XSLT_REFACTORED */
+
+/**
+ * xsltParseStylesheetStripSpace:
+ * @style: the XSLT stylesheet
+ * @cur: the "strip-space" element
+ *
+ * parse an XSLT stylesheet's strip-space element and record
+ * the elements needing stripping
+ */
+
+static void
+xsltParseStylesheetStripSpace(xsltStylesheetPtr style, xmlNodePtr cur) {
+ xmlChar *elements;
+ xmlChar *element, *end;
+
+ if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
+ return;
+
+ elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL);
+ if (elements == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsltParseStylesheetStripSpace: missing elements attribute\n");
+ if (style != NULL) style->warnings++;
+ return;
+ }
+
+ if (style->stripSpaces == NULL)
+ style->stripSpaces = xmlHashCreate(10);
+ if (style->stripSpaces == NULL)
+ return;
+
+ element = elements;
+ while (*element != 0) {
+ while (IS_BLANK(*element)) element++;
+ if (*element == 0)
+ break;
+ end = element;
+ while ((*end != 0) && (!IS_BLANK(*end))) end++;
+ element = xmlStrndup(element, end - element);
+ if (element) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "add stripped space element %s\n", element);
+#endif
+ if (xmlStrEqual(element, (const xmlChar *)"*")) {
+ style->stripAll = 1;
+ } else {
+ const xmlChar *URI;
+
+ /*
+ * TODO: Don't use xsltGetQNameURI().
+ */
+ URI = xsltGetQNameURI(cur, &element);
+
+ xmlHashAddEntry2(style->stripSpaces, element, URI,
+ (xmlChar *) "strip");
+ }
+ xmlFree(element);
+ }
+ element = end;
+ }
+ xmlFree(elements);
+ if (cur->children != NULL) {
+ xsltParseContentError(style, cur->children);
+ }
+}
+
+#ifdef XSLT_REFACTORED
+#else
+/**
+ * xsltParseStylesheetExcludePrefix:
+ * @style: the XSLT stylesheet
+ * @cur: the current point in the stylesheet
+ *
+ * parse an XSLT stylesheet exclude prefix and record
+ * namespaces needing stripping
+ *
+ * Returns the number of Excluded prefixes added at that level
+ */
+
+static int
+xsltParseStylesheetExcludePrefix(xsltStylesheetPtr style, xmlNodePtr cur,
+ int isXsltElem)
+{
+ int nb = 0;
+ xmlChar *prefixes;
+ xmlChar *prefix, *end;
+
+ if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
+ return(0);
+
+ if (isXsltElem)
+ prefixes = xmlGetNsProp(cur,
+ (const xmlChar *)"exclude-result-prefixes", NULL);
+ else
+ prefixes = xmlGetNsProp(cur,
+ (const xmlChar *)"exclude-result-prefixes", XSLT_NAMESPACE);
+
+ if (prefixes == NULL) {
+ return(0);
+ }
+
+ prefix = prefixes;
+ while (*prefix != 0) {
+ while (IS_BLANK(*prefix)) prefix++;
+ if (*prefix == 0)
+ break;
+ end = prefix;
+ while ((*end != 0) && (!IS_BLANK(*end))) end++;
+ prefix = xmlStrndup(prefix, end - prefix);
+ if (prefix) {
+ xmlNsPtr ns;
+
+ if (xmlStrEqual(prefix, (const xmlChar *)"#default"))
+ ns = xmlSearchNs(style->doc, cur, NULL);
+ else
+ ns = xmlSearchNs(style->doc, cur, prefix);
+ if (ns == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsl:exclude-result-prefixes : undefined namespace %s\n",
+ prefix);
+ if (style != NULL) style->warnings++;
+ } else {
+ if (exclPrefixPush(style, (xmlChar *) ns->href) >= 0) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "exclude result prefix %s\n", prefix);
+#endif
+ nb++;
+ }
+ }
+ xmlFree(prefix);
+ }
+ prefix = end;
+ }
+ xmlFree(prefixes);
+ return(nb);
+}
+#endif /* else of XSLT_REFACTORED */
+
+#ifdef XSLT_REFACTORED
+
+/*
+* xsltTreeEnsureXMLDecl:
+* @doc: the doc
+*
+* BIG NOTE:
+* This was copy&pasted from Libxml2's xmlTreeEnsureXMLDecl() in "tree.c".
+* Ensures that there is an XML namespace declaration on the doc.
+*
+* Returns the XML ns-struct or NULL on API and internal errors.
+*/
+static xmlNsPtr
+xsltTreeEnsureXMLDecl(xmlDocPtr doc)
+{
+ if (doc == NULL)
+ return (NULL);
+ if (doc->oldNs != NULL)
+ return (doc->oldNs);
+ {
+ xmlNsPtr ns;
+ ns = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
+ if (ns == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xsltTreeEnsureXMLDecl: Failed to allocate "
+ "the XML namespace.\n");
+ return (NULL);
+ }
+ memset(ns, 0, sizeof(xmlNs));
+ ns->type = XML_LOCAL_NAMESPACE;
+ /*
+ * URGENT TODO: revisit this.
+ */
+#ifdef LIBXML_NAMESPACE_DICT
+ if (doc->dict)
+ ns->href = xmlDictLookup(doc->dict, XML_XML_NAMESPACE, -1);
+ else
+ ns->href = xmlStrdup(XML_XML_NAMESPACE);
+#else
+ ns->href = xmlStrdup(XML_XML_NAMESPACE);
+#endif
+ ns->prefix = xmlStrdup((const xmlChar *)"xml");
+ doc->oldNs = ns;
+ return (ns);
+ }
+}
+
+/*
+* xsltTreeAcquireStoredNs:
+* @doc: the doc
+* @nsName: the namespace name
+* @prefix: the prefix
+*
+* BIG NOTE:
+* This was copy&pasted from Libxml2's xmlDOMWrapStoreNs() in "tree.c".
+* Creates or reuses an xmlNs struct on doc->oldNs with
+* the given prefix and namespace name.
+*
+* Returns the aquired ns struct or NULL in case of an API
+* or internal error.
+*/
+static xmlNsPtr
+xsltTreeAcquireStoredNs(xmlDocPtr doc,
+ const xmlChar *nsName,
+ const xmlChar *prefix)
+{
+ xmlNsPtr ns;
+
+ if (doc == NULL)
+ return (NULL);
+ if (doc->oldNs != NULL)
+ ns = doc->oldNs;
+ else
+ ns = xsltTreeEnsureXMLDecl(doc);
+ if (ns == NULL)
+ return (NULL);
+ if (ns->next != NULL) {
+ /* Reuse. */
+ ns = ns->next;
+ while (ns != NULL) {
+ if ((ns->prefix == NULL) != (prefix == NULL)) {
+ /* NOP */
+ } else if (prefix == NULL) {
+ if (xmlStrEqual(ns->href, nsName))
+ return (ns);
+ } else {
+ if ((ns->prefix[0] == prefix[0]) &&
+ xmlStrEqual(ns->prefix, prefix) &&
+ xmlStrEqual(ns->href, nsName))
+ return (ns);
+
+ }
+ if (ns->next == NULL)
+ break;
+ ns = ns->next;
+ }
+ }
+ /* Create. */
+ ns->next = xmlNewNs(NULL, nsName, prefix);
+ return (ns->next);
+}
+
+/**
+ * xsltLREBuildEffectiveNs:
+ *
+ * Apply ns-aliasing on the namespace of the given @elem and
+ * its attributes.
+ */
+static int
+xsltLREBuildEffectiveNs(xsltCompilerCtxtPtr cctxt,
+ xmlNodePtr elem)
+{
+ xmlNsPtr ns;
+ xsltNsAliasPtr alias;
+
+ if ((cctxt == NULL) || (elem == NULL))
+ return(-1);
+ if ((cctxt->nsAliases == NULL) || (! cctxt->hasNsAliases))
+ return(0);
+
+ alias = cctxt->nsAliases;
+ while (alias != NULL) {
+ if ( /* If both namespaces are NULL... */
+ ( (elem->ns == NULL) &&
+ ((alias->literalNs == NULL) ||
+ (alias->literalNs->href == NULL)) ) ||
+ /* ... or both namespace are equal */
+ ( (elem->ns != NULL) &&
+ (alias->literalNs != NULL) &&
+ xmlStrEqual(elem->ns->href, alias->literalNs->href) ) )
+ {
+ if ((alias->targetNs != NULL) &&
+ (alias->targetNs->href != NULL))
+ {
+ /*
+ * Convert namespace.
+ */
+ if (elem->doc == alias->docOfTargetNs) {
+ /*
+ * This is the nice case: same docs.
+ * This will eventually assign a ns-decl which
+ * is shadowed, but this has no negative effect on
+ * the generation of the result tree.
+ */
+ elem->ns = alias->targetNs;
+ } else {
+ /*
+ * This target xmlNs originates from a different
+ * stylesheet tree. Try to locate it in the
+ * in-scope namespaces.
+ * OPTIMIZE TODO: Use the compiler-node-info inScopeNs.
+ */
+ ns = xmlSearchNs(elem->doc, elem,
+ alias->targetNs->prefix);
+ /*
+ * If no matching ns-decl found, then assign a
+ * ns-decl stored in xmlDoc.
+ */
+ if ((ns == NULL) ||
+ (! xmlStrEqual(ns->href, alias->targetNs->href)))
+ {
+ /*
+ * BIG NOTE: The use of xsltTreeAcquireStoredNs()
+ * is not very efficient, but currently I don't
+ * see an other way of *safely* changing a node's
+ * namespace, since the xmlNs struct in
+ * alias->targetNs might come from an other
+ * stylesheet tree. So we need to anchor it in the
+ * current document, without adding it to the tree,
+ * which would otherwise change the in-scope-ns
+ * semantic of the tree.
+ */
+ ns = xsltTreeAcquireStoredNs(elem->doc,
+ alias->targetNs->href,
+ alias->targetNs->prefix);
+
+ if (ns == NULL) {
+ xsltTransformError(NULL, cctxt->style, elem,
+ "Internal error in "
+ "xsltLREBuildEffectiveNs(): "
+ "failed to acquire a stored "
+ "ns-declaration.\n");
+ cctxt->style->errors++;
+ return(-1);
+
+ }
+ }
+ elem->ns = ns;
+ }
+ } else {
+ /*
+ * Move into or leave in the NULL namespace.
+ */
+ elem->ns = NULL;
+ }
+ break;
+ }
+ alias = alias->next;
+ }
+ /*
+ * Same with attributes of literal result elements.
+ */
+ if (elem->properties != NULL) {
+ xmlAttrPtr attr = elem->properties;
+
+ while (attr != NULL) {
+ if (attr->ns == NULL) {
+ attr = attr->next;
+ continue;
+ }
+ alias = cctxt->nsAliases;
+ while (alias != NULL) {
+ if ( /* If both namespaces are NULL... */
+ ( (elem->ns == NULL) &&
+ ((alias->literalNs == NULL) ||
+ (alias->literalNs->href == NULL)) ) ||
+ /* ... or both namespace are equal */
+ ( (elem->ns != NULL) &&
+ (alias->literalNs != NULL) &&
+ xmlStrEqual(elem->ns->href, alias->literalNs->href) ) )
+ {
+ if ((alias->targetNs != NULL) &&
+ (alias->targetNs->href != NULL))
+ {
+ if (elem->doc == alias->docOfTargetNs) {
+ elem->ns = alias->targetNs;
+ } else {
+ ns = xmlSearchNs(elem->doc, elem,
+ alias->targetNs->prefix);
+ if ((ns == NULL) ||
+ (! xmlStrEqual(ns->href, alias->targetNs->href)))
+ {
+ ns = xsltTreeAcquireStoredNs(elem->doc,
+ alias->targetNs->href,
+ alias->targetNs->prefix);
+
+ if (ns == NULL) {
+ xsltTransformError(NULL, cctxt->style, elem,
+ "Internal error in "
+ "xsltLREBuildEffectiveNs(): "
+ "failed to acquire a stored "
+ "ns-declaration.\n");
+ cctxt->style->errors++;
+ return(-1);
+
+ }
+ }
+ elem->ns = ns;
+ }
+ } else {
+ /*
+ * Move into or leave in the NULL namespace.
+ */
+ elem->ns = NULL;
+ }
+ break;
+ }
+ alias = alias->next;
+ }
+
+ attr = attr->next;
+ }
+ }
+ return(0);
+}
+
+/**
+ * xsltLREBuildEffectiveNsNodes:
+ *
+ * Computes the effective namespaces nodes for a literal result
+ * element.
+ * @effectiveNs is the set of effective ns-nodes
+ * on the literal result element, which will be added to the result
+ * element if not already existing in the result tree.
+ * This means that excluded namespaces (via exclude-result-prefixes,
+ * extension-element-prefixes and the XSLT namespace) not added
+ * to the set.
+ * Namespace-aliasing was applied on the @effectiveNs.
+ */
+static int
+xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt,
+ xsltStyleItemLRElementInfoPtr item,
+ xmlNodePtr elem,
+ int isLRE)
+{
+ xmlNsPtr ns, tmpns;
+ xsltEffectiveNsPtr effNs, lastEffNs = NULL;
+ int i, j, holdByElem;
+ xsltPointerListPtr extElemNs = cctxt->inode->extElemNs;
+ xsltPointerListPtr exclResultNs = cctxt->inode->exclResultNs;
+
+ if ((cctxt == NULL) || (cctxt->inode == NULL) || (elem == NULL) ||
+ (item == NULL) || (item->effectiveNs != NULL))
+ return(-1);
+
+ if (item->inScopeNs == NULL)
+ return(0);
+
+ extElemNs = cctxt->inode->extElemNs;
+ exclResultNs = cctxt->inode->exclResultNs;
+
+ for (i = 0; i < item->inScopeNs->totalNumber; i++) {
+ ns = item->inScopeNs->list[i];
+ /*
+ * Skip namespaces designated as excluded namespaces
+ * -------------------------------------------------
+ *
+ * XSLT-20 TODO: In XSLT 2.0 we need to keep namespaces
+ * which are target namespaces of namespace-aliases
+ * regardless if designated as excluded.
+ *
+ * Exclude the XSLT namespace.
+ */
+ if (xmlStrEqual(ns->href, XSLT_NAMESPACE))
+ goto skip_ns;
+
+ /*
+ * Apply namespace aliasing
+ * ------------------------
+ *
+ * SPEC XSLT 2.0
+ * "- A namespace node whose string value is a literal namespace
+ * URI is not copied to the result tree.
+ * - A namespace node whose string value is a target namespace URI
+ * is copied to the result tree, whether or not the URI
+ * identifies an excluded namespace."
+ *
+ * NOTE: The ns-aliasing machanism is non-cascading.
+ * (checked with Saxon, Xalan and MSXML .NET).
+ * URGENT TODO: is style->nsAliases the effective list of
+ * ns-aliases, or do we need to lookup the whole
+ * import-tree?
+ * TODO: Get rid of import-tree lookup.
+ */
+ if (cctxt->hasNsAliases) {
+ xsltNsAliasPtr alias;
+ /*
+ * First check for being a target namespace.
+ */
+ alias = cctxt->nsAliases;
+ do {
+ /*
+ * TODO: Is xmlns="" handled already?
+ */
+ if ((alias->targetNs != NULL) &&
+ (xmlStrEqual(alias->targetNs->href, ns->href)))
+ {
+ /*
+ * Recognized as a target namespace; use it regardless
+ * if excluded otherwise.
+ */
+ goto add_effective_ns;
+ }
+ alias = alias->next;
+ } while (alias != NULL);
+
+ alias = cctxt->nsAliases;
+ do {
+ /*
+ * TODO: Is xmlns="" handled already?
+ */
+ if ((alias->literalNs != NULL) &&
+ (xmlStrEqual(alias->literalNs->href, ns->href)))
+ {
+ /*
+ * Recognized as an namespace alias; do not use it.
+ */
+ goto skip_ns;
+ }
+ alias = alias->next;
+ } while (alias != NULL);
+ }
+
+ /*
+ * Exclude excluded result namespaces.
+ */
+ if (exclResultNs) {
+ for (j = 0; j < exclResultNs->number; j++)
+ if (xmlStrEqual(ns->href, BAD_CAST exclResultNs->items[j]))
+ goto skip_ns;
+ }
+ /*
+ * Exclude extension-element namespaces.
+ */
+ if (extElemNs) {
+ for (j = 0; j < extElemNs->number; j++)
+ if (xmlStrEqual(ns->href, BAD_CAST extElemNs->items[j]))
+ goto skip_ns;
+ }
+
+add_effective_ns:
+ /*
+ * OPTIMIZE TODO: This information may not be needed.
+ */
+ if (isLRE && (elem->nsDef != NULL)) {
+ holdByElem = 0;
+ tmpns = elem->nsDef;
+ do {
+ if (tmpns == ns) {
+ holdByElem = 1;
+ break;
+ }
+ tmpns = tmpns->next;
+ } while (tmpns != NULL);
+ } else
+ holdByElem = 0;
+
+
+ /*
+ * Add the effective namespace declaration.
+ */
+ effNs = (xsltEffectiveNsPtr) xmlMalloc(sizeof(xsltEffectiveNs));
+ if (effNs == NULL) {
+ xsltTransformError(NULL, cctxt->style, elem,
+ "Internal error in xsltLREBuildEffectiveNs(): "
+ "failed to allocate memory.\n");
+ cctxt->style->errors++;
+ return(-1);
+ }
+ if (cctxt->psData->effectiveNs == NULL) {
+ cctxt->psData->effectiveNs = effNs;
+ effNs->nextInStore = NULL;
+ } else {
+ effNs->nextInStore = cctxt->psData->effectiveNs;
+ cctxt->psData->effectiveNs = effNs;
+ }
+
+ effNs->next = NULL;
+ effNs->prefix = ns->prefix;
+ effNs->nsName = ns->href;
+ effNs->holdByElem = holdByElem;
+
+ if (lastEffNs == NULL)
+ item->effectiveNs = effNs;
+ else
+ lastEffNs->next = effNs;
+ lastEffNs = effNs;
+
+skip_ns:
+ {}
+ }
+ return(0);
+}
+
+
+/**
+ * xsltLREInfoCreate:
+ *
+ * @isLRE: indicates if the given @elem is a literal result element
+ *
+ * Creates a new info for a literal result element.
+ */
+static int
+xsltLREInfoCreate(xsltCompilerCtxtPtr cctxt,
+ xmlNodePtr elem,
+ int isLRE)
+{
+ xsltStyleItemLRElementInfoPtr item;
+
+ if ((cctxt == NULL) || (cctxt->inode == NULL))
+ return(-1);
+
+ item = (xsltStyleItemLRElementInfoPtr)
+ xmlMalloc(sizeof(xsltStyleItemLRElementInfo));
+ if (item == NULL) {
+ xsltTransformError(NULL, cctxt->style, NULL,
+ "Internal error in xsltLREInfoCreate(): "
+ "memory allocation failed.\n");
+ cctxt->style->errors++;
+ return(-1);
+ }
+ memset(item, 0, sizeof(xsltStyleItemLRElementInfo));
+ item->type = XSLT_FUNC_LITERAL_RESULT_ELEMENT;
+ /*
+ * Store it in the stylesheet.
+ */
+ item->next = cctxt->style->preComps;
+ cctxt->style->preComps = (xsltElemPreCompPtr) item;
+ /*
+ * @inScopeNs are used for execution of XPath expressions
+ * in AVTs.
+ */
+ item->inScopeNs = cctxt->inode->inScopeNs;
+
+ if (elem)
+ xsltLREBuildEffectiveNsNodes(cctxt, item, elem, isLRE);
+
+ cctxt->inode->litResElemInfo = item;
+ cctxt->inode->nsChanged = 0;
+ cctxt->maxLREs++;
+ return(0);
+}
+
+/**
+ * xsltCompilerVarInfoPush:
+ * @cctxt: the compilation context
+ *
+ * Pushes a new var/param info onto the stack.
+ *
+ * Returns the acquired variable info.
+ */
+static xsltVarInfoPtr
+xsltCompilerVarInfoPush(xsltCompilerCtxtPtr cctxt,
+ xmlNodePtr inst,
+ const xmlChar *name,
+ const xmlChar *nsName)
+{
+ xsltVarInfoPtr ivar;
+
+ if ((cctxt->ivar != NULL) && (cctxt->ivar->next != NULL)) {
+ ivar = cctxt->ivar->next;
+ } else if ((cctxt->ivar == NULL) && (cctxt->ivars != NULL)) {
+ ivar = cctxt->ivars;
+ } else {
+ ivar = (xsltVarInfoPtr) xmlMalloc(sizeof(xsltVarInfo));
+ if (ivar == NULL) {
+ xsltTransformError(NULL, cctxt->style, inst,
+ "xsltParseInScopeVarPush: xmlMalloc() failed!\n");
+ cctxt->style->errors++;
+ return(NULL);
+ }
+ /* memset(retVar, 0, sizeof(xsltInScopeVar)); */
+ if (cctxt->ivars == NULL) {
+ cctxt->ivars = ivar;
+ ivar->prev = NULL;
+ } else {
+ cctxt->ivar->next = ivar;
+ ivar->prev = cctxt->ivar;
+ }
+ cctxt->ivar = ivar;
+ ivar->next = NULL;
+ }
+ ivar->depth = cctxt->depth;
+ ivar->name = name;
+ ivar->nsName = nsName;
+ return(ivar);
+}
+
+/**
+ * xsltCompilerVarInfoPop:
+ * @cctxt: the compilation context
+ *
+ * Pops all var/param infos from the stack, which
+ * have the current depth.
+ */
+static void
+xsltCompilerVarInfoPop(xsltCompilerCtxtPtr cctxt)
+{
+
+ while ((cctxt->ivar != NULL) &&
+ (cctxt->ivar->depth > cctxt->depth))
+ {
+ cctxt->ivar = cctxt->ivar->prev;
+ }
+}
+
+/*
+* xsltCompilerNodePush:
+*
+* @cctxt: the compilation context
+* @node: the node to be pushed (this can also be the doc-node)
+*
+*
+*
+* Returns the current node info structure or
+* NULL in case of an internal error.
+*/
+static xsltCompilerNodeInfoPtr
+xsltCompilerNodePush(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
+{
+ xsltCompilerNodeInfoPtr inode, iprev;
+
+ if ((cctxt->inode != NULL) && (cctxt->inode->next != NULL)) {
+ inode = cctxt->inode->next;
+ } else if ((cctxt->inode == NULL) && (cctxt->inodeList != NULL)) {
+ inode = cctxt->inodeList;
+ } else {
+ /*
+ * Create a new node-info.
+ */
+ inode = (xsltCompilerNodeInfoPtr)
+ xmlMalloc(sizeof(xsltCompilerNodeInfo));
+ if (inode == NULL) {
+ xsltTransformError(NULL, cctxt->style, NULL,
+ "xsltCompilerNodePush: malloc failed.\n");
+ return(NULL);
+ }
+ memset(inode, 0, sizeof(xsltCompilerNodeInfo));
+ if (cctxt->inodeList == NULL)
+ cctxt->inodeList = inode;
+ else {
+ cctxt->inodeLast->next = inode;
+ inode->prev = cctxt->inodeLast;
+ }
+ cctxt->inodeLast = inode;
+ cctxt->maxNodeInfos++;
+ if (cctxt->inode == NULL) {
+ cctxt->inode = inode;
+ /*
+ * Create an initial literal result element info for
+ * the root of the stylesheet.
+ */
+ xsltLREInfoCreate(cctxt, NULL, 0);
+ }
+ }
+ cctxt->depth++;
+ cctxt->inode = inode;
+ /*
+ * REVISIT TODO: Keep the reset always complete.
+ * NOTE: Be carefull with the @node, since it might be
+ * a doc-node.
+ */
+ inode->node = node;
+ inode->depth = cctxt->depth;
+ inode->templ = NULL;
+ inode->category = XSLT_ELEMENT_CATEGORY_XSLT;
+ inode->type = 0;
+ inode->item = NULL;
+ inode->curChildType = 0;
+ inode->extContentHandled = 0;
+ inode->isRoot = 0;
+
+ if (inode->prev != NULL) {
+ iprev = inode->prev;
+ /*
+ * Inherit the following information:
+ * ---------------------------------
+ *
+ * In-scope namespaces
+ */
+ inode->inScopeNs = iprev->inScopeNs;
+ /*
+ * Info for literal result elements
+ */
+ inode->litResElemInfo = iprev->litResElemInfo;
+ inode->nsChanged = iprev->nsChanged;
+ /*
+ * Excluded result namespaces
+ */
+ inode->exclResultNs = iprev->exclResultNs;
+ /*
+ * Extension instruction namespaces
+ */
+ inode->extElemNs = iprev->extElemNs;
+ /*
+ * Whitespace preservation
+ */
+ inode->preserveWhitespace = iprev->preserveWhitespace;
+ /*
+ * Forwards-compatible mode
+ */
+ inode->forwardsCompat = iprev->forwardsCompat;
+ } else {
+ inode->inScopeNs = NULL;
+ inode->exclResultNs = NULL;
+ inode->extElemNs = NULL;
+ inode->preserveWhitespace = 0;
+ inode->forwardsCompat = 0;
+ }
+
+ return(inode);
+}
+
+/*
+* xsltCompilerNodePop:
+*
+* @cctxt: the compilation context
+* @node: the node to be pushed (this can also be the doc-node)
+*
+* Pops the current node info.
+*/
+static void
+xsltCompilerNodePop(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
+{
+ if (cctxt->inode == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xsltCompilerNodePop: Top-node mismatch.\n");
+ return;
+ }
+ /*
+ * NOTE: Be carefull with the @node, since it might be
+ * a doc-node.
+ */
+ if (cctxt->inode->node != node) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xsltCompilerNodePop: Node mismatch.\n");
+ goto mismatch;
+ }
+ if (cctxt->inode->depth != cctxt->depth) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xsltCompilerNodePop: Depth mismatch.\n");
+ goto mismatch;
+ }
+ cctxt->depth--;
+ /*
+ * Pop information of variables.
+ */
+ if ((cctxt->ivar) && (cctxt->ivar->depth > cctxt->depth))
+ xsltCompilerVarInfoPop(cctxt);
+
+ cctxt->inode = cctxt->inode->prev;
+ if (cctxt->inode != NULL)
+ cctxt->inode->curChildType = 0;
+ return;
+
+mismatch:
+ {
+ const xmlChar *nsName = NULL, *name = NULL;
+ const xmlChar *infnsName = NULL, *infname = NULL;
+
+ if (node) {
+ if (node->type == XML_ELEMENT_NODE) {
+ name = node->name;
+ if (node->ns != NULL)
+ nsName = node->ns->href;
+ else
+ nsName = BAD_CAST "";
+ } else {
+ name = BAD_CAST "#document";
+ nsName = BAD_CAST "";
+ }
+ } else
+ name = BAD_CAST "Not given";
+
+ if (cctxt->inode->node) {
+ if (node->type == XML_ELEMENT_NODE) {
+ infname = cctxt->inode->node->name;
+ if (cctxt->inode->node->ns != NULL)
+ infnsName = cctxt->inode->node->ns->href;
+ else
+ infnsName = BAD_CAST "";
+ } else {
+ infname = BAD_CAST "#document";
+ infnsName = BAD_CAST "";
+ }
+ } else
+ infname = BAD_CAST "Not given";
+
+
+ xmlGenericError(xmlGenericErrorContext,
+ "xsltCompilerNodePop: Given : '%s' URI '%s'\n",
+ name, nsName);
+ xmlGenericError(xmlGenericErrorContext,
+ "xsltCompilerNodePop: Expected: '%s' URI '%s'\n",
+ infname, infnsName);
+ }
+}
+
+/*
+* xsltCompilerBuildInScopeNsList:
+*
+* Create and store the list of in-scope namespaces for the given
+* node in the stylesheet. If there are no changes in the in-scope
+* namespaces then the last ns-info of the ancestor axis will be returned.
+* Compilation-time only.
+*
+* Returns the ns-info or NULL if there are no namespaces in scope.
+*/
+static xsltNsListContainerPtr
+xsltCompilerBuildInScopeNsList(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
+{
+ xsltNsListContainerPtr nsi = NULL;
+ xmlNsPtr *list = NULL, ns;
+ int i, maxns = 5;
+ /*
+ * Create a new ns-list for this position in the node-tree.
+ * xmlGetNsList() will return NULL, if there are no ns-decls in the
+ * tree. Note that the ns-decl for the XML namespace is not added
+ * to the resulting list; the XPath module handles the XML namespace
+ * internally.
+ */
+ while (node != NULL) {
+ if (node->type == XML_ELEMENT_NODE) {
+ ns = node->nsDef;
+ while (ns != NULL) {
+ if (nsi == NULL) {
+ nsi = (xsltNsListContainerPtr)
+ xmlMalloc(sizeof(xsltNsListContainer));
+ if (nsi == NULL) {
+ xsltTransformError(NULL, cctxt->style, NULL,
+ "xsltCompilerBuildInScopeNsList: "
+ "malloc failed!\n");
+ goto internal_err;
+ }
+ memset(nsi, 0, sizeof(xsltNsListContainer));
+ nsi->list =
+ (xmlNsPtr *) xmlMalloc(maxns * sizeof(xmlNsPtr));
+ if (nsi->list == NULL) {
+ xsltTransformError(NULL, cctxt->style, NULL,
+ "xsltCompilerBuildInScopeNsList: "
+ "malloc failed!\n");
+ goto internal_err;
+ }
+ nsi->list[0] = NULL;
+ }
+ /*
+ * Skip shadowed namespace bindings.
+ */
+ for (i = 0; i < nsi->totalNumber; i++) {
+ if ((ns->prefix == nsi->list[i]->prefix) ||
+ (xmlStrEqual(ns->prefix, nsi->list[i]->prefix)))
+ break;
+ }
+ if (i >= nsi->totalNumber) {
+ if (nsi->totalNumber +1 >= maxns) {
+ maxns *= 2;
+ nsi->list =
+ (xmlNsPtr *) xmlRealloc(nsi->list,
+ maxns * sizeof(xmlNsPtr));
+ if (nsi->list == NULL) {
+ xsltTransformError(NULL, cctxt->style, NULL,
+ "xsltCompilerBuildInScopeNsList: "
+ "realloc failed!\n");
+ goto internal_err;
+ }
+ }
+ nsi->list[nsi->totalNumber++] = ns;
+ nsi->list[nsi->totalNumber] = NULL;
+ }
+
+ ns = ns->next;
+ }
+ }
+ node = node->parent;
+ }
+ if (nsi == NULL)
+ return(NULL);
+ /*
+ * Move the default namespace to last position.
+ */
+ nsi->xpathNumber = nsi->totalNumber;
+ for (i = 0; i < nsi->totalNumber; i++) {
+ if (nsi->list[i]->prefix == NULL) {
+ ns = nsi->list[i];
+ nsi->list[i] = nsi->list[nsi->totalNumber-1];
+ nsi->list[nsi->totalNumber-1] = ns;
+ nsi->xpathNumber--;
+ break;
+ }
+ }
+ /*
+ * Store the ns-list in the stylesheet.
+ */
+ if (xsltPointerListAddSize(
+ (xsltPointerListPtr)cctxt->psData->inScopeNamespaces,
+ (void *) nsi, 5) == -1)
+ {
+ xmlFree(nsi);
+ nsi = NULL;
+ xsltTransformError(NULL, cctxt->style, NULL,
+ "xsltCompilerBuildInScopeNsList: failed to add ns-info.\n");
+ goto internal_err;
+ }
+ /*
+ * Notify of change in status wrt namespaces.
+ */
+ if (cctxt->inode != NULL)
+ cctxt->inode->nsChanged = 1;
+
+ return(nsi);
+
+internal_err:
+ if (list != NULL)
+ xmlFree(list);
+ cctxt->style->errors++;
+ return(NULL);
+}
+
+static int
+xsltParseNsPrefixList(xsltCompilerCtxtPtr cctxt,
+ xsltPointerListPtr list,
+ xmlNodePtr node,
+ const xmlChar *value)
+{
+ xmlChar *cur, *end;
+ xmlNsPtr ns;
+
+ if ((cctxt == NULL) || (value == NULL) || (list == NULL))
+ return(-1);
+
+ list->number = 0;
+
+ cur = (xmlChar *) value;
+ while (*cur != 0) {
+ while (IS_BLANK(*cur)) cur++;
+ if (*cur == 0)
+ break;
+ end = cur;
+ while ((*end != 0) && (!IS_BLANK(*end))) end++;
+ cur = xmlStrndup(cur, end - cur);
+ if (cur == NULL) {
+ cur = end;
+ continue;
+ }
+ /*
+ * TODO: Export and use xmlSearchNsByPrefixStrict()
+ * in Libxml2, tree.c, since xmlSearchNs() is in most
+ * cases not efficient and in some cases not correct.
+ *
+ * XSLT-2 TODO: XSLT 2.0 allows an additional "#all" value.
+ */
+ if ((cur[0] == '#') &&
+ xmlStrEqual(cur, (const xmlChar *)"#default"))
+ ns = xmlSearchNs(cctxt->style->doc, node, NULL);
+ else
+ ns = xmlSearchNs(cctxt->style->doc, node, cur);
+
+ if (ns == NULL) {
+ /*
+ * TODO: Better to report the attr-node, otherwise
+ * the user won't know which attribute was invalid.
+ */
+ xsltTransformError(NULL, cctxt->style, node,
+ "No namespace binding in scope for prefix '%s'.\n", cur);
+ /*
+ * XSLT-1.0: "It is an error if there is no namespace
+ * bound to the prefix on the element bearing the
+ * exclude-result-prefixes or xsl:exclude-result-prefixes
+ * attribute."
+ */
+ cctxt->style->errors++;
+ } else {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "resolved prefix '%s'\n", cur);
+#endif
+ /*
+ * Note that we put the namespace name into the dict.
+ */
+ if (xsltPointerListAddSize(list,
+ (void *) xmlDictLookup(cctxt->style->dict,
+ ns->href, -1), 5) == -1)
+ {
+ xmlFree(cur);
+ goto internal_err;
+ }
+ }
+ xmlFree(cur);
+
+ cur = end;
+ }
+ return(0);
+
+internal_err:
+ cctxt->style->errors++;
+ return(-1);
+}
+
+/**
+ * xsltCompilerUtilsCreateMergedList:
+ * @dest: the destination list (optional)
+ * @first: the first list
+ * @second: the second list (optional)
+ *
+ * Appends the content of @second to @first into @destination.
+ * If @destination is NULL a new list will be created.
+ *
+ * Returns the merged list of items or NULL if there's nothing to merge.
+ */
+static xsltPointerListPtr
+xsltCompilerUtilsCreateMergedList(xsltPointerListPtr first,
+ xsltPointerListPtr second)
+{
+ xsltPointerListPtr ret;
+ size_t num;
+
+ if (first)
+ num = first->number;
+ else
+ num = 0;
+ if (second)
+ num += second->number;
+ if (num == 0)
+ return(NULL);
+ ret = xsltPointerListCreate(num);
+ if (ret == NULL)
+ return(NULL);
+ /*
+ * Copy contents.
+ */
+ if ((first != NULL) && (first->number != 0)) {
+ memcpy(ret->items, first->items,
+ first->number * sizeof(void *));
+ if ((second != NULL) && (second->number != 0))
+ memcpy(ret->items + first->number, second->items,
+ second->number * sizeof(void *));
+ } else if ((second != NULL) && (second->number != 0))
+ memcpy(ret->items, (void *) second->items,
+ second->number * sizeof(void *));
+ ret->number = num;
+ return(ret);
+}
+
+/*
+* xsltParseExclResultPrefixes:
+*
+* Create and store the list of in-scope namespaces for the given
+* node in the stylesheet. If there are no changes in the in-scope
+* namespaces then the last ns-info of the ancestor axis will be returned.
+* Compilation-time only.
+*
+* Returns the ns-info or NULL if there are no namespaces in scope.
+*/
+static xsltPointerListPtr
+xsltParseExclResultPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,
+ xsltPointerListPtr def,
+ int instrCategory)
+{
+ xsltPointerListPtr list = NULL;
+ xmlChar *value;
+ xmlAttrPtr attr;
+
+ if ((cctxt == NULL) || (node == NULL))
+ return(NULL);
+
+ if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT)
+ attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes", NULL);
+ else
+ attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes",
+ XSLT_NAMESPACE);
+ if (attr == NULL)
+ return(def);
+
+ if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) {
+ /*
+ * Mark the XSLT attr.
+ */
+ attr->psvi = (void *) xsltXSLTAttrMarker;
+ }
+
+ if ((attr->children != NULL) &&
+ (attr->children->content != NULL))
+ value = attr->children->content;
+ else {
+ xsltTransformError(NULL, cctxt->style, node,
+ "Attribute 'exclude-result-prefixes': Invalid value.\n");
+ cctxt->style->errors++;
+ return(def);
+ }
+
+ if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node,
+ BAD_CAST value) != 0)
+ goto exit;
+ if (cctxt->tmpList->number == 0)
+ goto exit;
+ /*
+ * Merge the list with the inherited list.
+ */
+ list = xsltCompilerUtilsCreateMergedList(def, cctxt->tmpList);
+ if (list == NULL)
+ goto exit;
+ /*
+ * Store the list in the stylesheet/compiler context.
+ */
+ if (xsltPointerListAddSize(
+ cctxt->psData->exclResultNamespaces, list, 5) == -1)
+ {
+ xsltPointerListFree(list);
+ list = NULL;
+ goto exit;
+ }
+ /*
+ * Notify of change in status wrt namespaces.
+ */
+ if (cctxt->inode != NULL)
+ cctxt->inode->nsChanged = 1;
+
+exit:
+ if (list != NULL)
+ return(list);
+ else
+ return(def);
+}
+
+/*
+* xsltParseExtElemPrefixes:
+*
+* Create and store the list of in-scope namespaces for the given
+* node in the stylesheet. If there are no changes in the in-scope
+* namespaces then the last ns-info of the ancestor axis will be returned.
+* Compilation-time only.
+*
+* Returns the ns-info or NULL if there are no namespaces in scope.
+*/
+static xsltPointerListPtr
+xsltParseExtElemPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,
+ xsltPointerListPtr def,
+ int instrCategory)
+{
+ xsltPointerListPtr list = NULL;
+ xmlAttrPtr attr;
+ xmlChar *value;
+ int i;
+
+ if ((cctxt == NULL) || (node == NULL))
+ return(NULL);
+
+ if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT)
+ attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes", NULL);
+ else
+ attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes",
+ XSLT_NAMESPACE);
+ if (attr == NULL)
+ return(def);
+
+ if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) {
+ /*
+ * Mark the XSLT attr.
+ */
+ attr->psvi = (void *) xsltXSLTAttrMarker;
+ }
+
+ if ((attr->children != NULL) &&
+ (attr->children->content != NULL))
+ value = attr->children->content;
+ else {
+ xsltTransformError(NULL, cctxt->style, node,
+ "Attribute 'extension-element-prefixes': Invalid value.\n");
+ cctxt->style->errors++;
+ return(def);
+ }
+
+
+ if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node,
+ BAD_CAST value) != 0)
+ goto exit;
+
+ if (cctxt->tmpList->number == 0)
+ goto exit;
+ /*
+ * REVISIT: Register the extension namespaces.
+ */
+ for (i = 0; i < cctxt->tmpList->number; i++)
+ xsltRegisterExtPrefix(cctxt->style, NULL,
+ BAD_CAST cctxt->tmpList->items[i]);
+ /*
+ * Merge the list with the inherited list.
+ */
+ list = xsltCompilerUtilsCreateMergedList(def, cctxt->tmpList);
+ if (list == NULL)
+ goto exit;
+ /*
+ * Store the list in the stylesheet.
+ */
+ if (xsltPointerListAddSize(
+ cctxt->psData->extElemNamespaces, list, 5) == -1)
+ {
+ xsltPointerListFree(list);
+ list = NULL;
+ goto exit;
+ }
+ /*
+ * Notify of change in status wrt namespaces.
+ */
+ if (cctxt->inode != NULL)
+ cctxt->inode->nsChanged = 1;
+
+exit:
+ if (list != NULL)
+ return(list);
+ else
+ return(def);
+}
+
+/*
+* xsltParseAttrXSLTVersion:
+*
+* @cctxt: the compilation context
+* @node: the element-node
+* @isXsltElem: whether this is an XSLT element
+*
+* Parses the attribute xsl:version.
+*
+* Returns 1 if there was such an attribute, 0 if not and
+* -1 if an internal or API error occured.
+*/
+static int
+xsltParseAttrXSLTVersion(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,
+ int instrCategory)
+{
+ xmlChar *value;
+ xmlAttrPtr attr;
+
+ if ((cctxt == NULL) || (node == NULL))
+ return(-1);
+
+ if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT)
+ attr = xmlHasNsProp(node, BAD_CAST "version", NULL);
+ else
+ attr = xmlHasNsProp(node, BAD_CAST "version", XSLT_NAMESPACE);
+
+ if (attr == NULL)
+ return(0);
+
+ attr->psvi = (void *) xsltXSLTAttrMarker;
+
+ if ((attr->children != NULL) &&
+ (attr->children->content != NULL))
+ value = attr->children->content;
+ else {
+ xsltTransformError(NULL, cctxt->style, node,
+ "Attribute 'version': Invalid value.\n");
+ cctxt->style->errors++;
+ return(1);
+ }
+
+ if (! xmlStrEqual(value, (const xmlChar *)"1.0")) {
+ cctxt->inode->forwardsCompat = 1;
+ /*
+ * TODO: To what extent do we support the
+ * forwards-compatible mode?
+ */
+ /*
+ * Report this only once per compilation episode.
+ */
+ if (! cctxt->hasForwardsCompat) {
+ cctxt->hasForwardsCompat = 1;
+ cctxt->errSeverity = XSLT_ERROR_SEVERITY_WARNING;
+ xsltTransformError(NULL, cctxt->style, node,
+ "Warning: the attribute xsl:version specifies a value "
+ "different from '1.0'. Switching to forwards-compatible "
+ "mode. Only features of XSLT 1.0 are supported by this "
+ "processor.\n");
+ cctxt->style->warnings++;
+ cctxt->errSeverity = XSLT_ERROR_SEVERITY_ERROR;
+ }
+ } else {
+ cctxt->inode->forwardsCompat = 0;
+ }
+
+ if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) {
+ /*
+ * Set a marker on XSLT attributes.
+ */
+ attr->psvi = (void *) xsltXSLTAttrMarker;
+ }
+ return(1);
+}
+
+static int
+xsltParsePreprocessStylesheetTree(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
+{
+ xmlNodePtr deleteNode, cur, txt, textNode = NULL;
+ xmlDocPtr doc;
+ xsltStylesheetPtr style;
+ int internalize = 0, findSpaceAttr;
+ int xsltStylesheetElemDepth;
+ xmlAttrPtr attr;
+ xmlChar *value;
+ const xmlChar *name, *nsNameXSLT = NULL;
+ int strictWhitespace, inXSLText = 0;
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+ xsltNsMapPtr nsMapItem;
+#endif
+
+ if ((cctxt == NULL) || (cctxt->style == NULL) ||
+ (node == NULL) || (node->type != XML_ELEMENT_NODE))
+ return(-1);
+
+ doc = node->doc;
+ if (doc == NULL)
+ goto internal_err;
+
+ style = cctxt->style;
+ if ((style->dict != NULL) && (doc->dict == style->dict))
+ internalize = 1;
+ else
+ style->internalized = 0;
+
+ /*
+ * Init value of xml:space. Since this might be an embedded
+ * stylesheet, this is needed to be performed on the element
+ * where the stylesheet is rooted at, taking xml:space of
+ * ancestors into account.
+ */
+ if (! cctxt->simplified)
+ xsltStylesheetElemDepth = cctxt->depth +1;
+ else
+ xsltStylesheetElemDepth = 0;
+
+ if (xmlNodeGetSpacePreserve(node) != 1)
+ cctxt->inode->preserveWhitespace = 0;
+ else
+ cctxt->inode->preserveWhitespace = 1;
+
+ /*
+ * Eval if we should keep the old incorrect behaviour.
+ */
+ strictWhitespace = (cctxt->strict != 0) ? 1 : 0;
+
+ nsNameXSLT = xsltConstNamespaceNameXSLT;
+
+ deleteNode = NULL;
+ cur = node;
+ while (cur != NULL) {
+ if (deleteNode != NULL) {
+
+#ifdef WITH_XSLT_DEBUG_BLANKS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParsePreprocessStylesheetTree: removing node\n");
+#endif
+ xmlUnlinkNode(deleteNode);
+ xmlFreeNode(deleteNode);
+ deleteNode = NULL;
+ }
+ if (cur->type == XML_ELEMENT_NODE) {
+
+ /*
+ * Clear the PSVI field.
+ */
+ cur->psvi = NULL;
+
+ xsltCompilerNodePush(cctxt, cur);
+
+ inXSLText = 0;
+ textNode = NULL;
+ findSpaceAttr = 1;
+ cctxt->inode->stripWhitespace = 0;
+ /*
+ * TODO: I'd love to use a string pointer comparison here :-/
+ */
+ if (IS_XSLT_ELEM(cur)) {
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+ if (cur->ns->href != nsNameXSLT) {
+ nsMapItem = xsltNewNamespaceMapItem(cctxt,
+ doc, cur->ns, cur);
+ if (nsMapItem == NULL)
+ goto internal_err;
+ cur->ns->href = nsNameXSLT;
+ }
+#endif
+
+ if (cur->name == NULL)
+ goto process_attributes;
+ /*
+ * Mark the XSLT element for later recognition.
+ * TODO: Using the marker is still too dangerous, since if
+ * the parsing mechanism leaves out an XSLT element, then
+ * this might hit the transformation-mechanism, which
+ * will break if it doesn't expect such a marker.
+ */
+ /* cur->psvi = (void *) xsltXSLTElemMarker; */
+
+ /*
+ * XSLT 2.0: "Any whitespace text node whose parent is
+ * one of the following elements is removed from the "
+ * tree, regardless of any xml:space attributes:..."
+ * xsl:apply-imports,
+ * xsl:apply-templates,
+ * xsl:attribute-set,
+ * xsl:call-template,
+ * xsl:choose,
+ * xsl:stylesheet, xsl:transform.
+ * XSLT 2.0: xsl:analyze-string,
+ * xsl:character-map,
+ * xsl:next-match
+ *
+ * TODO: I'd love to use a string pointer comparison here :-/
+ */
+ name = cur->name;
+ switch (*name) {
+ case 't':
+ if ((name[0] == 't') && (name[1] == 'e') &&
+ (name[2] == 'x') && (name[3] == 't') &&
+ (name[4] == 0))
+ {
+ /*
+ * Process the xsl:text element.
+ * ----------------------------
+ * Mark it for later recognition.
+ */
+ cur->psvi = (void *) xsltXSLTTextMarker;
+ /*
+ * For stylesheets, the set of
+ * whitespace-preserving element names
+ * consists of just xsl:text.
+ */
+ findSpaceAttr = 0;
+ cctxt->inode->preserveWhitespace = 1;
+ inXSLText = 1;
+ }
+ break;
+ case 'c':
+ if (xmlStrEqual(name, BAD_CAST "choose") ||
+ xmlStrEqual(name, BAD_CAST "call-template"))
+ cctxt->inode->stripWhitespace = 1;
+ break;
+ case 'a':
+ if (xmlStrEqual(name, BAD_CAST "apply-templates") ||
+ xmlStrEqual(name, BAD_CAST "apply-imports") ||
+ xmlStrEqual(name, BAD_CAST "attribute-set"))
+
+ cctxt->inode->stripWhitespace = 1;
+ break;
+ default:
+ if (xsltStylesheetElemDepth == cctxt->depth) {
+ /*
+ * This is a xsl:stylesheet/xsl:transform.
+ */
+ cctxt->inode->stripWhitespace = 1;
+ break;
+ }
+
+ if ((cur->prev != NULL) &&
+ (cur->prev->type == XML_TEXT_NODE))
+ {
+ /*
+ * XSLT 2.0 : "Any whitespace text node whose
+ * following-sibling node is an xsl:param or
+ * xsl:sort element is removed from the tree,
+ * regardless of any xml:space attributes."
+ */
+ if (((*name == 'p') || (*name == 's')) &&
+ (xmlStrEqual(name, BAD_CAST "param") ||
+ xmlStrEqual(name, BAD_CAST "sort")))
+ {
+ do {
+ if (IS_BLANK_NODE(cur->prev)) {
+ txt = cur->prev;
+ xmlUnlinkNode(txt);
+ xmlFreeNode(txt);
+ } else {
+ /*
+ * This will result in a content
+ * error, when hitting the parsing
+ * functions.
+ */
+ break;
+ }
+ } while (cur->prev);
+ }
+ }
+ break;
+ }
+ }
+
+process_attributes:
+ /*
+ * Process attributes.
+ * ------------------
+ */
+ if (cur->properties != NULL) {
+ if (cur->children == NULL)
+ findSpaceAttr = 0;
+ attr = cur->properties;
+ do {
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+ if ((attr->ns) && (attr->ns->href != nsNameXSLT) &&
+ xmlStrEqual(attr->ns->href, nsNameXSLT))
+ {
+ nsMapItem = xsltNewNamespaceMapItem(cctxt,
+ doc, attr->ns, cur);
+ if (nsMapItem == NULL)
+ goto internal_err;
+ attr->ns->href = nsNameXSLT;
+ }
+#endif
+ if (internalize) {
+ /*
+ * Internalize the attribute's value; the goal is to
+ * speed up operations and minimize used space by
+ * compiled stylesheets.
+ */
+ txt = attr->children;
+ /*
+ * NOTE that this assumes only one
+ * text-node in the attribute's content.
+ */
+ if ((txt != NULL) && (txt->content != NULL) &&
+ (!xmlDictOwns(style->dict, txt->content)))
+ {
+ value = (xmlChar *) xmlDictLookup(style->dict,
+ txt->content, -1);
+ xmlNodeSetContent(txt, NULL);
+ txt->content = value;
+ }
+ }
+ /*
+ * Process xml:space attributes.
+ * ----------------------------
+ */
+ if ((findSpaceAttr != 0) &&
+ (attr->ns != NULL) &&
+ (attr->name != NULL) &&
+ (attr->name[0] == 's') &&
+ (attr->ns->prefix != NULL) &&
+ (attr->ns->prefix[0] == 'x') &&
+ (attr->ns->prefix[1] == 'm') &&
+ (attr->ns->prefix[2] == 'l') &&
+ (attr->ns->prefix[3] == 0))
+ {
+ value = xmlGetNsProp(cur, BAD_CAST "space",
+ XML_XML_NAMESPACE);
+ if (value != NULL) {
+ if (xmlStrEqual(value, BAD_CAST "preserve")) {
+ cctxt->inode->preserveWhitespace = 1;
+ } else if (xmlStrEqual(value, BAD_CAST "default")) {
+ cctxt->inode->preserveWhitespace = 0;
+ } else {
+ /* Invalid value for xml:space. */
+ xsltTransformError(NULL, style, cur,
+ "Attribute xml:space: Invalid value.\n");
+ cctxt->style->warnings++;
+ }
+ findSpaceAttr = 0;
+ xmlFree(value);
+ }
+
+ }
+ attr = attr->next;
+ } while (attr != NULL);
+ }
+ /*
+ * We'll descend into the children of element nodes only.
+ */
+ if (cur->children != NULL) {
+ cur = cur->children;
+ continue;
+ }
+ } else if ((cur->type == XML_TEXT_NODE) ||
+ (cur->type == XML_CDATA_SECTION_NODE))
+ {
+ /*
+ * Merge adjacent text/CDATA-section-nodes
+ * ---------------------------------------
+ * In order to avoid breaking of existing stylesheets,
+ * if the old behaviour is wanted (strictWhitespace == 0),
+ * then we *won't* merge adjacent text-nodes
+ * (except in xsl:text); this will ensure that whitespace-only
+ * text nodes are (incorrectly) not stripped in some cases.
+ *
+ * Example: : <foo> <!-- bar -->zoo</foo>
+ * Corrent (strict) result: <foo> zoo</foo>
+ * Incorrect (old) result : <foo>zoo</foo>
+ *
+ * NOTE that we *will* merge adjacent text-nodes if
+ * they are in xsl:text.
+ * Example, the following:
+ * <xsl:text> <!-- bar -->zoo<xsl:text>
+ * will result in both cases in:
+ * <xsl:text> zoo<xsl:text>
+ */
+ cur->type = XML_TEXT_NODE;
+ if ((strictWhitespace != 0) || (inXSLText != 0)) {
+ /*
+ * New behaviour; merge nodes.
+ */
+ if (textNode == NULL)
+ textNode = cur;
+ else {
+ if (cur->content != NULL)
+ xmlNodeAddContent(textNode, cur->content);
+ deleteNode = cur;
+ }
+ if ((cur->next == NULL) ||
+ (cur->next->type == XML_ELEMENT_NODE))
+ goto end_of_text;
+ else
+ goto next_sibling;
+ } else {
+ /*
+ * Old behaviour.
+ */
+ if (textNode == NULL)
+ textNode = cur;
+ goto end_of_text;
+ }
+ } else if ((cur->type == XML_COMMENT_NODE) ||
+ (cur->type == XML_PI_NODE))
+ {
+ /*
+ * Remove processing instructions and comments.
+ */
+ deleteNode = cur;
+ if ((cur->next == NULL) ||
+ (cur->next->type == XML_ELEMENT_NODE))
+ goto end_of_text;
+ else
+ goto next_sibling;
+ } else {
+ textNode = NULL;
+ /*
+ * Invalid node-type for this data-model.
+ */
+ xsltTransformError(NULL, style, cur,
+ "Invalid type of node for the XSLT data model.\n");
+ cctxt->style->errors++;
+ goto next_sibling;
+ }
+
+end_of_text:
+ if (textNode) {
+ value = textNode->content;
+ /*
+ * At this point all adjacent text/CDATA-section nodes
+ * have been merged.
+ *
+ * Strip whitespace-only text-nodes.
+ * (cctxt->inode->stripWhitespace)
+ */
+ if ((value == NULL) || (*value == 0) ||
+ (((cctxt->inode->stripWhitespace) ||
+ (! cctxt->inode->preserveWhitespace)) &&
+ IS_BLANK(*value) &&
+ xsltIsBlank(value)))
+ {
+ if (textNode != cur) {
+ xmlUnlinkNode(textNode);
+ xmlFreeNode(textNode);
+ } else
+ deleteNode = textNode;
+ textNode = NULL;
+ goto next_sibling;
+ }
+ /*
+ * Convert CDATA-section nodes to text-nodes.
+ * TODO: Can this produce problems?
+ */
+ if (textNode->type != XML_TEXT_NODE) {
+ textNode->type = XML_TEXT_NODE;
+ textNode->name = xmlStringText;
+ }
+ if (internalize &&
+ (textNode->content != NULL) &&
+ (!xmlDictOwns(style->dict, textNode->content)))
+ {
+ /*
+ * Internalize the string.
+ */
+ value = (xmlChar *) xmlDictLookup(style->dict,
+ textNode->content, -1);
+ xmlNodeSetContent(textNode, NULL);
+ textNode->content = value;
+ }
+ textNode = NULL;
+ /*
+ * Note that "disable-output-escaping" of the xsl:text
+ * element will be applied at a later level, when
+ * XSLT elements are processed.
+ */
+ }
+
+next_sibling:
+ if (cur->type == XML_ELEMENT_NODE) {
+ xsltCompilerNodePop(cctxt, cur);
+ }
+ if (cur == node)
+ break;
+ if (cur->next != NULL) {
+ cur = cur->next;
+ } else {
+ cur = cur->parent;
+ inXSLText = 0;
+ goto next_sibling;
+ };
+ }
+ if (deleteNode != NULL) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParsePreprocessStylesheetTree: removing node\n");
+#endif
+ xmlUnlinkNode(deleteNode);
+ xmlFreeNode(deleteNode);
+ }
+ return(0);
+
+internal_err:
+ return(-1);
+}
+
+#endif /* XSLT_REFACTORED */
+
+#ifdef XSLT_REFACTORED
+#else
+static void
+xsltPreprocessStylesheet(xsltStylesheetPtr style, xmlNodePtr cur)
+{
+ xmlNodePtr deleteNode, styleelem;
+ int internalize = 0;
+
+ if ((style == NULL) || (cur == NULL))
+ return;
+
+ if ((cur->doc != NULL) && (style->dict != NULL) &&
+ (cur->doc->dict == style->dict))
+ internalize = 1;
+ else
+ style->internalized = 0;
+
+ if ((cur != NULL) && (IS_XSLT_ELEM(cur)) &&
+ (IS_XSLT_NAME(cur, "stylesheet"))) {
+ styleelem = cur;
+ } else {
+ styleelem = NULL;
+ }
+
+ /*
+ * This content comes from the stylesheet
+ * For stylesheets, the set of whitespace-preserving
+ * element names consists of just xsl:text.
+ */
+ deleteNode = NULL;
+ while (cur != NULL) {
+ if (deleteNode != NULL) {
+#ifdef WITH_XSLT_DEBUG_BLANKS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltPreprocessStylesheet: removing ignorable blank node\n");
+#endif
+ xmlUnlinkNode(deleteNode);
+ xmlFreeNode(deleteNode);
+ deleteNode = NULL;
+ }
+ if (cur->type == XML_ELEMENT_NODE) {
+ int exclPrefixes;
+ /*
+ * Internalize attributes values.
+ */
+ if ((internalize) && (cur->properties != NULL)) {
+ xmlAttrPtr attr = cur->properties;
+ xmlNodePtr txt;
+
+ while (attr != NULL) {
+ txt = attr->children;
+ if ((txt != NULL) && (txt->type == XML_TEXT_NODE) &&
+ (txt->content != NULL) &&
+ (!xmlDictOwns(style->dict, txt->content)))
+ {
+ xmlChar *tmp;
+
+ /*
+ * internalize the text string, goal is to speed
+ * up operations and minimize used space by compiled
+ * stylesheets.
+ */
+ tmp = (xmlChar *) xmlDictLookup(style->dict,
+ txt->content, -1);
+ if (tmp != txt->content) {
+ xmlNodeSetContent(txt, NULL);
+ txt->content = tmp;
+ }
+ }
+ attr = attr->next;
+ }
+ }
+ if (IS_XSLT_ELEM(cur)) {
+ exclPrefixes = 0;
+ if (IS_XSLT_NAME(cur, "text")) {
+ for (;exclPrefixes > 0;exclPrefixes--)
+ exclPrefixPop(style);
+ goto skip_children;
+ }
+ } else {
+ exclPrefixes = xsltParseStylesheetExcludePrefix(style, cur, 0);
+ }
+
+ if ((cur->nsDef != NULL) && (style->exclPrefixNr > 0)) {
+ xmlNsPtr ns = cur->nsDef, prev = NULL, next;
+ xmlNodePtr root = NULL;
+ int i, moved;
+
+ root = xmlDocGetRootElement(cur->doc);
+ if ((root != NULL) && (root != cur)) {
+ while (ns != NULL) {
+ moved = 0;
+ next = ns->next;
+ for (i = 0;i < style->exclPrefixNr;i++) {
+ if ((ns->prefix != NULL) &&
+ (xmlStrEqual(ns->href,
+ style->exclPrefixTab[i]))) {
+ /*
+ * Move the namespace definition on the root
+ * element to avoid duplicating it without
+ * loosing it.
+ */
+ if (prev == NULL) {
+ cur->nsDef = ns->next;
+ } else {
+ prev->next = ns->next;
+ }
+ ns->next = root->nsDef;
+ root->nsDef = ns;
+ moved = 1;
+ break;
+ }
+ }
+ if (moved == 0)
+ prev = ns;
+ ns = next;
+ }
+ }
+ }
+ /*
+ * If we have prefixes locally, recurse and pop them up when
+ * going back
+ */
+ if (exclPrefixes > 0) {
+ xsltPreprocessStylesheet(style, cur->children);
+ for (;exclPrefixes > 0;exclPrefixes--)
+ exclPrefixPop(style);
+ goto skip_children;
+ }
+ } else if (cur->type == XML_TEXT_NODE) {
+ if (IS_BLANK_NODE(cur)) {
+ if (xmlNodeGetSpacePreserve(cur->parent) != 1) {
+ deleteNode = cur;
+ }
+ } else if ((cur->content != NULL) && (internalize) &&
+ (!xmlDictOwns(style->dict, cur->content))) {
+ xmlChar *tmp;
+
+ /*
+ * internalize the text string, goal is to speed
+ * up operations and minimize used space by compiled
+ * stylesheets.
+ */
+ tmp = (xmlChar *) xmlDictLookup(style->dict, cur->content, -1);
+ xmlNodeSetContent(cur, NULL);
+ cur->content = tmp;
+ }
+ } else if ((cur->type != XML_ELEMENT_NODE) &&
+ (cur->type != XML_CDATA_SECTION_NODE)) {
+ deleteNode = cur;
+ goto skip_children;
+ }
+
+ /*
+ * Skip to next node. In case of a namespaced element children of
+ * the stylesheet and not in the XSLT namespace and not an extension
+ * element, ignore its content.
+ */
+ if ((cur->type == XML_ELEMENT_NODE) && (cur->ns != NULL) &&
+ (styleelem != NULL) && (cur->parent == styleelem) &&
+ (!xmlStrEqual(cur->ns->href, XSLT_NAMESPACE)) &&
+ (!xsltCheckExtURI(style, cur->ns->href))) {
+ goto skip_children;
+ } else if (cur->children != NULL) {
+ if ((cur->children->type != XML_ENTITY_DECL) &&
+ (cur->children->type != XML_ENTITY_REF_NODE) &&
+ (cur->children->type != XML_ENTITY_NODE)) {
+ cur = cur->children;
+ continue;
+ }
+ }
+
+skip_children:
+ if (cur->next != NULL) {
+ cur = cur->next;
+ continue;
+ }
+ do {
+
+ cur = cur->parent;
+ if (cur == NULL)
+ break;
+ if (cur == (xmlNodePtr) style->doc) {
+ cur = NULL;
+ break;
+ }
+ if (cur->next != NULL) {
+ cur = cur->next;
+ break;
+ }
+ } while (cur != NULL);
+ }
+ if (deleteNode != NULL) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltPreprocessStylesheet: removing ignorable blank node\n");
+#endif
+ xmlUnlinkNode(deleteNode);
+ xmlFreeNode(deleteNode);
+ }
+}
+#endif /* end of else XSLT_REFACTORED */
+
+/**
+ * xsltGatherNamespaces:
+ * @style: the XSLT stylesheet
+ *
+ * Browse the stylesheet and build the namspace hash table which
+ * will be used for XPath interpretation. If needed do a bit of normalization
+ */
+
+static void
+xsltGatherNamespaces(xsltStylesheetPtr style) {
+ xmlNodePtr cur;
+ const xmlChar *URI;
+
+ if (style == NULL)
+ return;
+ /*
+ * TODO: basically if the stylesheet uses the same prefix for different
+ * patterns, well they may be in problem, hopefully they will get
+ * a warning first.
+ */
+ /*
+ * TODO: Eliminate the use of the hash for XPath expressions.
+ * An expression should be evaluated in the context of the in-scope
+ * namespaces; eliminate the restriction of an XML document to contain
+ * no duplicate prefixes for different namespace names.
+ *
+ */
+ cur = xmlDocGetRootElement(style->doc);
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE) {
+ xmlNsPtr ns = cur->nsDef;
+ while (ns != NULL) {
+ if (ns->prefix != NULL) {
+ if (style->nsHash == NULL) {
+ style->nsHash = xmlHashCreate(10);
+ if (style->nsHash == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "xsltGatherNamespaces: failed to create hash table\n");
+ style->errors++;
+ return;
+ }
+ }
+ URI = xmlHashLookup(style->nsHash, ns->prefix);
+ if ((URI != NULL) && (!xmlStrEqual(URI, ns->href))) {
+ xsltTransformError(NULL, style, cur,
+ "Namespaces prefix %s used for multiple namespaces\n",ns->prefix);
+ style->warnings++;
+ } else if (URI == NULL) {
+ xmlHashUpdateEntry(style->nsHash, ns->prefix,
+ (void *) ns->href, (xmlHashDeallocator)xmlFree);
+
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Added namespace: %s mapped to %s\n", ns->prefix, ns->href);
+#endif
+ }
+ }
+ ns = ns->next;
+ }
+ }
+
+ /*
+ * Skip to next node
+ */
+ if (cur->children != NULL) {
+ if (cur->children->type != XML_ENTITY_DECL) {
+ cur = cur->children;
+ continue;
+ }
+ }
+ if (cur->next != NULL) {
+ cur = cur->next;
+ continue;
+ }
+
+ do {
+ cur = cur->parent;
+ if (cur == NULL)
+ break;
+ if (cur == (xmlNodePtr) style->doc) {
+ cur = NULL;
+ break;
+ }
+ if (cur->next != NULL) {
+ cur = cur->next;
+ break;
+ }
+ } while (cur != NULL);
+ }
+}
+
+#ifdef XSLT_REFACTORED
+
+static xsltStyleType
+xsltGetXSLTElementTypeByNode(xsltCompilerCtxtPtr cctxt,
+ xmlNodePtr node)
+{
+ if ((node == NULL) || (node->type != XML_ELEMENT_NODE) ||
+ (node->name == NULL))
+ return(0);
+
+ if (node->name[0] == 'a') {
+ if (IS_XSLT_NAME(node, "apply-templates"))
+ return(XSLT_FUNC_APPLYTEMPLATES);
+ else if (IS_XSLT_NAME(node, "attribute"))
+ return(XSLT_FUNC_ATTRIBUTE);
+ else if (IS_XSLT_NAME(node, "apply-imports"))
+ return(XSLT_FUNC_APPLYIMPORTS);
+ else if (IS_XSLT_NAME(node, "attribute-set"))
+ return(0);
+
+ } else if (node->name[0] == 'c') {
+ if (IS_XSLT_NAME(node, "choose"))
+ return(XSLT_FUNC_CHOOSE);
+ else if (IS_XSLT_NAME(node, "copy"))
+ return(XSLT_FUNC_COPY);
+ else if (IS_XSLT_NAME(node, "copy-of"))
+ return(XSLT_FUNC_COPYOF);
+ else if (IS_XSLT_NAME(node, "call-template"))
+ return(XSLT_FUNC_CALLTEMPLATE);
+ else if (IS_XSLT_NAME(node, "comment"))
+ return(XSLT_FUNC_COMMENT);
+
+ } else if (node->name[0] == 'd') {
+ if (IS_XSLT_NAME(node, "document"))
+ return(XSLT_FUNC_DOCUMENT);
+ else if (IS_XSLT_NAME(node, "decimal-format"))
+ return(0);
+
+ } else if (node->name[0] == 'e') {
+ if (IS_XSLT_NAME(node, "element"))
+ return(XSLT_FUNC_ELEMENT);
+
+ } else if (node->name[0] == 'f') {
+ if (IS_XSLT_NAME(node, "for-each"))
+ return(XSLT_FUNC_FOREACH);
+ else if (IS_XSLT_NAME(node, "fallback"))
+ return(XSLT_FUNC_FALLBACK);
+
+ } else if (*(node->name) == 'i') {
+ if (IS_XSLT_NAME(node, "if"))
+ return(XSLT_FUNC_IF);
+ else if (IS_XSLT_NAME(node, "include"))
+ return(0);
+ else if (IS_XSLT_NAME(node, "import"))
+ return(0);
+
+ } else if (*(node->name) == 'k') {
+ if (IS_XSLT_NAME(node, "key"))
+ return(0);
+
+ } else if (*(node->name) == 'm') {
+ if (IS_XSLT_NAME(node, "message"))
+ return(XSLT_FUNC_MESSAGE);
+
+ } else if (*(node->name) == 'n') {
+ if (IS_XSLT_NAME(node, "number"))
+ return(XSLT_FUNC_NUMBER);
+ else if (IS_XSLT_NAME(node, "namespace-alias"))
+ return(0);
+
+ } else if (*(node->name) == 'o') {
+ if (IS_XSLT_NAME(node, "otherwise"))
+ return(XSLT_FUNC_OTHERWISE);
+ else if (IS_XSLT_NAME(node, "output"))
+ return(0);
+
+ } else if (*(node->name) == 'p') {
+ if (IS_XSLT_NAME(node, "param"))
+ return(XSLT_FUNC_PARAM);
+ else if (IS_XSLT_NAME(node, "processing-instruction"))
+ return(XSLT_FUNC_PI);
+ else if (IS_XSLT_NAME(node, "preserve-space"))
+ return(0);
+
+ } else if (*(node->name) == 's') {
+ if (IS_XSLT_NAME(node, "sort"))
+ return(XSLT_FUNC_SORT);
+ else if (IS_XSLT_NAME(node, "strip-space"))
+ return(0);
+ else if (IS_XSLT_NAME(node, "stylesheet"))
+ return(0);
+
+ } else if (node->name[0] == 't') {
+ if (IS_XSLT_NAME(node, "text"))
+ return(XSLT_FUNC_TEXT);
+ else if (IS_XSLT_NAME(node, "template"))
+ return(0);
+ else if (IS_XSLT_NAME(node, "transform"))
+ return(0);
+
+ } else if (*(node->name) == 'v') {
+ if (IS_XSLT_NAME(node, "value-of"))
+ return(XSLT_FUNC_VALUEOF);
+ else if (IS_XSLT_NAME(node, "variable"))
+ return(XSLT_FUNC_VARIABLE);
+
+ } else if (*(node->name) == 'w') {
+ if (IS_XSLT_NAME(node, "when"))
+ return(XSLT_FUNC_WHEN);
+ if (IS_XSLT_NAME(node, "with-param"))
+ return(XSLT_FUNC_WITHPARAM);
+ }
+ return(0);
+}
+
+/**
+ * xsltParseAnyXSLTElem:
+ *
+ * @cctxt: the compilation context
+ * @elem: the element node of the XSLT instruction
+ *
+ * Parses, validates the content models and compiles XSLT instructions.
+ *
+ * Returns 0 if everything's fine;
+ * -1 on API or internal errors.
+ */
+int
+xsltParseAnyXSLTElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr elem)
+{
+ if ((cctxt == NULL) || (elem == NULL) ||
+ (elem->type != XML_ELEMENT_NODE))
+ return(-1);
+
+ elem->psvi = NULL;
+
+ if (! (IS_XSLT_ELEM_FAST(elem)))
+ return(-1);
+ /*
+ * Detection of handled content of extension instructions.
+ */
+ if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) {
+ cctxt->inode->extContentHandled = 1;
+ }
+
+ xsltCompilerNodePush(cctxt, elem);
+ /*
+ * URGENT TODO: Find a way to speed up this annoying redundant
+ * textual node-name and namespace comparison.
+ */
+ if (cctxt->inode->prev->curChildType != 0)
+ cctxt->inode->type = cctxt->inode->prev->curChildType;
+ else
+ cctxt->inode->type = xsltGetXSLTElementTypeByNode(cctxt, elem);
+ /*
+ * Update the in-scope namespaces if needed.
+ */
+ if (elem->nsDef != NULL)
+ cctxt->inode->inScopeNs =
+ xsltCompilerBuildInScopeNsList(cctxt, elem);
+ /*
+ * xsltStylePreCompute():
+ * This will compile the information found on the current
+ * element's attributes. NOTE that this won't process the
+ * children of the instruction.
+ */
+ xsltStylePreCompute(cctxt->style, elem);
+ /*
+ * TODO: How to react on errors in xsltStylePreCompute() ?
+ */
+
+ /*
+ * Validate the content model of the XSLT-element.
+ */
+ switch (cctxt->inode->type) {
+ case XSLT_FUNC_APPLYIMPORTS:
+ /* EMPTY */
+ goto empty_content;
+ case XSLT_FUNC_APPLYTEMPLATES:
+ /* <!-- Content: (xsl:sort | xsl:with-param)* --> */
+ goto apply_templates;
+ case XSLT_FUNC_ATTRIBUTE:
+ /* <!-- Content: template --> */
+ goto sequence_constructor;
+ case XSLT_FUNC_CALLTEMPLATE:
+ /* <!-- Content: xsl:with-param* --> */
+ goto call_template;
+ case XSLT_FUNC_CHOOSE:
+ /* <!-- Content: (xsl:when+, xsl:otherwise?) --> */
+ goto choose;
+ case XSLT_FUNC_COMMENT:
+ /* <!-- Content: template --> */
+ goto sequence_constructor;
+ case XSLT_FUNC_COPY:
+ /* <!-- Content: template --> */
+ goto sequence_constructor;
+ case XSLT_FUNC_COPYOF:
+ /* EMPTY */
+ goto empty_content;
+ case XSLT_FUNC_DOCUMENT: /* Extra one */
+ /* ?? template ?? */
+ goto sequence_constructor;
+ case XSLT_FUNC_ELEMENT:
+ /* <!-- Content: template --> */
+ goto sequence_constructor;
+ case XSLT_FUNC_FALLBACK:
+ /* <!-- Content: template --> */
+ goto sequence_constructor;
+ case XSLT_FUNC_FOREACH:
+ /* <!-- Content: (xsl:sort*, template) --> */
+ goto for_each;
+ case XSLT_FUNC_IF:
+ /* <!-- Content: template --> */
+ goto sequence_constructor;
+ case XSLT_FUNC_OTHERWISE:
+ /* <!-- Content: template --> */
+ goto sequence_constructor;
+ case XSLT_FUNC_MESSAGE:
+ /* <!-- Content: template --> */
+ goto sequence_constructor;
+ case XSLT_FUNC_NUMBER:
+ /* EMPTY */
+ goto empty_content;
+ case XSLT_FUNC_PARAM:
+ /*
+ * Check for redefinition.
+ */
+ if ((elem->psvi != NULL) && (cctxt->ivar != NULL)) {
+ xsltVarInfoPtr ivar = cctxt->ivar;
+
+ do {
+ if ((ivar->name ==
+ ((xsltStyleItemParamPtr) elem->psvi)->name) &&
+ (ivar->nsName ==
+ ((xsltStyleItemParamPtr) elem->psvi)->ns))
+ {
+ elem->psvi = NULL;
+ xsltTransformError(NULL, cctxt->style, elem,
+ "Redefinition of variable or parameter '%s'.\n",
+ ivar->name);
+ cctxt->style->errors++;
+ goto error;
+ }
+ ivar = ivar->prev;
+ } while (ivar != NULL);
+ }
+ /* <!-- Content: template --> */
+ goto sequence_constructor;
+ case XSLT_FUNC_PI:
+ /* <!-- Content: template --> */
+ goto sequence_constructor;
+ case XSLT_FUNC_SORT:
+ /* EMPTY */
+ goto empty_content;
+ case XSLT_FUNC_TEXT:
+ /* <!-- Content: #PCDATA --> */
+ goto text;
+ case XSLT_FUNC_VALUEOF:
+ /* EMPTY */
+ goto empty_content;
+ case XSLT_FUNC_VARIABLE:
+ /*
+ * Check for redefinition.
+ */
+ if ((elem->psvi != NULL) && (cctxt->ivar != NULL)) {
+ xsltVarInfoPtr ivar = cctxt->ivar;
+
+ do {
+ if ((ivar->name ==
+ ((xsltStyleItemVariablePtr) elem->psvi)->name) &&
+ (ivar->nsName ==
+ ((xsltStyleItemVariablePtr) elem->psvi)->ns))
+ {
+ elem->psvi = NULL;
+ xsltTransformError(NULL, cctxt->style, elem,
+ "Redefinition of variable or parameter '%s'.\n",
+ ivar->name);
+ cctxt->style->errors++;
+ goto error;
+ }
+ ivar = ivar->prev;
+ } while (ivar != NULL);
+ }
+ /* <!-- Content: template --> */
+ goto sequence_constructor;
+ case XSLT_FUNC_WHEN:
+ /* <!-- Content: template --> */
+ goto sequence_constructor;
+ case XSLT_FUNC_WITHPARAM:
+ /* <!-- Content: template --> */
+ goto sequence_constructor;
+ default:
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseXSLTNode: Unhandled XSLT element '%s'.\n",
+ elem->name);
+#endif
+ xsltTransformError(NULL, cctxt->style, elem,
+ "xsltParseXSLTNode: Internal error; "
+ "unhandled XSLT element '%s'.\n", elem->name);
+ cctxt->style->errors++;
+ goto internal_err;
+ }
+
+apply_templates:
+ /* <!-- Content: (xsl:sort | xsl:with-param)* --> */
+ if (elem->children != NULL) {
+ xmlNodePtr child = elem->children;
+ do {
+ if (child->type == XML_ELEMENT_NODE) {
+ if (IS_XSLT_ELEM_FAST(child)) {
+ if (xmlStrEqual(child->name, BAD_CAST "with-param")) {
+ cctxt->inode->curChildType = XSLT_FUNC_WITHPARAM;
+ xsltParseAnyXSLTElem(cctxt, child);
+ } else if (xmlStrEqual(child->name, BAD_CAST "sort")) {
+ cctxt->inode->curChildType = XSLT_FUNC_SORT;
+ xsltParseAnyXSLTElem(cctxt, child);
+ } else
+ xsltParseContentError(cctxt->style, child);
+ } else
+ xsltParseContentError(cctxt->style, child);
+ }
+ child = child->next;
+ } while (child != NULL);
+ }
+ goto exit;
+
+call_template:
+ /* <!-- Content: xsl:with-param* --> */
+ if (elem->children != NULL) {
+ xmlNodePtr child = elem->children;
+ do {
+ if (child->type == XML_ELEMENT_NODE) {
+ if (IS_XSLT_ELEM_FAST(child)) {
+ xsltStyleType type;
+
+ type = xsltGetXSLTElementTypeByNode(cctxt, child);
+ if (type == XSLT_FUNC_WITHPARAM) {
+ cctxt->inode->curChildType = XSLT_FUNC_WITHPARAM;
+ xsltParseAnyXSLTElem(cctxt, child);
+ } else {
+ xsltParseContentError(cctxt->style, child);
+ }
+ } else
+ xsltParseContentError(cctxt->style, child);
+ }
+ child = child->next;
+ } while (child != NULL);
+ }
+ goto exit;
+
+text:
+ if (elem->children != NULL) {
+ xmlNodePtr child = elem->children;
+ do {
+ if ((child->type != XML_TEXT_NODE) &&
+ (child->type != XML_CDATA_SECTION_NODE))
+ {
+ xsltTransformError(NULL, cctxt->style, elem,
+ "The XSLT 'text' element must have only character "
+ "data as content.\n");
+ }
+ child = child->next;
+ } while (child != NULL);
+ }
+ goto exit;
+
+empty_content:
+ if (elem->children != NULL) {
+ xmlNodePtr child = elem->children;
+ /*
+ * Relaxed behaviour: we will allow whitespace-only text-nodes.
+ */
+ do {
+ if (((child->type != XML_TEXT_NODE) &&
+ (child->type != XML_CDATA_SECTION_NODE)) ||
+ (! IS_BLANK_NODE(child)))
+ {
+ xsltTransformError(NULL, cctxt->style, elem,
+ "This XSLT element must have no content.\n");
+ cctxt->style->errors++;
+ break;
+ }
+ child = child->next;
+ } while (child != NULL);
+ }
+ goto exit;
+
+choose:
+ /* <!-- Content: (xsl:when+, xsl:otherwise?) --> */
+ /*
+ * TODO: text-nodes in between are *not* allowed in XSLT 1.0.
+ * The old behaviour did not check this.
+ * NOTE: In XSLT 2.0 they are stripped beforehand
+ * if whitespace-only (regardless of xml:space).
+ */
+ if (elem->children != NULL) {
+ xmlNodePtr child = elem->children;
+ int nbWhen = 0, nbOtherwise = 0, err = 0;
+ do {
+ if (child->type == XML_ELEMENT_NODE) {
+ if (IS_XSLT_ELEM_FAST(child)) {
+ xsltStyleType type;
+
+ type = xsltGetXSLTElementTypeByNode(cctxt, child);
+ if (type == XSLT_FUNC_WHEN) {
+ nbWhen++;
+ if (nbOtherwise) {
+ xsltParseContentError(cctxt->style, child);
+ err = 1;
+ break;
+ }
+ cctxt->inode->curChildType = XSLT_FUNC_WHEN;
+ xsltParseAnyXSLTElem(cctxt, child);
+ } else if (type == XSLT_FUNC_OTHERWISE) {
+ if (! nbWhen) {
+ xsltParseContentError(cctxt->style, child);
+ err = 1;
+ break;
+ }
+ if (nbOtherwise) {
+ xsltTransformError(NULL, cctxt->style, elem,
+ "The XSLT 'choose' element must not contain "
+ "more than one XSLT 'otherwise' element.\n");
+ cctxt->style->errors++;
+ err = 1;
+ break;
+ }
+ nbOtherwise++;
+ cctxt->inode->curChildType = XSLT_FUNC_OTHERWISE;
+ xsltParseAnyXSLTElem(cctxt, child);
+ } else
+ xsltParseContentError(cctxt->style, child);
+ } else
+ xsltParseContentError(cctxt->style, child);
+ }
+ /*
+ else
+ xsltParseContentError(cctxt, child);
+ */
+ child = child->next;
+ } while (child != NULL);
+ if ((! err) && (! nbWhen)) {
+ xsltTransformError(NULL, cctxt->style, elem,
+ "The XSLT element 'choose' must contain at least one "
+ "XSLT element 'when'.\n");
+ cctxt->style->errors++;
+ }
+ }
+ goto exit;
+
+for_each:
+ /* <!-- Content: (xsl:sort*, template) --> */
+ /*
+ * NOTE: Text-nodes before xsl:sort are *not* allowed in XSLT 1.0.
+ * The old behaviour did not allow this, but it catched this
+ * only at transformation-time.
+ * In XSLT 2.0 they are stripped beforehand if whitespace-only
+ * (regardless of xml:space).
+ */
+ if (elem->children != NULL) {
+ xmlNodePtr child = elem->children;
+ /*
+ * Parse xsl:sort first.
+ */
+ do {
+ if ((child->type == XML_ELEMENT_NODE) &&
+ IS_XSLT_ELEM_FAST(child))
+ {
+ if (xsltGetXSLTElementTypeByNode(cctxt, child) ==
+ XSLT_FUNC_SORT)
+ {
+ cctxt->inode->curChildType = XSLT_FUNC_SORT;
+ xsltParseAnyXSLTElem(cctxt, child);
+ } else
+ break;
+ } else
+ break;
+ child = child->next;
+ } while (child != NULL);
+ /*
+ * Parse the sequece constructor.
+ */
+ if (child != NULL)
+ xsltParseSequenceConstructor(cctxt, child);
+ }
+ goto exit;
+
+sequence_constructor:
+ /*
+ * Parse the sequence constructor.
+ */
+ if (elem->children != NULL)
+ xsltParseSequenceConstructor(cctxt, elem->children);
+
+ /*
+ * Register information for vars/params. Only needed if there
+ * are any following siblings.
+ */
+ if ((elem->next != NULL) &&
+ ((cctxt->inode->type == XSLT_FUNC_VARIABLE) ||
+ (cctxt->inode->type == XSLT_FUNC_PARAM)))
+ {
+ if ((elem->psvi != NULL) &&
+ (((xsltStyleBasicItemVariablePtr) elem->psvi)->name))
+ {
+ xsltCompilerVarInfoPush(cctxt, elem,
+ ((xsltStyleBasicItemVariablePtr) elem->psvi)->name,
+ ((xsltStyleBasicItemVariablePtr) elem->psvi)->ns);
+ }
+ }
+
+error:
+exit:
+ xsltCompilerNodePop(cctxt, elem);
+ return(0);
+
+internal_err:
+ xsltCompilerNodePop(cctxt, elem);
+ return(-1);
+}
+
+/**
+ * xsltForwardsCompatUnkownItemCreate:
+ *
+ * @cctxt: the compilation context
+ *
+ * Creates a compiled representation of the unknown
+ * XSLT instruction.
+ *
+ * Returns the compiled representation.
+ */
+static xsltStyleItemUknownPtr
+xsltForwardsCompatUnkownItemCreate(xsltCompilerCtxtPtr cctxt)
+{
+ xsltStyleItemUknownPtr item;
+
+ item = (xsltStyleItemUknownPtr) xmlMalloc(sizeof(xsltStyleItemUknown));
+ if (item == NULL) {
+ xsltTransformError(NULL, cctxt->style, NULL,
+ "Internal error in xsltForwardsCompatUnkownItemCreate(): "
+ "Failed to allocate memory.\n");
+ cctxt->style->errors++;
+ return(NULL);
+ }
+ memset(item, 0, sizeof(xsltStyleItemUknown));
+ item->type = XSLT_FUNC_UNKOWN_FORWARDS_COMPAT;
+ /*
+ * Store it in the stylesheet.
+ */
+ item->next = cctxt->style->preComps;
+ cctxt->style->preComps = (xsltElemPreCompPtr) item;
+ return(item);
+}
+
+/**
+ * xsltParseUnknownXSLTElem:
+ *
+ * @cctxt: the compilation context
+ * @node: the element of the unknown XSLT instruction
+ *
+ * Parses an unknown XSLT element.
+ * If forwards compatible mode is enabled this will allow
+ * such an unknown XSLT and; otherwise it is rejected.
+ *
+ * Returns 1 in the unknown XSLT instruction is rejected,
+ * 0 if everything's fine and
+ * -1 on API or internal errors.
+ */
+static int
+xsltParseUnknownXSLTElem(xsltCompilerCtxtPtr cctxt,
+ xmlNodePtr node)
+{
+ if ((cctxt == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE))
+ return(-1);
+
+ /*
+ * Detection of handled content of extension instructions.
+ */
+ if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) {
+ cctxt->inode->extContentHandled = 1;
+ }
+ if (cctxt->inode->forwardsCompat == 0) {
+ /*
+ * We are not in forwards-compatible mode, so raise an error.
+ */
+ xsltTransformError(NULL, cctxt->style, node,
+ "Unknown XSLT element '%s'.\n", node->name);
+ cctxt->style->errors++;
+ return(1);
+ }
+ /*
+ * Forwards-compatible mode.
+ * ------------------------
+ *
+ * Parse/compile xsl:fallback elements.
+ *
+ * QUESTION: Do we have to raise an error if there's no xsl:fallback?
+ * ANSWER: No, since in the stylesheet the fallback behaviour might
+ * also be provided by using the XSLT function "element-available".
+ */
+ if (cctxt->unknownItem == NULL) {
+ /*
+ * Create a singleton for all unknown XSLT instructions.
+ */
+ cctxt->unknownItem = xsltForwardsCompatUnkownItemCreate(cctxt);
+ if (cctxt->unknownItem == NULL) {
+ node->psvi = NULL;
+ return(-1);
+ }
+ }
+ node->psvi = cctxt->unknownItem;
+ if (node->children == NULL)
+ return(0);
+ else {
+ xmlNodePtr child = node->children;
+
+ xsltCompilerNodePush(cctxt, node);
+ /*
+ * Update the in-scope namespaces if needed.
+ */
+ if (node->nsDef != NULL)
+ cctxt->inode->inScopeNs =
+ xsltCompilerBuildInScopeNsList(cctxt, node);
+ /*
+ * Parse all xsl:fallback children.
+ */
+ do {
+ if ((child->type == XML_ELEMENT_NODE) &&
+ IS_XSLT_ELEM_FAST(child) &&
+ IS_XSLT_NAME(child, "fallback"))
+ {
+ cctxt->inode->curChildType = XSLT_FUNC_FALLBACK;
+ xsltParseAnyXSLTElem(cctxt, child);
+ }
+ child = child->next;
+ } while (child != NULL);
+
+ xsltCompilerNodePop(cctxt, node);
+ }
+ return(0);
+}
+
+/**
+ * xsltParseSequenceConstructor:
+ *
+ * @cctxt: the compilation context
+ * @cur: the start-node of the content to be parsed
+ *
+ * Parses a "template" content (or "sequence constructor" in XSLT 2.0 terms).
+ * This will additionally remove xsl:text elements from the tree.
+ */
+void
+xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur)
+{
+ xsltStyleType type;
+ xmlNodePtr deleteNode = NULL;
+
+ if (cctxt == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xsltParseSequenceConstructor: Bad arguments\n");
+ cctxt->style->errors++;
+ return;
+ }
+ /*
+ * Detection of handled content of extension instructions.
+ */
+ if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) {
+ cctxt->inode->extContentHandled = 1;
+ }
+ if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
+ return;
+ /*
+ * This is the content reffered to as a "template".
+ * E.g. an xsl:element has such content model:
+ * <xsl:element
+ * name = { qname }
+ * namespace = { uri-reference }
+ * use-attribute-sets = qnames>
+ * <!-- Content: template -->
+ *
+ * NOTE that in XSLT-2 the term "template" was abandoned due to
+ * confusion with xsl:template and the term "sequence constructor"
+ * was introduced instead.
+ *
+ * The following XSLT-instructions are allowed to appear:
+ * xsl:apply-templates, xsl:call-template, xsl:apply-imports,
+ * xsl:for-each, xsl:value-of, xsl:copy-of, xsl:number,
+ * xsl:choose, xsl:if, xsl:text, xsl:copy, xsl:variable,
+ * xsl:message, xsl:fallback,
+ * xsl:processing-instruction, xsl:comment, xsl:element
+ * xsl:attribute.
+ * Additional allowed content:
+ * 1) extension instructions
+ * 2) literal result elements
+ * 3) PCDATA
+ *
+ * NOTE that this content model does *not* allow xsl:param.
+ */
+ while (cur != NULL) {
+ if (deleteNode != NULL) {
+#ifdef WITH_XSLT_DEBUG_BLANKS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseSequenceConstructor: removing xsl:text element\n");
+#endif
+ xmlUnlinkNode(deleteNode);
+ xmlFreeNode(deleteNode);
+ deleteNode = NULL;
+ }
+ if (cur->type == XML_ELEMENT_NODE) {
+
+ if (cur->psvi == xsltXSLTTextMarker) {
+ /*
+ * xsl:text elements
+ * --------------------------------------------------------
+ */
+ xmlNodePtr tmp;
+
+ cur->psvi = NULL;
+ /*
+ * Mark the xsl:text element for later deletion.
+ */
+ deleteNode = cur;
+ /*
+ * Validate content.
+ */
+ tmp = cur->children;
+ if (tmp) {
+ /*
+ * We don't expect more than one text-node in the
+ * content, since we already merged adjacent
+ * text/CDATA-nodes and eliminated PI/comment-nodes.
+ */
+ if ((tmp->type == XML_TEXT_NODE) ||
+ (tmp->next == NULL))
+ {
+ /*
+ * Leave the contained text-node in the tree.
+ */
+ xmlUnlinkNode(tmp);
+ xmlAddPrevSibling(cur, tmp);
+ } else {
+ tmp = NULL;
+ xsltTransformError(NULL, cctxt->style, cur,
+ "Element 'xsl:text': Invalid type "
+ "of node found in content.\n");
+ cctxt->style->errors++;
+ }
+ }
+ if (cur->properties) {
+ xmlAttrPtr attr;
+ /*
+ * TODO: We need to report errors for
+ * invalid attrs.
+ */
+ attr = cur->properties;
+ do {
+ if ((attr->ns == NULL) &&
+ (attr->name != NULL) &&
+ (attr->name[0] == 'd') &&
+ xmlStrEqual(attr->name,
+ BAD_CAST "disable-output-escaping"))
+ {
+ /*
+ * Attr "disable-output-escaping".
+ * XSLT-2: This attribute is deprecated.
+ */
+ if ((attr->children != NULL) &&
+ xmlStrEqual(attr->children->content,
+ BAD_CAST "yes"))
+ {
+ /*
+ * Disable output escaping for this
+ * text node.
+ */
+ if (tmp)
+ tmp->name = xmlStringTextNoenc;
+ } else if ((attr->children == NULL) ||
+ (attr->children->content == NULL) ||
+ (!xmlStrEqual(attr->children->content,
+ BAD_CAST "no")))
+ {
+ xsltTransformError(NULL, cctxt->style,
+ cur,
+ "Attribute 'disable-output-escaping': "
+ "Invalid value. Expected is "
+ "'yes' or 'no'.\n");
+ cctxt->style->errors++;
+ }
+ break;
+ }
+ attr = attr->next;
+ } while (attr != NULL);
+ }
+ } else if (IS_XSLT_ELEM_FAST(cur)) {
+ /*
+ * TODO: Using the XSLT-marker is still not stable yet.
+ */
+ /* if (cur->psvi == xsltXSLTElemMarker) { */
+ /*
+ * XSLT instructions
+ * --------------------------------------------------------
+ */
+ cur->psvi = NULL;
+ type = xsltGetXSLTElementTypeByNode(cctxt, cur);
+ switch (type) {
+ case XSLT_FUNC_APPLYIMPORTS:
+ case XSLT_FUNC_APPLYTEMPLATES:
+ case XSLT_FUNC_ATTRIBUTE:
+ case XSLT_FUNC_CALLTEMPLATE:
+ case XSLT_FUNC_CHOOSE:
+ case XSLT_FUNC_COMMENT:
+ case XSLT_FUNC_COPY:
+ case XSLT_FUNC_COPYOF:
+ case XSLT_FUNC_DOCUMENT: /* Extra one */
+ case XSLT_FUNC_ELEMENT:
+ case XSLT_FUNC_FALLBACK:
+ case XSLT_FUNC_FOREACH:
+ case XSLT_FUNC_IF:
+ case XSLT_FUNC_MESSAGE:
+ case XSLT_FUNC_NUMBER:
+ case XSLT_FUNC_PI:
+ case XSLT_FUNC_TEXT:
+ case XSLT_FUNC_VALUEOF:
+ case XSLT_FUNC_VARIABLE:
+ /*
+ * Parse the XSLT element.
+ */
+ cctxt->inode->curChildType = type;
+ xsltParseAnyXSLTElem(cctxt, cur);
+ break;
+ default:
+ xsltParseUnknownXSLTElem(cctxt, cur);
+ cur = cur->next;
+ continue;
+ }
+ } else {
+ /*
+ * Non-XSLT elements
+ * -----------------
+ */
+ xsltCompilerNodePush(cctxt, cur);
+ /*
+ * Update the in-scope namespaces if needed.
+ */
+ if (cur->nsDef != NULL)
+ cctxt->inode->inScopeNs =
+ xsltCompilerBuildInScopeNsList(cctxt, cur);
+ /*
+ * The current element is either a literal result element
+ * or an extension instruction.
+ *
+ * Process attr "xsl:extension-element-prefixes".
+ * FUTURE TODO: IIRC in XSLT 2.0 this attribute must be
+ * processed by the implementor of the extension function;
+ * i.e., it won't be handled by the XSLT processor.
+ */
+ /* SPEC 1.0:
+ * "exclude-result-prefixes" is only allowed on literal
+ * result elements and "xsl:exclude-result-prefixes"
+ * on xsl:stylesheet/xsl:transform.
+ * SPEC 2.0:
+ * "There are a number of standard attributes
+ * that may appear on any XSLT element: specifically
+ * version, exclude-result-prefixes,
+ * extension-element-prefixes, xpath-default-namespace,
+ * default-collation, and use-when."
+ *
+ * SPEC 2.0:
+ * For literal result elements:
+ * "xsl:version, xsl:exclude-result-prefixes,
+ * xsl:extension-element-prefixes,
+ * xsl:xpath-default-namespace,
+ * xsl:default-collation, or xsl:use-when."
+ */
+ if (cur->properties)
+ cctxt->inode->extElemNs =
+ xsltParseExtElemPrefixes(cctxt,
+ cur, cctxt->inode->extElemNs,
+ XSLT_ELEMENT_CATEGORY_LRE);
+ /*
+ * Eval if we have an extension instruction here.
+ */
+ if ((cur->ns != NULL) &&
+ (cctxt->inode->extElemNs != NULL) &&
+ (xsltCheckExtPrefix(cctxt->style, cur->ns->href) == 1))
+ {
+ /*
+ * Extension instructions
+ * ----------------------------------------------------
+ * Mark the node information.
+ */
+ cctxt->inode->category = XSLT_ELEMENT_CATEGORY_EXTENSION;
+ cctxt->inode->extContentHandled = 0;
+ if (cur->psvi != NULL) {
+ cur->psvi = NULL;
+ /*
+ * TODO: Temporary sanity check.
+ */
+ xsltTransformError(NULL, cctxt->style, cur,
+ "Internal error in xsltParseSequenceConstructor(): "
+ "Occupied PSVI field.\n");
+ cctxt->style->errors++;
+ cur = cur->next;
+ continue;
+ }
+ cur->psvi = (void *)
+ xsltPreComputeExtModuleElement(cctxt->style, cur);
+
+ if (cur->psvi == NULL) {
+ /*
+ * OLD COMMENT: "Unknown element, maybe registered
+ * at the context level. Mark it for later
+ * recognition."
+ * QUESTION: What does the xsltExtMarker mean?
+ * ANSWER: It is used in
+ * xsltApplySequenceConstructor() at
+ * transformation-time to look out for extension
+ * registered in the transformation context.
+ */
+ cur->psvi = (void *) xsltExtMarker;
+ }
+ /*
+ * BIG NOTE: Now the ugly part. In previous versions
+ * of Libxslt (until 1.1.16), all the content of an
+ * extension instruction was processed and compiled without
+ * the need of the extension-author to explicitely call
+ * such a processing;.We now need to mimic this old
+ * behaviour in order to avoid breaking old code
+ * on the extension-author's side.
+ * The mechanism:
+ * 1) If the author does *not* set the
+ * compile-time-flag @extContentHandled, then we'll
+ * parse the content assuming that it's a "template"
+ * (or "sequence constructor in XSLT 2.0 terms).
+ * NOTE: If the extension is registered at
+ * transformation-time only, then there's no way of
+ * knowing that content shall be valid, and we'll
+ * process the content the same way.
+ * 2) If the author *does* set the flag, then we'll assume
+ * that the author has handled the parsing him/herself
+ * (e.g. called xsltParseSequenceConstructor(), etc.
+ * explicitely in his/her code).
+ */
+ if ((cur->children != NULL) &&
+ (cctxt->inode->extContentHandled == 0))
+ {
+ /*
+ * Default parsing of the content using the
+ * sequence-constructor model.
+ */
+ xsltParseSequenceConstructor(cctxt, cur->children);
+ }
+ } else {
+ /*
+ * Literal result element
+ * ----------------------------------------------------
+ * Allowed XSLT attributes:
+ * xsl:extension-element-prefixes CDATA #IMPLIED
+ * xsl:exclude-result-prefixes CDATA #IMPLIED
+ * TODO: xsl:use-attribute-sets %qnames; #IMPLIED
+ * xsl:version NMTOKEN #IMPLIED
+ */
+ cur->psvi = NULL;
+ cctxt->inode->category = XSLT_ELEMENT_CATEGORY_LRE;
+ if (cur->properties != NULL) {
+ xmlAttrPtr attr = cur->properties;
+ /*
+ * Attribute "xsl:exclude-result-prefixes".
+ */
+ cctxt->inode->exclResultNs =
+ xsltParseExclResultPrefixes(cctxt, cur,
+ cctxt->inode->exclResultNs,
+ XSLT_ELEMENT_CATEGORY_LRE);
+ /*
+ * Attribute "xsl:version".
+ */
+ xsltParseAttrXSLTVersion(cctxt, cur,
+ XSLT_ELEMENT_CATEGORY_LRE);
+ /*
+ * Report invalid XSLT attributes.
+ * For XSLT 1.0 only xsl:use-attribute-sets is allowed
+ * next to xsl:version, xsl:exclude-result-prefixes and
+ * xsl:extension-element-prefixes.
+ *
+ * Mark all XSLT attributes, in order to skip such
+ * attributes when instantiating the LRE.
+ */
+ do {
+ if ((attr->psvi != xsltXSLTAttrMarker) &&
+ IS_XSLT_ATTR_FAST(attr))
+ {
+ if (! xmlStrEqual(attr->name,
+ BAD_CAST "use-attribute-sets"))
+ {
+ xsltTransformError(NULL, cctxt->style,
+ cur,
+ "Unknown XSLT attribute '%s'.\n",
+ attr->name);
+ cctxt->style->errors++;
+ } else {
+ /*
+ * XSLT attr marker.
+ */
+ attr->psvi = (void *) xsltXSLTAttrMarker;
+ }
+ }
+ attr = attr->next;
+ } while (attr != NULL);
+ }
+ /*
+ * Create/reuse info for the literal result element.
+ */
+ if (cctxt->inode->nsChanged)
+ xsltLREInfoCreate(cctxt, cur, 1);
+ cur->psvi = cctxt->inode->litResElemInfo;
+ /*
+ * Apply ns-aliasing on the element and on its attributes.
+ */
+ if (cctxt->hasNsAliases)
+ xsltLREBuildEffectiveNs(cctxt, cur);
+ /*
+ * Compile attribute value templates (AVT).
+ */
+ if (cur->properties) {
+ xmlAttrPtr attr = cur->properties;
+
+ while (attr != NULL) {
+ xsltCompileAttr(cctxt->style, attr);
+ attr = attr->next;
+ }
+ }
+ /*
+ * Parse the content, which is defined to be a "template"
+ * (or "sequence constructor" in XSLT 2.0 terms).
+ */
+ if (cur->children != NULL) {
+ xsltParseSequenceConstructor(cctxt, cur->children);
+ }
+ }
+ /*
+ * Leave the non-XSLT element.
+ */
+ xsltCompilerNodePop(cctxt, cur);
+ }
+ }
+ cur = cur->next;
+ }
+ if (deleteNode != NULL) {
+#ifdef WITH_XSLT_DEBUG_BLANKS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseSequenceConstructor: removing xsl:text element\n");
+#endif
+ xmlUnlinkNode(deleteNode);
+ xmlFreeNode(deleteNode);
+ deleteNode = NULL;
+ }
+}
+
+/**
+ * xsltParseTemplateContent:
+ * @style: the XSLT stylesheet
+ * @templ: the node containing the content to be parsed
+ *
+ * Parses and compiles the content-model of an xsl:template element.
+ * Note that this is *not* the "template" content model (or "sequence
+ * constructor" in XSLT 2.0); it it allows addional xsl:param
+ * elements as immediate children of @templ.
+ *
+ * Called by:
+ * exsltFuncFunctionComp() (EXSLT, functions.c)
+ * So this is intended to be called from extension functions.
+ */
+void
+xsltParseTemplateContent(xsltStylesheetPtr style, xmlNodePtr templ) {
+ if ((style == NULL) || (templ == NULL) ||
+ (templ->type == XML_NAMESPACE_DECL))
+ return;
+
+ /*
+ * Detection of handled content of extension instructions.
+ */
+ if (XSLT_CCTXT(style)->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) {
+ XSLT_CCTXT(style)->inode->extContentHandled = 1;
+ }
+
+ if (templ->children != NULL) {
+ xmlNodePtr child = templ->children;
+ /*
+ * Process xsl:param elements, which can only occur as the
+ * immediate children of xsl:template (well, and of any
+ * user-defined extension instruction if needed).
+ */
+ do {
+ if ((child->type == XML_ELEMENT_NODE) &&
+ IS_XSLT_ELEM_FAST(child) &&
+ IS_XSLT_NAME(child, "param"))
+ {
+ XSLT_CCTXT(style)->inode->curChildType = XSLT_FUNC_PARAM;
+ xsltParseAnyXSLTElem(XSLT_CCTXT(style), child);
+ } else
+ break;
+ child = child->next;
+ } while (child != NULL);
+ /*
+ * Parse the content and register the pattern.
+ */
+ xsltParseSequenceConstructor(XSLT_CCTXT(style), child);
+ }
+}
+
+#else /* XSLT_REFACTORED */
+
+/**
+ * xsltParseTemplateContent:
+ * @style: the XSLT stylesheet
+ * @templ: the container node (can be a document for literal results)
+ *
+ * parse a template content-model
+ * Clean-up the template content from unwanted ignorable blank nodes
+ * and process xslt:text
+ */
+void
+xsltParseTemplateContent(xsltStylesheetPtr style, xmlNodePtr templ) {
+ xmlNodePtr cur, delete;
+
+ if ((style == NULL) || (templ == NULL) ||
+ (templ->type == XML_NAMESPACE_DECL)) return;
+
+ /*
+ * This content comes from the stylesheet
+ * For stylesheets, the set of whitespace-preserving
+ * element names consists of just xsl:text.
+ */
+ cur = templ->children;
+ delete = NULL;
+ while (cur != NULL) {
+ if (delete != NULL) {
+#ifdef WITH_XSLT_DEBUG_BLANKS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseTemplateContent: removing text\n");
+#endif
+ xmlUnlinkNode(delete);
+ xmlFreeNode(delete);
+ delete = NULL;
+ }
+ if (IS_XSLT_ELEM(cur)) {
+ xsltStylePreCompute(style, cur);
+
+ if (IS_XSLT_NAME(cur, "text")) {
+ /*
+ * TODO: Processing of xsl:text should be moved to
+ * xsltPreprocessStylesheet(), since otherwise this
+ * will be performed for every multiply included
+ * stylesheet; i.e. this here is not skipped with
+ * the use of the style->nopreproc flag.
+ */
+ if (cur->children != NULL) {
+ xmlChar *prop;
+ xmlNodePtr text = cur->children, next;
+ int noesc = 0;
+
+ prop = xmlGetNsProp(cur,
+ (const xmlChar *)"disable-output-escaping",
+ NULL);
+ if (prop != NULL) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "Disable escaping: %s\n", text->content);
+#endif
+ if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
+ noesc = 1;
+ } else if (!xmlStrEqual(prop,
+ (const xmlChar *)"no")){
+ xsltTransformError(NULL, style, cur,
+ "xsl:text: disable-output-escaping allows only yes or no\n");
+ style->warnings++;
+
+ }
+ xmlFree(prop);
+ }
+
+ while (text != NULL) {
+ if (text->type == XML_COMMENT_NODE) {
+ text = text->next;
+ continue;
+ }
+ if ((text->type != XML_TEXT_NODE) &&
+ (text->type != XML_CDATA_SECTION_NODE)) {
+ xsltTransformError(NULL, style, cur,
+ "xsltParseTemplateContent: xslt:text content problem\n");
+ style->errors++;
+ break;
+ }
+ if ((noesc) && (text->type != XML_CDATA_SECTION_NODE))
+ text->name = xmlStringTextNoenc;
+ text = text->next;
+ }
+
+ /*
+ * replace xsl:text by the list of childs
+ */
+ if (text == NULL) {
+ text = cur->children;
+ while (text != NULL) {
+ if ((style->internalized) &&
+ (text->content != NULL) &&
+ (!xmlDictOwns(style->dict, text->content))) {
+
+ /*
+ * internalize the text string
+ */
+ if (text->doc->dict != NULL) {
+ const xmlChar *tmp;
+
+ tmp = xmlDictLookup(text->doc->dict,
+ text->content, -1);
+ if (tmp != text->content) {
+ xmlNodeSetContent(text, NULL);
+ text->content = (xmlChar *) tmp;
+ }
+ }
+ }
+
+ next = text->next;
+ xmlUnlinkNode(text);
+ xmlAddPrevSibling(cur, text);
+ text = next;
+ }
+ }
+ }
+ delete = cur;
+ goto skip_children;
+ }
+ }
+ else if ((cur->ns != NULL) && (style->nsDefs != NULL) &&
+ (xsltCheckExtPrefix(style, cur->ns->prefix)))
+ {
+ /*
+ * okay this is an extension element compile it too
+ */
+ xsltStylePreCompute(style, cur);
+ }
+ else if (cur->type == XML_ELEMENT_NODE)
+ {
+ /*
+ * This is an element which will be output as part of the
+ * template exectution, precompile AVT if found.
+ */
+ if ((cur->ns == NULL) && (style->defaultAlias != NULL)) {
+ cur->ns = xmlSearchNsByHref(cur->doc, cur,
+ style->defaultAlias);
+ }
+ if (cur->properties != NULL) {
+ xmlAttrPtr attr = cur->properties;
+
+ while (attr != NULL) {
+ xsltCompileAttr(style, attr);
+ attr = attr->next;
+ }
+ }
+ }
+ /*
+ * Skip to next node
+ */
+ if (cur->children != NULL) {
+ if (cur->children->type != XML_ENTITY_DECL) {
+ cur = cur->children;
+ continue;
+ }
+ }
+skip_children:
+ if (cur->next != NULL) {
+ cur = cur->next;
+ continue;
+ }
+
+ do {
+ cur = cur->parent;
+ if (cur == NULL)
+ break;
+ if (cur == templ) {
+ cur = NULL;
+ break;
+ }
+ if (cur->next != NULL) {
+ cur = cur->next;
+ break;
+ }
+ } while (cur != NULL);
+ }
+ if (delete != NULL) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseTemplateContent: removing text\n");
+#endif
+ xmlUnlinkNode(delete);
+ xmlFreeNode(delete);
+ delete = NULL;
+ }
+
+ /*
+ * Skip the first params
+ */
+ cur = templ->children;
+ while (cur != NULL) {
+ if ((IS_XSLT_ELEM(cur)) && (!(IS_XSLT_NAME(cur, "param"))))
+ break;
+ cur = cur->next;
+ }
+
+ /*
+ * Browse the remainder of the template
+ */
+ while (cur != NULL) {
+ if ((IS_XSLT_ELEM(cur)) && (IS_XSLT_NAME(cur, "param"))) {
+ xmlNodePtr param = cur;
+
+ xsltTransformError(NULL, style, cur,
+ "xsltParseTemplateContent: ignoring misplaced param element\n");
+ if (style != NULL) style->warnings++;
+ cur = cur->next;
+ xmlUnlinkNode(param);
+ xmlFreeNode(param);
+ } else
+ break;
+ }
+}
+
+#endif /* else XSLT_REFACTORED */
+
+/**
+ * xsltParseStylesheetKey:
+ * @style: the XSLT stylesheet
+ * @key: the "key" element
+ *
+ * <!-- Category: top-level-element -->
+ * <xsl:key name = qname, match = pattern, use = expression />
+ *
+ * parse an XSLT stylesheet key definition and register it
+ */
+
+static void
+xsltParseStylesheetKey(xsltStylesheetPtr style, xmlNodePtr key) {
+ xmlChar *prop = NULL;
+ xmlChar *use = NULL;
+ xmlChar *match = NULL;
+ xmlChar *name = NULL;
+ xmlChar *nameURI = NULL;
+
+ if ((style == NULL) || (key == NULL) || (key->type != XML_ELEMENT_NODE))
+ return;
+
+ /*
+ * Get arguments
+ */
+ prop = xmlGetNsProp(key, (const xmlChar *)"name", NULL);
+ if (prop != NULL) {
+ const xmlChar *URI;
+
+ /*
+ * TODO: Don't use xsltGetQNameURI().
+ */
+ URI = xsltGetQNameURI(key, &prop);
+ if (prop == NULL) {
+ if (style != NULL) style->errors++;
+ goto error;
+ } else {
+ name = prop;
+ if (URI != NULL)
+ nameURI = xmlStrdup(URI);
+ }
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseStylesheetKey: name %s\n", name);
+#endif
+ } else {
+ xsltTransformError(NULL, style, key,
+ "xsl:key : error missing name\n");
+ if (style != NULL) style->errors++;
+ goto error;
+ }
+
+ match = xmlGetNsProp(key, (const xmlChar *)"match", NULL);
+ if (match == NULL) {
+ xsltTransformError(NULL, style, key,
+ "xsl:key : error missing match\n");
+ if (style != NULL) style->errors++;
+ goto error;
+ }
+
+ use = xmlGetNsProp(key, (const xmlChar *)"use", NULL);
+ if (use == NULL) {
+ xsltTransformError(NULL, style, key,
+ "xsl:key : error missing use\n");
+ if (style != NULL) style->errors++;
+ goto error;
+ }
+
+ /*
+ * register the keys
+ */
+ xsltAddKey(style, name, nameURI, match, use, key);
+
+
+error:
+ if (use != NULL)
+ xmlFree(use);
+ if (match != NULL)
+ xmlFree(match);
+ if (name != NULL)
+ xmlFree(name);
+ if (nameURI != NULL)
+ xmlFree(nameURI);
+
+ if (key->children != NULL) {
+ xsltParseContentError(style, key->children);
+ }
+}
+
+#ifdef XSLT_REFACTORED
+/**
+ * xsltParseXSLTTemplate:
+ * @style: the XSLT stylesheet
+ * @template: the "template" element
+ *
+ * parse an XSLT stylesheet template building the associated structures
+ * TODO: Is @style ever expected to be NULL?
+ *
+ * Called from:
+ * xsltParseXSLTStylesheet()
+ * xsltParseStylesheetTop()
+ */
+
+static void
+xsltParseXSLTTemplate(xsltCompilerCtxtPtr cctxt, xmlNodePtr templNode) {
+ xsltTemplatePtr templ;
+ xmlChar *prop;
+ double priority;
+
+ if ((cctxt == NULL) || (templNode == NULL) ||
+ (templNode->type != XML_ELEMENT_NODE))
+ return;
+
+ /*
+ * Create and link the structure
+ */
+ templ = xsltNewTemplate();
+ if (templ == NULL)
+ return;
+
+ xsltCompilerNodePush(cctxt, templNode);
+ if (templNode->nsDef != NULL)
+ cctxt->inode->inScopeNs =
+ xsltCompilerBuildInScopeNsList(cctxt, templNode);
+
+ templ->next = cctxt->style->templates;
+ cctxt->style->templates = templ;
+ templ->style = cctxt->style;
+
+ /*
+ * Attribute "mode".
+ */
+ prop = xmlGetNsProp(templNode, (const xmlChar *)"mode", NULL);
+ if (prop != NULL) {
+ const xmlChar *modeURI;
+
+ /*
+ * TODO: We need a standardized function for extraction
+ * of namespace names and local names from QNames.
+ * Don't use xsltGetQNameURI() as it cannot channe�
+ * reports through the context.
+ */
+ modeURI = xsltGetQNameURI(templNode, &prop);
+ if (prop == NULL) {
+ cctxt->style->errors++;
+ goto error;
+ }
+ templ->mode = xmlDictLookup(cctxt->style->dict, prop, -1);
+ xmlFree(prop);
+ prop = NULL;
+ if (xmlValidateNCName(templ->mode, 0)) {
+ xsltTransformError(NULL, cctxt->style, templNode,
+ "xsl:template: Attribute 'mode': The local part '%s' "
+ "of the value is not a valid NCName.\n", templ->name);
+ cctxt->style->errors++;
+ goto error;
+ }
+ if (modeURI != NULL)
+ templ->modeURI = xmlDictLookup(cctxt->style->dict, modeURI, -1);
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseXSLTTemplate: mode %s\n", templ->mode);
+#endif
+ }
+ /*
+ * Attribute "match".
+ */
+ prop = xmlGetNsProp(templNode, (const xmlChar *)"match", NULL);
+ if (prop != NULL) {
+ templ->match = prop;
+ prop = NULL;
+ }
+ /*
+ * Attribute "priority".
+ */
+ prop = xmlGetNsProp(templNode, (const xmlChar *)"priority", NULL);
+ if (prop != NULL) {
+ priority = xmlXPathStringEvalNumber(prop);
+ templ->priority = (float) priority;
+ xmlFree(prop);
+ prop = NULL;
+ }
+ /*
+ * Attribute "name".
+ */
+ prop = xmlGetNsProp(templNode, (const xmlChar *)"name", NULL);
+ if (prop != NULL) {
+ const xmlChar *nameURI;
+ xsltTemplatePtr curTempl;
+
+ /*
+ * TODO: Don't use xsltGetQNameURI().
+ */
+ nameURI = xsltGetQNameURI(templNode, &prop);
+ if (prop == NULL) {
+ cctxt->style->errors++;
+ goto error;
+ }
+ templ->name = xmlDictLookup(cctxt->style->dict, prop, -1);
+ xmlFree(prop);
+ prop = NULL;
+ if (xmlValidateNCName(templ->name, 0)) {
+ xsltTransformError(NULL, cctxt->style, templNode,
+ "xsl:template: Attribute 'name': The local part '%s' of "
+ "the value is not a valid NCName.\n", templ->name);
+ cctxt->style->errors++;
+ goto error;
+ }
+ if (nameURI != NULL)
+ templ->nameURI = xmlDictLookup(cctxt->style->dict, nameURI, -1);
+ curTempl = templ->next;
+ while (curTempl != NULL) {
+ if ((nameURI != NULL && xmlStrEqual(curTempl->name, templ->name) &&
+ xmlStrEqual(curTempl->nameURI, nameURI) ) ||
+ (nameURI == NULL && curTempl->nameURI == NULL &&
+ xmlStrEqual(curTempl->name, templ->name)))
+ {
+ xsltTransformError(NULL, cctxt->style, templNode,
+ "xsl:template: error duplicate name '%s'\n", templ->name);
+ cctxt->style->errors++;
+ goto error;
+ }
+ curTempl = curTempl->next;
+ }
+ }
+ if (templNode->children != NULL) {
+ xsltParseTemplateContent(cctxt->style, templNode);
+ /*
+ * MAYBE TODO: Custom behaviour: In order to stay compatible with
+ * Xalan and MSXML(.NET), we could allow whitespace
+ * to appear before an xml:param element; this whitespace
+ * will additionally become part of the "template".
+ * NOTE that this is totally deviates from the spec, but
+ * is the de facto behaviour of Xalan and MSXML(.NET).
+ * Personally I wouldn't allow this, since if we have:
+ * <xsl:template ...xml:space="preserve">
+ * <xsl:param name="foo"/>
+ * <xsl:param name="bar"/>
+ * <xsl:param name="zoo"/>
+ * ... the whitespace between every xsl:param would be
+ * added to the result tree.
+ */
+ }
+
+ templ->elem = templNode;
+ templ->content = templNode->children;
+ xsltAddTemplate(cctxt->style, templ, templ->mode, templ->modeURI);
+
+error:
+ xsltCompilerNodePop(cctxt, templNode);
+ return;
+}
+
+#else /* XSLT_REFACTORED */
+
+/**
+ * xsltParseStylesheetTemplate:
+ * @style: the XSLT stylesheet
+ * @template: the "template" element
+ *
+ * parse an XSLT stylesheet template building the associated structures
+ */
+
+static void
+xsltParseStylesheetTemplate(xsltStylesheetPtr style, xmlNodePtr template) {
+ xsltTemplatePtr ret;
+ xmlChar *prop;
+ xmlChar *mode = NULL;
+ xmlChar *modeURI = NULL;
+ double priority;
+
+ if ((style == NULL) || (template == NULL) ||
+ (template->type != XML_ELEMENT_NODE))
+ return;
+
+ /*
+ * Create and link the structure
+ */
+ ret = xsltNewTemplate();
+ if (ret == NULL)
+ return;
+ ret->next = style->templates;
+ style->templates = ret;
+ ret->style = style;
+
+ /*
+ * Get inherited namespaces
+ */
+ /*
+ * TODO: Apply the optimized in-scope-namespace mechanism
+ * as for the other XSLT instructions.
+ */
+ xsltGetInheritedNsList(style, ret, template);
+
+ /*
+ * Get arguments
+ */
+ prop = xmlGetNsProp(template, (const xmlChar *)"mode", NULL);
+ if (prop != NULL) {
+ const xmlChar *URI;
+
+ /*
+ * TODO: Don't use xsltGetQNameURI().
+ */
+ URI = xsltGetQNameURI(template, &prop);
+ if (prop == NULL) {
+ if (style != NULL) style->errors++;
+ goto error;
+ } else {
+ mode = prop;
+ if (URI != NULL)
+ modeURI = xmlStrdup(URI);
+ }
+ ret->mode = xmlDictLookup(style->dict, mode, -1);
+ ret->modeURI = xmlDictLookup(style->dict, modeURI, -1);
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseStylesheetTemplate: mode %s\n", mode);
+#endif
+ if (mode != NULL) xmlFree(mode);
+ if (modeURI != NULL) xmlFree(modeURI);
+ }
+ prop = xmlGetNsProp(template, (const xmlChar *)"match", NULL);
+ if (prop != NULL) {
+ if (ret->match != NULL) xmlFree(ret->match);
+ ret->match = prop;
+ }
+
+ prop = xmlGetNsProp(template, (const xmlChar *)"priority", NULL);
+ if (prop != NULL) {
+ priority = xmlXPathStringEvalNumber(prop);
+ ret->priority = (float) priority;
+ xmlFree(prop);
+ }
+
+ prop = xmlGetNsProp(template, (const xmlChar *)"name", NULL);
+ if (prop != NULL) {
+ const xmlChar *URI;
+
+ /*
+ * TODO: Don't use xsltGetQNameURI().
+ */
+ URI = xsltGetQNameURI(template, &prop);
+ if (prop == NULL) {
+ if (style != NULL) style->errors++;
+ goto error;
+ } else {
+ if (xmlValidateNCName(prop,0)) {
+ xsltTransformError(NULL, style, template,
+ "xsl:template : error invalid name '%s'\n", prop);
+ if (style != NULL) style->errors++;
+ goto error;
+ }
+ ret->name = xmlDictLookup(style->dict, BAD_CAST prop, -1);
+ xmlFree(prop);
+ prop = NULL;
+ if (URI != NULL)
+ ret->nameURI = xmlDictLookup(style->dict, BAD_CAST URI, -1);
+ else
+ ret->nameURI = NULL;
+ }
+ }
+
+ /*
+ * parse the content and register the pattern
+ */
+ xsltParseTemplateContent(style, template);
+ ret->elem = template;
+ ret->content = template->children;
+ xsltAddTemplate(style, ret, ret->mode, ret->modeURI);
+
+error:
+ return;
+}
+
+#endif /* else XSLT_REFACTORED */
+
+#ifdef XSLT_REFACTORED
+
+/**
+ * xsltIncludeComp:
+ * @cctxt: the compilation contenxt
+ * @node: the xsl:include node
+ *
+ * Process the xslt include node on the source node
+ */
+static xsltStyleItemIncludePtr
+xsltCompileXSLTIncludeElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) {
+ xsltStyleItemIncludePtr item;
+
+ if ((cctxt == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE))
+ return(NULL);
+
+ node->psvi = NULL;
+ item = (xsltStyleItemIncludePtr) xmlMalloc(sizeof(xsltStyleItemInclude));
+ if (item == NULL) {
+ xsltTransformError(NULL, cctxt->style, node,
+ "xsltIncludeComp : malloc failed\n");
+ cctxt->style->errors++;
+ return(NULL);
+ }
+ memset(item, 0, sizeof(xsltStyleItemInclude));
+
+ node->psvi = item;
+ item->inst = node;
+ item->type = XSLT_FUNC_INCLUDE;
+
+ item->next = cctxt->style->preComps;
+ cctxt->style->preComps = (xsltElemPreCompPtr) item;
+
+ return(item);
+}
+
+/**
+ * xsltParseFindTopLevelElem:
+ */
+static int
+xsltParseFindTopLevelElem(xsltCompilerCtxtPtr cctxt,
+ xmlNodePtr cur,
+ const xmlChar *name,
+ const xmlChar *namespaceURI,
+ int breakOnOtherElem,
+ xmlNodePtr *resultNode)
+{
+ if (name == NULL)
+ return(-1);
+
+ *resultNode = NULL;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE) {
+ if ((cur->ns != NULL) && (cur->name != NULL)) {
+ if ((*(cur->name) == *name) &&
+ xmlStrEqual(cur->name, name) &&
+ xmlStrEqual(cur->ns->href, namespaceURI))
+ {
+ *resultNode = cur;
+ return(1);
+ }
+ }
+ if (breakOnOtherElem)
+ break;
+ }
+ cur = cur->next;
+ }
+ *resultNode = cur;
+ return(0);
+}
+
+static int
+xsltParseTopLevelXSLTElem(xsltCompilerCtxtPtr cctxt,
+ xmlNodePtr node,
+ xsltStyleType type)
+{
+ int ret = 0;
+
+ /*
+ * TODO: The reason why this function exists:
+ * due to historical reasons some of the
+ * top-level declarations are processed by functions
+ * in other files. Since we need still to set
+ * up the node-info and generate information like
+ * in-scope namespaces, this is a wrapper around
+ * those old parsing functions.
+ */
+ xsltCompilerNodePush(cctxt, node);
+ if (node->nsDef != NULL)
+ cctxt->inode->inScopeNs =
+ xsltCompilerBuildInScopeNsList(cctxt, node);
+ cctxt->inode->type = type;
+
+ switch (type) {
+ case XSLT_FUNC_INCLUDE:
+ {
+ int oldIsInclude;
+
+ if (xsltCompileXSLTIncludeElem(cctxt, node) == NULL)
+ goto exit;
+ /*
+ * Mark this stylesheet tree as being currently included.
+ */
+ oldIsInclude = cctxt->isInclude;
+ cctxt->isInclude = 1;
+
+ if (xsltParseStylesheetInclude(cctxt->style, node) != 0) {
+ cctxt->style->errors++;
+ }
+ cctxt->isInclude = oldIsInclude;
+ }
+ break;
+ case XSLT_FUNC_PARAM:
+ xsltStylePreCompute(cctxt->style, node);
+ xsltParseGlobalParam(cctxt->style, node);
+ break;
+ case XSLT_FUNC_VARIABLE:
+ xsltStylePreCompute(cctxt->style, node);
+ xsltParseGlobalVariable(cctxt->style, node);
+ break;
+ case XSLT_FUNC_ATTRSET:
+ xsltParseStylesheetAttributeSet(cctxt->style, node);
+ break;
+ default:
+ xsltTransformError(NULL, cctxt->style, node,
+ "Internal error: (xsltParseTopLevelXSLTElem) "
+ "Cannot handle this top-level declaration.\n");
+ cctxt->style->errors++;
+ ret = -1;
+ }
+
+exit:
+ xsltCompilerNodePop(cctxt, node);
+
+ return(ret);
+}
+
+#if 0
+static int
+xsltParseRemoveWhitespace(xmlNodePtr node)
+{
+ if ((node == NULL) || (node->children == NULL))
+ return(0);
+ else {
+ xmlNodePtr delNode = NULL, child = node->children;
+
+ do {
+ if (delNode) {
+ xmlUnlinkNode(delNode);
+ xmlFreeNode(delNode);
+ delNode = NULL;
+ }
+ if (((child->type == XML_TEXT_NODE) ||
+ (child->type == XML_CDATA_SECTION_NODE)) &&
+ (IS_BLANK_NODE(child)))
+ delNode = child;
+ child = child->next;
+ } while (child != NULL);
+ if (delNode) {
+ xmlUnlinkNode(delNode);
+ xmlFreeNode(delNode);
+ delNode = NULL;
+ }
+ }
+ return(0);
+}
+#endif
+
+static int
+xsltParseXSLTStylesheetElemCore(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
+{
+#ifdef WITH_XSLT_DEBUG_PARSING
+ int templates = 0;
+#endif
+ xmlNodePtr cur, start = NULL;
+ xsltStylesheetPtr style;
+
+ if ((cctxt == NULL) || (node == NULL) ||
+ (node->type != XML_ELEMENT_NODE))
+ return(-1);
+
+ style = cctxt->style;
+ /*
+ * At this stage all import declarations of all stylesheet modules
+ * with the same stylesheet level have been processed.
+ * Now we can safely parse the rest of the declarations.
+ */
+ if (IS_XSLT_ELEM_FAST(node) && IS_XSLT_NAME(node, "include"))
+ {
+ xsltDocumentPtr include;
+ /*
+ * URGENT TODO: Make this work with simplified stylesheets!
+ * I.e., when we won't find an xsl:stylesheet element.
+ */
+ /*
+ * This is as include declaration.
+ */
+ include = ((xsltStyleItemIncludePtr) node->psvi)->include;
+ if (include == NULL) {
+ /* TODO: raise error? */
+ return(-1);
+ }
+ /*
+ * TODO: Actually an xsl:include should locate an embedded
+ * stylesheet as well; so the document-element won't always
+ * be the element where the actual stylesheet is rooted at.
+ * But such embedded stylesheets are not supported by Libxslt yet.
+ */
+ node = xmlDocGetRootElement(include->doc);
+ if (node == NULL) {
+ return(-1);
+ }
+ }
+
+ if (node->children == NULL)
+ return(0);
+ /*
+ * Push the xsl:stylesheet/xsl:transform element.
+ */
+ xsltCompilerNodePush(cctxt, node);
+ cctxt->inode->isRoot = 1;
+ cctxt->inode->nsChanged = 0;
+ /*
+ * Start with the naked dummy info for literal result elements.
+ */
+ cctxt->inode->litResElemInfo = cctxt->inodeList->litResElemInfo;
+
+ /*
+ * In every case, we need to have
+ * the in-scope namespaces of the element, where the
+ * stylesheet is rooted at, regardless if it's an XSLT
+ * instruction or a literal result instruction (or if
+ * this is an embedded stylesheet).
+ */
+ cctxt->inode->inScopeNs =
+ xsltCompilerBuildInScopeNsList(cctxt, node);
+
+ /*
+ * Process attributes of xsl:stylesheet/xsl:transform.
+ * --------------------------------------------------
+ * Allowed are:
+ * id = id
+ * extension-element-prefixes = tokens
+ * exclude-result-prefixes = tokens
+ * version = number (mandatory)
+ */
+ if (xsltParseAttrXSLTVersion(cctxt, node,
+ XSLT_ELEMENT_CATEGORY_XSLT) == 0)
+ {
+ /*
+ * Attribute "version".
+ * XSLT 1.0: "An xsl:stylesheet element *must* have a version
+ * attribute, indicating the version of XSLT that the
+ * stylesheet requires".
+ * The root element of a simplified stylesheet must also have
+ * this attribute.
+ */
+#ifdef XSLT_REFACTORED_MANDATORY_VERSION
+ if (isXsltElem)
+ xsltTransformError(NULL, cctxt->style, node,
+ "The attribute 'version' is missing.\n");
+ cctxt->style->errors++;
+#else
+ /* OLD behaviour. */
+ xsltTransformError(NULL, cctxt->style, node,
+ "xsl:version is missing: document may not be a stylesheet\n");
+ cctxt->style->warnings++;
+#endif
+ }
+ /*
+ * The namespaces declared by the attributes
+ * "extension-element-prefixes" and
+ * "exclude-result-prefixes" are local to *this*
+ * stylesheet tree; i.e., they are *not* visible to
+ * other stylesheet-modules, whether imported or included.
+ *
+ * Attribute "extension-element-prefixes".
+ */
+ cctxt->inode->extElemNs =
+ xsltParseExtElemPrefixes(cctxt, node, NULL,
+ XSLT_ELEMENT_CATEGORY_XSLT);
+ /*
+ * Attribute "exclude-result-prefixes".
+ */
+ cctxt->inode->exclResultNs =
+ xsltParseExclResultPrefixes(cctxt, node, NULL,
+ XSLT_ELEMENT_CATEGORY_XSLT);
+ /*
+ * Create/reuse info for the literal result element.
+ */
+ if (cctxt->inode->nsChanged)
+ xsltLREInfoCreate(cctxt, node, 0);
+ /*
+ * Processed top-level elements:
+ * ----------------------------
+ * xsl:variable, xsl:param (QName, in-scope ns,
+ * expression (vars allowed))
+ * xsl:attribute-set (QName, in-scope ns)
+ * xsl:strip-space, xsl:preserve-space (XPath NameTests,
+ * in-scope ns)
+ * I *think* global scope, merge with includes
+ * xsl:output (QName, in-scope ns)
+ * xsl:key (QName, in-scope ns, pattern,
+ * expression (vars *not* allowed))
+ * xsl:decimal-format (QName, needs in-scope ns)
+ * xsl:namespace-alias (in-scope ns)
+ * global scope, merge with includes
+ * xsl:template (last, QName, pattern)
+ *
+ * (whitespace-only text-nodes have *not* been removed
+ * yet; this will be done in xsltParseSequenceConstructor)
+ *
+ * Report misplaced child-nodes first.
+ */
+ cur = node->children;
+ while (cur != NULL) {
+ if (cur->type == XML_TEXT_NODE) {
+ xsltTransformError(NULL, style, cur,
+ "Misplaced text node (content: '%s').\n",
+ (cur->content != NULL) ? cur->content : BAD_CAST "");
+ style->errors++;
+ } else if (cur->type != XML_ELEMENT_NODE) {
+ xsltTransformError(NULL, style, cur, "Misplaced node.\n");
+ style->errors++;
+ }
+ cur = cur->next;
+ }
+ /*
+ * Skip xsl:import elements; they have been processed
+ * already.
+ */
+ cur = node->children;
+ while ((cur != NULL) && xsltParseFindTopLevelElem(cctxt, cur,
+ BAD_CAST "import", XSLT_NAMESPACE, 1, &cur) == 1)
+ cur = cur->next;
+ if (cur == NULL)
+ goto exit;
+
+ start = cur;
+ /*
+ * Process all top-level xsl:param elements.
+ */
+ while ((cur != NULL) &&
+ xsltParseFindTopLevelElem(cctxt, cur,
+ BAD_CAST "param", XSLT_NAMESPACE, 0, &cur) == 1)
+ {
+ xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_PARAM);
+ cur = cur->next;
+ }
+ /*
+ * Process all top-level xsl:variable elements.
+ */
+ cur = start;
+ while ((cur != NULL) &&
+ xsltParseFindTopLevelElem(cctxt, cur,
+ BAD_CAST "variable", XSLT_NAMESPACE, 0, &cur) == 1)
+ {
+ xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_VARIABLE);
+ cur = cur->next;
+ }
+ /*
+ * Process all the rest of top-level elements.
+ */
+ cur = start;
+ while (cur != NULL) {
+ /*
+ * Process element nodes.
+ */
+ if (cur->type == XML_ELEMENT_NODE) {
+ if (cur->ns == NULL) {
+ xsltTransformError(NULL, style, cur,
+ "Unexpected top-level element in no namespace.\n");
+ style->errors++;
+ cur = cur->next;
+ continue;
+ }
+ /*
+ * Process all XSLT elements.
+ */
+ if (IS_XSLT_ELEM_FAST(cur)) {
+ /*
+ * xsl:import is only allowed at the beginning.
+ */
+ if (IS_XSLT_NAME(cur, "import")) {
+ xsltTransformError(NULL, style, cur,
+ "Misplaced xsl:import element.\n");
+ style->errors++;
+ cur = cur->next;
+ continue;
+ }
+ /*
+ * TODO: Change the return type of the parsing functions
+ * to int.
+ */
+ if (IS_XSLT_NAME(cur, "template")) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ templates++;
+#endif
+ /*
+ * TODO: Is the position of xsl:template in the
+ * tree significant? If not it would be easier to
+ * parse them at a later stage.
+ */
+ xsltParseXSLTTemplate(cctxt, cur);
+ } else if (IS_XSLT_NAME(cur, "variable")) {
+ /* NOP; done already */
+ } else if (IS_XSLT_NAME(cur, "param")) {
+ /* NOP; done already */
+ } else if (IS_XSLT_NAME(cur, "include")) {
+ if (cur->psvi != NULL)
+ xsltParseXSLTStylesheetElemCore(cctxt, cur);
+ else {
+ xsltTransformError(NULL, style, cur,
+ "Internal error: "
+ "(xsltParseXSLTStylesheetElemCore) "
+ "The xsl:include element was not compiled.\n");
+ style->errors++;
+ }
+ } else if (IS_XSLT_NAME(cur, "strip-space")) {
+ /* No node info needed. */
+ xsltParseStylesheetStripSpace(style, cur);
+ } else if (IS_XSLT_NAME(cur, "preserve-space")) {
+ /* No node info needed. */
+ xsltParseStylesheetPreserveSpace(style, cur);
+ } else if (IS_XSLT_NAME(cur, "output")) {
+ /* No node-info needed. */
+ xsltParseStylesheetOutput(style, cur);
+ } else if (IS_XSLT_NAME(cur, "key")) {
+ /* TODO: node-info needed for expressions ? */
+ xsltParseStylesheetKey(style, cur);
+ } else if (IS_XSLT_NAME(cur, "decimal-format")) {
+ /* No node-info needed. */
+ xsltParseStylesheetDecimalFormat(style, cur);
+ } else if (IS_XSLT_NAME(cur, "attribute-set")) {
+ xsltParseTopLevelXSLTElem(cctxt, cur,
+ XSLT_FUNC_ATTRSET);
+ } else if (IS_XSLT_NAME(cur, "namespace-alias")) {
+ /* NOP; done already */
+ } else {
+ if (cctxt->inode->forwardsCompat) {
+ /*
+ * Forwards-compatible mode:
+ *
+ * XSLT-1: "if it is a top-level element and
+ * XSLT 1.0 does not allow such elements as top-level
+ * elements, then the element must be ignored along
+ * with its content;"
+ */
+ /*
+ * TODO: I don't think we should generate a warning.
+ */
+ xsltTransformError(NULL, style, cur,
+ "Forwards-compatible mode: Ignoring unknown XSLT "
+ "element '%s'.\n", cur->name);
+ style->warnings++;
+ } else {
+ xsltTransformError(NULL, style, cur,
+ "Unknown XSLT element '%s'.\n", cur->name);
+ style->errors++;
+ }
+ }
+ } else {
+ xsltTopLevelFunction function;
+
+ /*
+ * Process non-XSLT elements, which are in a
+ * non-NULL namespace.
+ */
+ /*
+ * QUESTION: What does xsltExtModuleTopLevelLookup()
+ * do exactly?
+ */
+ function = xsltExtModuleTopLevelLookup(cur->name,
+ cur->ns->href);
+ if (function != NULL)
+ function(style, cur);
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseXSLTStylesheetElemCore : User-defined "
+ "data element '%s'.\n", cur->name);
+#endif
+ }
+ }
+ cur = cur->next;
+ }
+
+exit:
+
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "### END of parsing top-level elements of doc '%s'.\n",
+ node->doc->URL);
+ xsltGenericDebug(xsltGenericDebugContext,
+ "### Templates: %d\n", templates);
+#ifdef XSLT_REFACTORED
+ xsltGenericDebug(xsltGenericDebugContext,
+ "### Max inodes: %d\n", cctxt->maxNodeInfos);
+ xsltGenericDebug(xsltGenericDebugContext,
+ "### Max LREs : %d\n", cctxt->maxLREs);
+#endif /* XSLT_REFACTORED */
+#endif /* WITH_XSLT_DEBUG_PARSING */
+
+ xsltCompilerNodePop(cctxt, node);
+ return(0);
+}
+
+/**
+ * xsltParseXSLTStylesheet:
+ * @cctxt: the compiler context
+ * @node: the xsl:stylesheet/xsl:transform element-node
+ *
+ * Parses the xsl:stylesheet and xsl:transform element.
+ *
+ * <xsl:stylesheet
+ * id = id
+ * extension-element-prefixes = tokens
+ * exclude-result-prefixes = tokens
+ * version = number>
+ * <!-- Content: (xsl:import*, top-level-elements) -->
+ * </xsl:stylesheet>
+ *
+ * BIG TODO: The xsl:include stuff.
+ *
+ * Called by xsltParseStylesheetTree()
+ *
+ * Returns 0 on success, a positive result on errors and
+ * -1 on API or internal errors.
+ */
+static int
+xsltParseXSLTStylesheetElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
+{
+ xmlNodePtr cur, start;
+
+ if ((cctxt == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE))
+ return(-1);
+
+ if (node->children == NULL)
+ goto exit;
+
+ /*
+ * Process top-level elements:
+ * xsl:import (must be first)
+ * xsl:include (this is just a pre-processing)
+ */
+ cur = node->children;
+ /*
+ * Process xsl:import elements.
+ * XSLT 1.0: "The xsl:import element children must precede all
+ * other element children of an xsl:stylesheet element,
+ * including any xsl:include element children."
+ */
+ while ((cur != NULL) &&
+ xsltParseFindTopLevelElem(cctxt, cur,
+ BAD_CAST "import", XSLT_NAMESPACE, 1, &cur) == 1)
+ {
+ if (xsltParseStylesheetImport(cctxt->style, cur) != 0) {
+ cctxt->style->errors++;
+ }
+ cur = cur->next;
+ }
+ if (cur == NULL)
+ goto exit;
+ start = cur;
+ /*
+ * Pre-process all xsl:include elements.
+ */
+ cur = start;
+ while ((cur != NULL) &&
+ xsltParseFindTopLevelElem(cctxt, cur,
+ BAD_CAST "include", XSLT_NAMESPACE, 0, &cur) == 1)
+ {
+ xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_INCLUDE);
+ cur = cur->next;
+ }
+ /*
+ * Pre-process all xsl:namespace-alias elements.
+ * URGENT TODO: This won't work correctly: the order of included
+ * aliases and aliases defined here is significant.
+ */
+ cur = start;
+ while ((cur != NULL) &&
+ xsltParseFindTopLevelElem(cctxt, cur,
+ BAD_CAST "namespace-alias", XSLT_NAMESPACE, 0, &cur) == 1)
+ {
+ xsltNamespaceAlias(cctxt->style, cur);
+ cur = cur->next;
+ }
+
+ if (cctxt->isInclude) {
+ /*
+ * If this stylesheet is intended for inclusion, then
+ * we will process only imports and includes.
+ */
+ goto exit;
+ }
+ /*
+ * Now parse the rest of the top-level elements.
+ */
+ xsltParseXSLTStylesheetElemCore(cctxt, node);
+exit:
+
+ return(0);
+}
+
+#else /* XSLT_REFACTORED */
+
+/**
+ * xsltParseStylesheetTop:
+ * @style: the XSLT stylesheet
+ * @top: the top level "stylesheet" or "transform" element
+ *
+ * scan the top level elements of an XSL stylesheet
+ */
+static void
+xsltParseStylesheetTop(xsltStylesheetPtr style, xmlNodePtr top) {
+ xmlNodePtr cur;
+ xmlChar *prop;
+#ifdef WITH_XSLT_DEBUG_PARSING
+ int templates = 0;
+#endif
+
+ if ((top == NULL) || (top->type != XML_ELEMENT_NODE))
+ return;
+
+ prop = xmlGetNsProp(top, (const xmlChar *)"version", NULL);
+ if (prop == NULL) {
+ xsltTransformError(NULL, style, top,
+ "xsl:version is missing: document may not be a stylesheet\n");
+ if (style != NULL) style->warnings++;
+ } else {
+ if ((!xmlStrEqual(prop, (const xmlChar *)"1.0")) &&
+ (!xmlStrEqual(prop, (const xmlChar *)"1.1"))) {
+ xsltTransformError(NULL, style, top,
+ "xsl:version: only 1.1 features are supported\n");
+ if (style != NULL) {
+ style->forwards_compatible = 1;
+ style->warnings++;
+ }
+ }
+ xmlFree(prop);
+ }
+
+ /*
+ * process xsl:import elements
+ */
+ cur = top->children;
+ while (cur != NULL) {
+ if (IS_BLANK_NODE(cur)) {
+ cur = cur->next;
+ continue;
+ }
+ if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "import")) {
+ if (xsltParseStylesheetImport(style, cur) != 0)
+ if (style != NULL) style->errors++;
+ } else
+ break;
+ cur = cur->next;
+ }
+
+ /*
+ * process other top-level elements
+ */
+ while (cur != NULL) {
+ if (IS_BLANK_NODE(cur)) {
+ cur = cur->next;
+ continue;
+ }
+ if (cur->type == XML_TEXT_NODE) {
+ if (cur->content != NULL) {
+ xsltTransformError(NULL, style, cur,
+ "misplaced text node: '%s'\n", cur->content);
+ }
+ if (style != NULL) style->errors++;
+ cur = cur->next;
+ continue;
+ }
+ if ((cur->type == XML_ELEMENT_NODE) && (cur->ns == NULL)) {
+ xsltGenericError(xsltGenericErrorContext,
+ "Found a top-level element %s with null namespace URI\n",
+ cur->name);
+ if (style != NULL) style->errors++;
+ cur = cur->next;
+ continue;
+ }
+ if ((cur->type == XML_ELEMENT_NODE) && (!(IS_XSLT_ELEM(cur)))) {
+ xsltTopLevelFunction function;
+
+ function = xsltExtModuleTopLevelLookup(cur->name,
+ cur->ns->href);
+ if (function != NULL)
+ function(style, cur);
+
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseStylesheetTop : found foreign element %s\n",
+ cur->name);
+#endif
+ cur = cur->next;
+ continue;
+ }
+ if (IS_XSLT_NAME(cur, "import")) {
+ xsltTransformError(NULL, style, cur,
+ "xsltParseStylesheetTop: ignoring misplaced import element\n");
+ if (style != NULL) style->errors++;
+ } else if (IS_XSLT_NAME(cur, "include")) {
+ if (xsltParseStylesheetInclude(style, cur) != 0)
+ if (style != NULL) style->errors++;
+ } else if (IS_XSLT_NAME(cur, "strip-space")) {
+ xsltParseStylesheetStripSpace(style, cur);
+ } else if (IS_XSLT_NAME(cur, "preserve-space")) {
+ xsltParseStylesheetPreserveSpace(style, cur);
+ } else if (IS_XSLT_NAME(cur, "output")) {
+ xsltParseStylesheetOutput(style, cur);
+ } else if (IS_XSLT_NAME(cur, "key")) {
+ xsltParseStylesheetKey(style, cur);
+ } else if (IS_XSLT_NAME(cur, "decimal-format")) {
+ xsltParseStylesheetDecimalFormat(style, cur);
+ } else if (IS_XSLT_NAME(cur, "attribute-set")) {
+ xsltParseStylesheetAttributeSet(style, cur);
+ } else if (IS_XSLT_NAME(cur, "variable")) {
+ xsltParseGlobalVariable(style, cur);
+ } else if (IS_XSLT_NAME(cur, "param")) {
+ xsltParseGlobalParam(style, cur);
+ } else if (IS_XSLT_NAME(cur, "template")) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ templates++;
+#endif
+ xsltParseStylesheetTemplate(style, cur);
+ } else if (IS_XSLT_NAME(cur, "namespace-alias")) {
+ xsltNamespaceAlias(style, cur);
+ } else {
+ if ((style != NULL) && (style->forwards_compatible == 0)) {
+ xsltTransformError(NULL, style, cur,
+ "xsltParseStylesheetTop: unknown %s element\n",
+ cur->name);
+ if (style != NULL) style->errors++;
+ }
+ }
+ cur = cur->next;
+ }
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "parsed %d templates\n", templates);
+#endif
+}
+
+#endif /* else of XSLT_REFACTORED */
+
+#ifdef XSLT_REFACTORED
+/**
+ * xsltParseSimplifiedStylesheetTree:
+ *
+ * @style: the stylesheet (TODO: Change this to the compiler context)
+ * @doc: the document containing the stylesheet.
+ * @node: the node where the stylesheet is rooted at
+ *
+ * Returns 0 in case of success, a positive result if an error occurred
+ * and -1 on API and internal errors.
+ */
+static int
+xsltParseSimplifiedStylesheetTree(xsltCompilerCtxtPtr cctxt,
+ xmlDocPtr doc,
+ xmlNodePtr node)
+{
+ xsltTemplatePtr templ;
+
+ if ((cctxt == NULL) || (node == NULL))
+ return(-1);
+
+ if (xsltParseAttrXSLTVersion(cctxt, node, 0) == XSLT_ELEMENT_CATEGORY_LRE)
+ {
+ /*
+ * TODO: Adjust report, since this might be an
+ * embedded stylesheet.
+ */
+ xsltTransformError(NULL, cctxt->style, node,
+ "The attribute 'xsl:version' is missing; cannot identify "
+ "this document as an XSLT stylesheet document.\n");
+ cctxt->style->errors++;
+ return(1);
+ }
+
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseSimplifiedStylesheetTree: document is stylesheet\n");
+#endif
+
+ /*
+ * Create and link the template
+ */
+ templ = xsltNewTemplate();
+ if (templ == NULL) {
+ return(-1);
+ }
+ templ->next = cctxt->style->templates;
+ cctxt->style->templates = templ;
+ templ->match = xmlStrdup(BAD_CAST "/");
+
+ /*
+ * Note that we push the document-node in this special case.
+ */
+ xsltCompilerNodePush(cctxt, (xmlNodePtr) doc);
+ /*
+ * In every case, we need to have
+ * the in-scope namespaces of the element, where the
+ * stylesheet is rooted at, regardless if it's an XSLT
+ * instruction or a literal result instruction (or if
+ * this is an embedded stylesheet).
+ */
+ cctxt->inode->inScopeNs =
+ xsltCompilerBuildInScopeNsList(cctxt, node);
+ /*
+ * Parse the content and register the match-pattern.
+ */
+ xsltParseSequenceConstructor(cctxt, node);
+ xsltCompilerNodePop(cctxt, (xmlNodePtr) doc);
+
+ templ->elem = (xmlNodePtr) doc;
+ templ->content = node;
+ xsltAddTemplate(cctxt->style, templ, NULL, NULL);
+ cctxt->style->literal_result = 1;
+ return(0);
+}
+
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+/**
+ * xsltRestoreDocumentNamespaces:
+ * @ns: map of namespaces
+ * @doc: the document
+ *
+ * Restore the namespaces for the document
+ *
+ * Returns 0 in case of success, -1 in case of failure
+ */
+int
+xsltRestoreDocumentNamespaces(xsltNsMapPtr ns, xmlDocPtr doc)
+{
+ if (doc == NULL)
+ return(-1);
+ /*
+ * Revert the changes we have applied to the namespace-URIs of
+ * ns-decls.
+ */
+ while (ns != NULL) {
+ if ((ns->doc == doc) && (ns->ns != NULL)) {
+ ns->ns->href = ns->origNsName;
+ ns->origNsName = NULL;
+ ns->ns = NULL;
+ }
+ ns = ns->next;
+ }
+ return(0);
+}
+#endif /* XSLT_REFACTORED_XSLT_NSCOMP */
+
+/**
+ * xsltParseStylesheetProcess:
+ * @style: the XSLT stylesheet (the current stylesheet-level)
+ * @doc: and xmlDoc parsed XML
+ *
+ * Parses an XSLT stylesheet, adding the associated structures.
+ * Called by:
+ * xsltParseStylesheetImportedDoc() (xslt.c)
+ * xsltParseStylesheetInclude() (imports.c)
+ *
+ * Returns the value of the @style parameter if everything
+ * went right, NULL if something went amiss.
+ */
+xsltStylesheetPtr
+xsltParseStylesheetProcess(xsltStylesheetPtr style, xmlDocPtr doc)
+{
+ xsltCompilerCtxtPtr cctxt;
+ xmlNodePtr cur;
+ int oldIsSimplifiedStylesheet;
+
+ xsltInitGlobals();
+
+ if ((style == NULL) || (doc == NULL))
+ return(NULL);
+
+ cctxt = XSLT_CCTXT(style);
+
+ cur = xmlDocGetRootElement(doc);
+ if (cur == NULL) {
+ xsltTransformError(NULL, style, (xmlNodePtr) doc,
+ "xsltParseStylesheetProcess : empty stylesheet\n");
+ return(NULL);
+ }
+ oldIsSimplifiedStylesheet = cctxt->simplified;
+
+ if ((IS_XSLT_ELEM(cur)) &&
+ ((IS_XSLT_NAME(cur, "stylesheet")) ||
+ (IS_XSLT_NAME(cur, "transform")))) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseStylesheetProcess : found stylesheet\n");
+#endif
+ cctxt->simplified = 0;
+ style->literal_result = 0;
+ } else {
+ cctxt->simplified = 1;
+ style->literal_result = 1;
+ }
+ /*
+ * Pre-process the stylesheet if not already done before.
+ * This will remove PIs and comments, merge adjacent
+ * text nodes, internalize strings, etc.
+ */
+ if (! style->nopreproc)
+ xsltParsePreprocessStylesheetTree(cctxt, cur);
+ /*
+ * Parse and compile the stylesheet.
+ */
+ if (style->literal_result == 0) {
+ if (xsltParseXSLTStylesheetElem(cctxt, cur) != 0)
+ return(NULL);
+ } else {
+ if (xsltParseSimplifiedStylesheetTree(cctxt, doc, cur) != 0)
+ return(NULL);
+ }
+
+ cctxt->simplified = oldIsSimplifiedStylesheet;
+
+ return(style);
+}
+
+#else /* XSLT_REFACTORED */
+
+/**
+ * xsltParseStylesheetProcess:
+ * @ret: the XSLT stylesheet (the current stylesheet-level)
+ * @doc: and xmlDoc parsed XML
+ *
+ * Parses an XSLT stylesheet, adding the associated structures.
+ * Called by:
+ * xsltParseStylesheetImportedDoc() (xslt.c)
+ * xsltParseStylesheetInclude() (imports.c)
+ *
+ * Returns the value of the @style parameter if everything
+ * went right, NULL if something went amiss.
+ */
+xsltStylesheetPtr
+xsltParseStylesheetProcess(xsltStylesheetPtr ret, xmlDocPtr doc) {
+ xmlNodePtr cur;
+
+ xsltInitGlobals();
+
+ if (doc == NULL)
+ return(NULL);
+ if (ret == NULL)
+ return(ret);
+
+ /*
+ * First steps, remove blank nodes,
+ * locate the xsl:stylesheet element and the
+ * namespace declaration.
+ */
+ cur = xmlDocGetRootElement(doc);
+ if (cur == NULL) {
+ xsltTransformError(NULL, ret, (xmlNodePtr) doc,
+ "xsltParseStylesheetProcess : empty stylesheet\n");
+ return(NULL);
+ }
+
+ if ((IS_XSLT_ELEM(cur)) &&
+ ((IS_XSLT_NAME(cur, "stylesheet")) ||
+ (IS_XSLT_NAME(cur, "transform")))) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseStylesheetProcess : found stylesheet\n");
+#endif
+ ret->literal_result = 0;
+ xsltParseStylesheetExcludePrefix(ret, cur, 1);
+ xsltParseStylesheetExtPrefix(ret, cur, 1);
+ } else {
+ xsltParseStylesheetExcludePrefix(ret, cur, 0);
+ xsltParseStylesheetExtPrefix(ret, cur, 0);
+ ret->literal_result = 1;
+ }
+ if (!ret->nopreproc) {
+ xsltPreprocessStylesheet(ret, cur);
+ }
+ if (ret->literal_result == 0) {
+ xsltParseStylesheetTop(ret, cur);
+ } else {
+ xmlChar *prop;
+ xsltTemplatePtr template;
+
+ /*
+ * the document itself might be the template, check xsl:version
+ */
+ prop = xmlGetNsProp(cur, (const xmlChar *)"version", XSLT_NAMESPACE);
+ if (prop == NULL) {
+ xsltTransformError(NULL, ret, cur,
+ "xsltParseStylesheetProcess : document is not a stylesheet\n");
+ return(NULL);
+ }
+
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseStylesheetProcess : document is stylesheet\n");
+#endif
+
+ if ((!xmlStrEqual(prop, (const xmlChar *)"1.0")) &&
+ (!xmlStrEqual(prop, (const xmlChar *)"1.1"))) {
+ xsltTransformError(NULL, ret, cur,
+ "xsl:version: only 1.1 features are supported\n");
+ ret->forwards_compatible = 1;
+ ret->warnings++;
+ }
+ xmlFree(prop);
+
+ /*
+ * Create and link the template
+ */
+ template = xsltNewTemplate();
+ if (template == NULL) {
+ return(NULL);
+ }
+ template->next = ret->templates;
+ ret->templates = template;
+ template->match = xmlStrdup((const xmlChar *)"/");
+
+ /*
+ * parse the content and register the pattern
+ */
+ xsltParseTemplateContent(ret, (xmlNodePtr) doc);
+ template->elem = (xmlNodePtr) doc;
+ template->content = doc->children;
+ xsltAddTemplate(ret, template, NULL, NULL);
+ ret->literal_result = 1;
+ }
+
+ return(ret);
+}
+
+#endif /* else of XSLT_REFACTORED */
+
+/**
+ * xsltParseStylesheetImportedDoc:
+ * @doc: an xmlDoc parsed XML
+ * @parentStyle: pointer to the parent stylesheet (if it exists)
+ *
+ * parse an XSLT stylesheet building the associated structures
+ * except the processing not needed for imported documents.
+ *
+ * Returns a new XSLT stylesheet structure.
+ */
+
+xsltStylesheetPtr
+xsltParseStylesheetImportedDoc(xmlDocPtr doc,
+ xsltStylesheetPtr parentStyle) {
+ xsltStylesheetPtr retStyle;
+
+ if (doc == NULL)
+ return(NULL);
+
+ retStyle = xsltNewStylesheet();
+ if (retStyle == NULL)
+ return(NULL);
+ /*
+ * Set the importing stylesheet module; also used to detect recursion.
+ */
+ retStyle->parent = parentStyle;
+ /*
+ * Adjust the string dict.
+ */
+ if (doc->dict != NULL) {
+ xmlDictFree(retStyle->dict);
+ retStyle->dict = doc->dict;
+#ifdef WITH_XSLT_DEBUG
+ xsltGenericDebug(xsltGenericDebugContext,
+ "reusing dictionary from %s for stylesheet\n",
+ doc->URL);
+#endif
+ xmlDictReference(retStyle->dict);
+ }
+
+ /*
+ * TODO: Eliminate xsltGatherNamespaces(); we must not restrict
+ * the stylesheet to containt distinct namespace prefixes.
+ */
+ xsltGatherNamespaces(retStyle);
+
+#ifdef XSLT_REFACTORED
+ {
+ xsltCompilerCtxtPtr cctxt;
+ xsltStylesheetPtr oldCurSheet;
+
+ if (parentStyle == NULL) {
+ xsltPrincipalStylesheetDataPtr principalData;
+ /*
+ * Principal stylesheet
+ * --------------------
+ */
+ retStyle->principal = retStyle;
+ /*
+ * Create extra data for the principal stylesheet.
+ */
+ principalData = xsltNewPrincipalStylesheetData();
+ if (principalData == NULL) {
+ xsltFreeStylesheet(retStyle);
+ return(NULL);
+ }
+ retStyle->principalData = principalData;
+ /*
+ * Create the compilation context
+ * ------------------------------
+ * (only once; for the principal stylesheet).
+ * This is currently the only function where the
+ * compilation context is created.
+ */
+ cctxt = xsltCompilationCtxtCreate(retStyle);
+ if (cctxt == NULL) {
+ xsltFreeStylesheet(retStyle);
+ return(NULL);
+ }
+ retStyle->compCtxt = (void *) cctxt;
+ cctxt->style = retStyle;
+ cctxt->dict = retStyle->dict;
+ cctxt->psData = principalData;
+ /*
+ * Push initial dummy node info.
+ */
+ cctxt->depth = -1;
+ xsltCompilerNodePush(cctxt, (xmlNodePtr) doc);
+ } else {
+ /*
+ * Imported stylesheet.
+ */
+ retStyle->principal = parentStyle->principal;
+ cctxt = parentStyle->compCtxt;
+ retStyle->compCtxt = cctxt;
+ }
+ /*
+ * Save the old and set the current stylesheet structure in the
+ * compilation context.
+ */
+ oldCurSheet = cctxt->style;
+ cctxt->style = retStyle;
+
+ retStyle->doc = doc;
+ xsltParseStylesheetProcess(retStyle, doc);
+
+ cctxt->style = oldCurSheet;
+ if (parentStyle == NULL) {
+ /*
+ * Pop the initial dummy node info.
+ */
+ xsltCompilerNodePop(cctxt, (xmlNodePtr) doc);
+ } else {
+ /*
+ * Clear the compilation context of imported
+ * stylesheets.
+ * TODO: really?
+ */
+ /* retStyle->compCtxt = NULL; */
+ }
+ /*
+ * Free the stylesheet if there were errors.
+ */
+ if (retStyle != NULL) {
+ if (retStyle->errors != 0) {
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+ /*
+ * Restore all changes made to namespace URIs of ns-decls.
+ */
+ if (cctxt->psData->nsMap)
+ xsltRestoreDocumentNamespaces(cctxt->psData->nsMap, doc);
+#endif
+ /*
+ * Detach the doc from the stylesheet; otherwise the doc
+ * will be freed in xsltFreeStylesheet().
+ */
+ retStyle->doc = NULL;
+ /*
+ * Cleanup the doc if its the main stylesheet.
+ */
+ if (parentStyle == NULL) {
+ xsltCleanupStylesheetTree(doc, xmlDocGetRootElement(doc));
+ if (retStyle->compCtxt != NULL) {
+ xsltCompilationCtxtFree(retStyle->compCtxt);
+ retStyle->compCtxt = NULL;
+ }
+ }
+
+ xsltFreeStylesheet(retStyle);
+ retStyle = NULL;
+ }
+ }
+ }
+
+#else /* XSLT_REFACTORED */
+ /*
+ * Old behaviour.
+ */
+ retStyle->doc = doc;
+ if (xsltParseStylesheetProcess(retStyle, doc) == NULL) {
+ retStyle->doc = NULL;
+ xsltFreeStylesheet(retStyle);
+ retStyle = NULL;
+ }
+ if (retStyle != NULL) {
+ if (retStyle->errors != 0) {
+ retStyle->doc = NULL;
+ if (parentStyle == NULL)
+ xsltCleanupStylesheetTree(doc,
+ xmlDocGetRootElement(doc));
+ xsltFreeStylesheet(retStyle);
+ retStyle = NULL;
+ }
+ }
+#endif /* else of XSLT_REFACTORED */
+
+ return(retStyle);
+}
+
+/**
+ * xsltParseStylesheetDoc:
+ * @doc: and xmlDoc parsed XML
+ *
+ * parse an XSLT stylesheet, building the associated structures. doc
+ * is kept as a reference within the returned stylesheet, so changes
+ * to doc after the parsing will be reflected when the stylesheet
+ * is applied, and the doc is automatically freed when the
+ * stylesheet is closed.
+ *
+ * Returns a new XSLT stylesheet structure.
+ */
+
+xsltStylesheetPtr
+xsltParseStylesheetDoc(xmlDocPtr doc) {
+ xsltStylesheetPtr ret;
+
+ xsltInitGlobals();
+
+ ret = xsltParseStylesheetImportedDoc(doc, NULL);
+ if (ret == NULL)
+ return(NULL);
+
+ xsltResolveStylesheetAttributeSet(ret);
+#ifdef XSLT_REFACTORED
+ /*
+ * Free the compilation context.
+ * TODO: Check if it's better to move this cleanup to
+ * xsltParseStylesheetImportedDoc().
+ */
+ if (ret->compCtxt != NULL) {
+ xsltCompilationCtxtFree(XSLT_CCTXT(ret));
+ ret->compCtxt = NULL;
+ }
+#endif
+ return(ret);
+}
+
+/**
+ * xsltParseStylesheetFile:
+ * @filename: the filename/URL to the stylesheet
+ *
+ * Load and parse an XSLT stylesheet
+ *
+ * Returns a new XSLT stylesheet structure.
+ */
+
+xsltStylesheetPtr
+xsltParseStylesheetFile(const xmlChar* filename) {
+ xsltSecurityPrefsPtr sec;
+ xsltStylesheetPtr ret;
+ xmlDocPtr doc;
+
+ xsltInitGlobals();
+
+ if (filename == NULL)
+ return(NULL);
+
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltParseStylesheetFile : parse %s\n", filename);
+#endif
+
+ /*
+ * Security framework check
+ */
+ sec = xsltGetDefaultSecurityPrefs();
+ if (sec != NULL) {
+ int res;
+
+ res = xsltCheckRead(sec, NULL, filename);
+ if (res == 0) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltParseStylesheetFile: read rights for %s denied\n",
+ filename);
+ return(NULL);
+ }
+ }
+
+ doc = xsltDocDefaultLoader(filename, NULL, XSLT_PARSE_OPTIONS,
+ NULL, XSLT_LOAD_START);
+ if (doc == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltParseStylesheetFile : cannot parse %s\n", filename);
+ return(NULL);
+ }
+ ret = xsltParseStylesheetDoc(doc);
+ if (ret == NULL) {
+ xmlFreeDoc(doc);
+ return(NULL);
+ }
+
+ return(ret);
+}
+
+/************************************************************************
+ * *
+ * Handling of Stylesheet PI *
+ * *
+ ************************************************************************/
+
+#define CUR (*cur)
+#define SKIP(val) cur += (val)
+#define NXT(val) cur[(val)]
+#define SKIP_BLANKS \
+ while (IS_BLANK(CUR)) NEXT
+#define NEXT ((*cur) ? cur++ : cur)
+
+/**
+ * xsltParseStylesheetPI:
+ * @value: the value of the PI
+ *
+ * This function checks that the type is text/xml and extracts
+ * the URI-Reference for the stylesheet
+ *
+ * Returns the URI-Reference for the stylesheet or NULL (it need to
+ * be freed by the caller)
+ */
+static xmlChar *
+xsltParseStylesheetPI(const xmlChar *value) {
+ const xmlChar *cur;
+ const xmlChar *start;
+ xmlChar *val;
+ xmlChar tmp;
+ xmlChar *href = NULL;
+ int isXml = 0;
+
+ if (value == NULL)
+ return(NULL);
+
+ cur = value;
+ while (CUR != 0) {
+ SKIP_BLANKS;
+ if ((CUR == 't') && (NXT(1) == 'y') && (NXT(2) == 'p') &&
+ (NXT(3) == 'e')) {
+ SKIP(4);
+ SKIP_BLANKS;
+ if (CUR != '=')
+ continue;
+ NEXT;
+ if ((CUR != '\'') && (CUR != '"'))
+ continue;
+ tmp = CUR;
+ NEXT;
+ start = cur;
+ while ((CUR != 0) && (CUR != tmp))
+ NEXT;
+ if (CUR != tmp)
+ continue;
+ val = xmlStrndup(start, cur - start);
+ NEXT;
+ if (val == NULL)
+ return(NULL);
+ if ((xmlStrcasecmp(val, BAD_CAST "text/xml")) &&
+ (xmlStrcasecmp(val, BAD_CAST "text/xsl"))) {
+ xmlFree(val);
+ break;
+ }
+ isXml = 1;
+ xmlFree(val);
+ } else if ((CUR == 'h') && (NXT(1) == 'r') && (NXT(2) == 'e') &&
+ (NXT(3) == 'f')) {
+ SKIP(4);
+ SKIP_BLANKS;
+ if (CUR != '=')
+ continue;
+ NEXT;
+ if ((CUR != '\'') && (CUR != '"'))
+ continue;
+ tmp = CUR;
+ NEXT;
+ start = cur;
+ while ((CUR != 0) && (CUR != tmp))
+ NEXT;
+ if (CUR != tmp)
+ continue;
+ if (href == NULL)
+ href = xmlStrndup(start, cur - start);
+ NEXT;
+ } else {
+ while ((CUR != 0) && (!IS_BLANK(CUR)))
+ NEXT;
+ }
+
+ }
+
+ if (!isXml) {
+ if (href != NULL)
+ xmlFree(href);
+ href = NULL;
+ }
+ return(href);
+}
+
+/**
+ * xsltLoadStylesheetPI:
+ * @doc: a document to process
+ *
+ * This function tries to locate the stylesheet PI in the given document
+ * If found, and if contained within the document, it will extract
+ * that subtree to build the stylesheet to process @doc (doc itself will
+ * be modified). If found but referencing an external document it will
+ * attempt to load it and generate a stylesheet from it. In both cases,
+ * the resulting stylesheet and the document need to be freed once the
+ * transformation is done.
+ *
+ * Returns a new XSLT stylesheet structure or NULL if not found.
+ */
+xsltStylesheetPtr
+xsltLoadStylesheetPI(xmlDocPtr doc) {
+ xmlNodePtr child;
+ xsltStylesheetPtr ret = NULL;
+ xmlChar *href = NULL;
+ xmlURIPtr URI;
+
+ xsltInitGlobals();
+
+ if (doc == NULL)
+ return(NULL);
+
+ /*
+ * Find the text/xml stylesheet PI id any before the root
+ */
+ child = doc->children;
+ while ((child != NULL) && (child->type != XML_ELEMENT_NODE)) {
+ if ((child->type == XML_PI_NODE) &&
+ (xmlStrEqual(child->name, BAD_CAST "xml-stylesheet"))) {
+ href = xsltParseStylesheetPI(child->content);
+ if (href != NULL)
+ break;
+ }
+ child = child->next;
+ }
+
+ /*
+ * If found check the href to select processing
+ */
+ if (href != NULL) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltLoadStylesheetPI : found PI href=%s\n", href);
+#endif
+ URI = xmlParseURI((const char *) href);
+ if (URI == NULL) {
+ xsltTransformError(NULL, NULL, child,
+ "xml-stylesheet : href %s is not valid\n", href);
+ xmlFree(href);
+ return(NULL);
+ }
+ if ((URI->fragment != NULL) && (URI->scheme == NULL) &&
+ (URI->opaque == NULL) && (URI->authority == NULL) &&
+ (URI->server == NULL) && (URI->user == NULL) &&
+ (URI->path == NULL) && (URI->query == NULL)) {
+ xmlAttrPtr ID;
+
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltLoadStylesheetPI : Reference to ID %s\n", href);
+#endif
+ if (URI->fragment[0] == '#')
+ ID = xmlGetID(doc, (const xmlChar *) &(URI->fragment[1]));
+ else
+ ID = xmlGetID(doc, (const xmlChar *) URI->fragment);
+ if (ID == NULL) {
+ xsltTransformError(NULL, NULL, child,
+ "xml-stylesheet : no ID %s found\n", URI->fragment);
+ } else {
+ xmlDocPtr fake;
+ xmlNodePtr subtree, newtree;
+ xmlNsPtr ns;
+
+#ifdef WITH_XSLT_DEBUG
+ xsltGenericDebug(xsltGenericDebugContext,
+ "creating new document from %s for embedded stylesheet\n",
+ doc->URL);
+#endif
+ /*
+ * move the subtree in a new document passed to
+ * the stylesheet analyzer
+ */
+ subtree = ID->parent;
+ fake = xmlNewDoc(NULL);
+ if (fake != NULL) {
+ /*
+ * Should the dictionary still be shared even though
+ * the nodes are being copied rather than moved?
+ */
+ fake->dict = doc->dict;
+ xmlDictReference(doc->dict);
+#ifdef WITH_XSLT_DEBUG
+ xsltGenericDebug(xsltGenericDebugContext,
+ "reusing dictionary from %s for embedded stylesheet\n",
+ doc->URL);
+#endif
+
+ newtree = xmlDocCopyNode(subtree, fake, 1);
+
+ fake->URL = xmlNodeGetBase(doc, subtree->parent);
+#ifdef WITH_XSLT_DEBUG
+ xsltGenericDebug(xsltGenericDebugContext,
+ "set base URI for embedded stylesheet as %s\n",
+ fake->URL);
+#endif
+
+ /*
+ * Add all namespaces in scope of embedded stylesheet to
+ * root element of newly created stylesheet document
+ */
+ while ((subtree = subtree->parent) != (xmlNodePtr)doc) {
+ for (ns = subtree->ns; ns; ns = ns->next) {
+ xmlNewNs(newtree, ns->href, ns->prefix);
+ }
+ }
+
+ xmlAddChild((xmlNodePtr)fake, newtree);
+ ret = xsltParseStylesheetDoc(fake);
+ if (ret == NULL)
+ xmlFreeDoc(fake);
+ }
+ }
+ } else {
+ xmlChar *URL, *base;
+
+ /*
+ * Reference to an external stylesheet
+ */
+
+ base = xmlNodeGetBase(doc, (xmlNodePtr) doc);
+ URL = xmlBuildURI(href, base);
+ if (URL != NULL) {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltLoadStylesheetPI : fetching %s\n", URL);
+#endif
+ ret = xsltParseStylesheetFile(URL);
+ xmlFree(URL);
+ } else {
+#ifdef WITH_XSLT_DEBUG_PARSING
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltLoadStylesheetPI : fetching %s\n", href);
+#endif
+ ret = xsltParseStylesheetFile(href);
+ }
+ if (base != NULL)
+ xmlFree(base);
+ }
+ xmlFreeURI(URI);
+ xmlFree(href);
+ }
+ return(ret);
+}
diff --git a/libxslt/xslt.h b/libxslt/xslt.h
new file mode 100644
index 0000000..02f491a
--- /dev/null
+++ b/libxslt/xslt.h
@@ -0,0 +1,110 @@
+/*
+ * Summary: Interfaces, constants and types related to the XSLT engine
+ * Description: Interfaces, constants and types related to the XSLT engine
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_H__
+#define __XML_XSLT_H__
+
+#include <libxml/tree.h>
+#include "xsltexports.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * XSLT_DEFAULT_VERSION:
+ *
+ * The default version of XSLT supported.
+ */
+#define XSLT_DEFAULT_VERSION "1.0"
+
+/**
+ * XSLT_DEFAULT_VENDOR:
+ *
+ * The XSLT "vendor" string for this processor.
+ */
+#define XSLT_DEFAULT_VENDOR "libxslt"
+
+/**
+ * XSLT_DEFAULT_URL:
+ *
+ * The XSLT "vendor" URL for this processor.
+ */
+#define XSLT_DEFAULT_URL "http://xmlsoft.org/XSLT/"
+
+/**
+ * XSLT_NAMESPACE:
+ *
+ * The XSLT specification namespace.
+ */
+#define XSLT_NAMESPACE ((const xmlChar *)"http://www.w3.org/1999/XSL/Transform")
+
+/**
+ * XSLT_PARSE_OPTIONS:
+ *
+ * The set of options to pass to an xmlReadxxx when loading files for
+ * XSLT consumption.
+ */
+#define XSLT_PARSE_OPTIONS \
+ XML_PARSE_NOENT | XML_PARSE_DTDLOAD | XML_PARSE_DTDATTR | XML_PARSE_NOCDATA
+
+/**
+ * xsltMaxDepth:
+ *
+ * This value is used to detect templates loops.
+ */
+XSLTPUBVAR int xsltMaxDepth;
+
+/**
+ * * xsltMaxVars:
+ * *
+ * * This value is used to detect templates loops.
+ * */
+XSLTPUBVAR int xsltMaxVars;
+
+/**
+ * xsltEngineVersion:
+ *
+ * The version string for libxslt.
+ */
+XSLTPUBVAR const char *xsltEngineVersion;
+
+/**
+ * xsltLibxsltVersion:
+ *
+ * The version of libxslt compiled.
+ */
+XSLTPUBVAR const int xsltLibxsltVersion;
+
+/**
+ * xsltLibxmlVersion:
+ *
+ * The version of libxml libxslt was compiled against.
+ */
+XSLTPUBVAR const int xsltLibxmlVersion;
+
+/*
+ * Global initialization function.
+ */
+
+XSLTPUBFUN void XSLTCALL
+ xsltInit (void);
+
+/*
+ * Global cleanup function.
+ */
+XSLTPUBFUN void XSLTCALL
+ xsltCleanupGlobals (void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_H__ */
+
diff --git a/libxslt/xsltInternals.h b/libxslt/xsltInternals.h
new file mode 100644
index 0000000..060b178
--- /dev/null
+++ b/libxslt/xsltInternals.h
@@ -0,0 +1,1977 @@
+/*
+ * Summary: internal data structures, constants and functions
+ * Description: Internal data structures, constants and functions used
+ * by the XSLT engine.
+ * They are not part of the API or ABI, i.e. they can change
+ * without prior notice, use carefully.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_INTERNALS_H__
+#define __XML_XSLT_INTERNALS_H__
+
+#include <libxml/tree.h>
+#include <libxml/hash.h>
+#include <libxml/xpath.h>
+#include <libxml/xmlerror.h>
+#include <libxml/dict.h>
+#include <libxml/xmlstring.h>
+#include <libxslt/xslt.h>
+#include "xsltexports.h"
+#include "xsltlocale.h"
+#include "numbersInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* #define XSLT_DEBUG_PROFILE_CACHE */
+
+/**
+ * XSLT_IS_TEXT_NODE:
+ *
+ * check if the argument is a text node
+ */
+#define XSLT_IS_TEXT_NODE(n) ((n != NULL) && \
+ (((n)->type == XML_TEXT_NODE) || \
+ ((n)->type == XML_CDATA_SECTION_NODE)))
+
+
+/**
+ * XSLT_MARK_RES_TREE_FRAG:
+ *
+ * internal macro to set up tree fragments
+ */
+#define XSLT_MARK_RES_TREE_FRAG(n) \
+ (n)->name = (char *) xmlStrdup(BAD_CAST " fake node libxslt");
+
+/**
+ * XSLT_IS_RES_TREE_FRAG:
+ *
+ * internal macro to test tree fragments
+ */
+#define XSLT_IS_RES_TREE_FRAG(n) \
+ ((n != NULL) && ((n)->type == XML_DOCUMENT_NODE) && \
+ ((n)->name != NULL) && ((n)->name[0] == ' '))
+
+/**
+ * XSLT_REFACTORED_KEYCOMP:
+ *
+ * Internal define to enable on-demand xsl:key computation.
+ * That's the only mode now but the define is kept for compatibility
+ */
+#define XSLT_REFACTORED_KEYCOMP
+
+/**
+ * XSLT_FAST_IF:
+ *
+ * Internal define to enable usage of xmlXPathCompiledEvalToBoolean()
+ * for XSLT "tests"; e.g. in <xsl:if test="/foo/bar">
+ */
+#define XSLT_FAST_IF
+
+/**
+ * XSLT_REFACTORED:
+ *
+ * Internal define to enable the refactored parts of Libxslt.
+ */
+/* #define XSLT_REFACTORED */
+/* ==================================================================== */
+
+/**
+ * XSLT_REFACTORED_VARS:
+ *
+ * Internal define to enable the refactored variable part of libxslt
+ */
+#define XSLT_REFACTORED_VARS
+
+#ifdef XSLT_REFACTORED
+
+extern const xmlChar *xsltXSLTAttrMarker;
+
+
+/* TODO: REMOVE: #define XSLT_REFACTORED_EXCLRESNS */
+
+/* TODO: REMOVE: #define XSLT_REFACTORED_NSALIAS */
+
+/**
+ * XSLT_REFACTORED_XSLT_NSCOMP
+ *
+ * Internal define to enable the pointer-comparison of
+ * namespaces of XSLT elements.
+ */
+/* #define XSLT_REFACTORED_XSLT_NSCOMP */
+
+/**
+ * XSLT_REFACTORED_XPATHCOMP:
+ *
+ * Internal define to enable the optimization of the
+ * compilation of XPath expressions.
+ */
+#define XSLT_REFACTORED_XPATHCOMP
+
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+
+extern const xmlChar *xsltConstNamespaceNameXSLT;
+
+/**
+ * IS_XSLT_ELEM_FAST:
+ *
+ * quick test to detect XSLT elements
+ */
+#define IS_XSLT_ELEM_FAST(n) \
+ (((n) != NULL) && ((n)->ns != NULL) && \
+ ((n)->ns->href == xsltConstNamespaceNameXSLT))
+
+/**
+ * IS_XSLT_ATTR_FAST:
+ *
+ * quick test to detect XSLT attributes
+ */
+#define IS_XSLT_ATTR_FAST(a) \
+ (((a) != NULL) && ((a)->ns != NULL) && \
+ ((a)->ns->href == xsltConstNamespaceNameXSLT))
+
+/**
+ * XSLT_HAS_INTERNAL_NSMAP:
+ *
+ * check for namespace mapping
+ */
+#define XSLT_HAS_INTERNAL_NSMAP(s) \
+ (((s) != NULL) && ((s)->principal) && \
+ ((s)->principal->principalData) && \
+ ((s)->principal->principalData->nsMap))
+
+/**
+ * XSLT_GET_INTERNAL_NSMAP:
+ *
+ * get pointer to namespace map
+ */
+#define XSLT_GET_INTERNAL_NSMAP(s) ((s)->principal->principalData->nsMap)
+
+#else /* XSLT_REFACTORED_XSLT_NSCOMP */
+
+/**
+ * IS_XSLT_ELEM_FAST:
+ *
+ * quick check whether this is an xslt element
+ */
+#define IS_XSLT_ELEM_FAST(n) \
+ (((n) != NULL) && ((n)->ns != NULL) && \
+ (xmlStrEqual((n)->ns->href, XSLT_NAMESPACE)))
+
+/**
+ * IS_XSLT_ATTR_FAST:
+ *
+ * quick check for xslt namespace attribute
+ */
+#define IS_XSLT_ATTR_FAST(a) \
+ (((a) != NULL) && ((a)->ns != NULL) && \
+ (xmlStrEqual((a)->ns->href, XSLT_NAMESPACE)))
+
+
+#endif /* XSLT_REFACTORED_XSLT_NSCOMP */
+
+
+/**
+ * XSLT_REFACTORED_MANDATORY_VERSION:
+ *
+ * TODO: Currently disabled to surpress regression test failures, since
+ * the old behaviour was that a missing version attribute
+ * produced a only a warning and not an error, which was incerrect.
+ * So the regression tests need to be fixed if this is enabled.
+ */
+/* #define XSLT_REFACTORED_MANDATORY_VERSION */
+
+/**
+ * xsltPointerList:
+ *
+ * Pointer-list for various purposes.
+ */
+typedef struct _xsltPointerList xsltPointerList;
+typedef xsltPointerList *xsltPointerListPtr;
+struct _xsltPointerList {
+ void **items;
+ int number;
+ int size;
+};
+
+#endif
+
+/**
+ * XSLT_REFACTORED_PARSING:
+ *
+ * Internal define to enable the refactored parts of Libxslt
+ * related to parsing.
+ */
+/* #define XSLT_REFACTORED_PARSING */
+
+/**
+ * XSLT_MAX_SORT:
+ *
+ * Max number of specified xsl:sort on an element.
+ */
+#define XSLT_MAX_SORT 15
+
+/**
+ * XSLT_PAT_NO_PRIORITY:
+ *
+ * Specific value for pattern without priority expressed.
+ */
+#define XSLT_PAT_NO_PRIORITY -12345789
+
+/**
+ * xsltRuntimeExtra:
+ *
+ * Extra information added to the transformation context.
+ */
+typedef struct _xsltRuntimeExtra xsltRuntimeExtra;
+typedef xsltRuntimeExtra *xsltRuntimeExtraPtr;
+struct _xsltRuntimeExtra {
+ void *info; /* pointer to the extra data */
+ xmlFreeFunc deallocate; /* pointer to the deallocation routine */
+ union { /* dual-purpose field */
+ void *ptr; /* data not needing deallocation */
+ int ival; /* integer value storage */
+ } val;
+};
+
+/**
+ * XSLT_RUNTIME_EXTRA_LST:
+ * @ctxt: the transformation context
+ * @nr: the index
+ *
+ * Macro used to access extra information stored in the context
+ */
+#define XSLT_RUNTIME_EXTRA_LST(ctxt, nr) (ctxt)->extras[(nr)].info
+/**
+ * XSLT_RUNTIME_EXTRA_FREE:
+ * @ctxt: the transformation context
+ * @nr: the index
+ *
+ * Macro used to free extra information stored in the context
+ */
+#define XSLT_RUNTIME_EXTRA_FREE(ctxt, nr) (ctxt)->extras[(nr)].deallocate
+/**
+ * XSLT_RUNTIME_EXTRA:
+ * @ctxt: the transformation context
+ * @nr: the index
+ *
+ * Macro used to define extra information stored in the context
+ */
+#define XSLT_RUNTIME_EXTRA(ctxt, nr, typ) (ctxt)->extras[(nr)].val.typ
+
+/**
+ * xsltTemplate:
+ *
+ * The in-memory structure corresponding to an XSLT Template.
+ */
+typedef struct _xsltTemplate xsltTemplate;
+typedef xsltTemplate *xsltTemplatePtr;
+struct _xsltTemplate {
+ struct _xsltTemplate *next;/* chained list sorted by priority */
+ struct _xsltStylesheet *style;/* the containing stylesheet */
+ xmlChar *match; /* the matching string */
+ float priority; /* as given from the stylesheet, not computed */
+ const xmlChar *name; /* the local part of the name QName */
+ const xmlChar *nameURI; /* the URI part of the name QName */
+ const xmlChar *mode;/* the local part of the mode QName */
+ const xmlChar *modeURI;/* the URI part of the mode QName */
+ xmlNodePtr content; /* the template replacement value */
+ xmlNodePtr elem; /* the source element */
+
+ /*
+ * TODO: @inheritedNsNr and @inheritedNs won't be used in the
+ * refactored code.
+ */
+ int inheritedNsNr; /* number of inherited namespaces */
+ xmlNsPtr *inheritedNs;/* inherited non-excluded namespaces */
+
+ /* Profiling informations */
+ int nbCalls; /* the number of time the template was called */
+ unsigned long time; /* the time spent in this template */
+ void *params; /* xsl:param instructions */
+
+ int templNr; /* Nb of templates in the stack */
+ int templMax; /* Size of the templtes stack */
+ xsltTemplatePtr *templCalledTab; /* templates called */
+ int *templCountTab; /* .. and how often */
+};
+
+/**
+ * xsltDecimalFormat:
+ *
+ * Data structure of decimal-format.
+ */
+typedef struct _xsltDecimalFormat xsltDecimalFormat;
+typedef xsltDecimalFormat *xsltDecimalFormatPtr;
+struct _xsltDecimalFormat {
+ struct _xsltDecimalFormat *next; /* chained list */
+ xmlChar *name;
+ /* Used for interpretation of pattern */
+ xmlChar *digit;
+ xmlChar *patternSeparator;
+ /* May appear in result */
+ xmlChar *minusSign;
+ xmlChar *infinity;
+ xmlChar *noNumber; /* Not-a-number */
+ /* Used for interpretation of pattern and may appear in result */
+ xmlChar *decimalPoint;
+ xmlChar *grouping;
+ xmlChar *percent;
+ xmlChar *permille;
+ xmlChar *zeroDigit;
+};
+
+/**
+ * xsltDocument:
+ *
+ * Data structure associated to a parsed document.
+ */
+typedef struct _xsltDocument xsltDocument;
+typedef xsltDocument *xsltDocumentPtr;
+struct _xsltDocument {
+ struct _xsltDocument *next; /* documents are kept in a chained list */
+ int main; /* is this the main document */
+ xmlDocPtr doc; /* the parsed document */
+ void *keys; /* key tables storage */
+ struct _xsltDocument *includes; /* subsidiary includes */
+ int preproc; /* pre-processing already done */
+ int nbKeysComputed;
+};
+
+/**
+ * xsltKeyDef:
+ *
+ * Representation of an xsl:key.
+ */
+typedef struct _xsltKeyDef xsltKeyDef;
+typedef xsltKeyDef *xsltKeyDefPtr;
+struct _xsltKeyDef {
+ struct _xsltKeyDef *next;
+ xmlNodePtr inst;
+ xmlChar *name;
+ xmlChar *nameURI;
+ xmlChar *match;
+ xmlChar *use;
+ xmlXPathCompExprPtr comp;
+ xmlXPathCompExprPtr usecomp;
+ xmlNsPtr *nsList; /* the namespaces in scope */
+ int nsNr; /* the number of namespaces in scope */
+};
+
+/**
+ * xsltKeyTable:
+ *
+ * Holds the computed keys for key definitions of the same QName.
+ * Is owned by an xsltDocument.
+ */
+typedef struct _xsltKeyTable xsltKeyTable;
+typedef xsltKeyTable *xsltKeyTablePtr;
+struct _xsltKeyTable {
+ struct _xsltKeyTable *next;
+ xmlChar *name;
+ xmlChar *nameURI;
+ xmlHashTablePtr keys;
+};
+
+/*
+ * The in-memory structure corresponding to an XSLT Stylesheet.
+ * NOTE: most of the content is simply linked from the doc tree
+ * structure, no specific allocation is made.
+ */
+typedef struct _xsltStylesheet xsltStylesheet;
+typedef xsltStylesheet *xsltStylesheetPtr;
+
+typedef struct _xsltTransformContext xsltTransformContext;
+typedef xsltTransformContext *xsltTransformContextPtr;
+
+/**
+ * xsltElemPreComp:
+ *
+ * The in-memory structure corresponding to element precomputed data,
+ * designed to be extended by extension implementors.
+ */
+typedef struct _xsltElemPreComp xsltElemPreComp;
+typedef xsltElemPreComp *xsltElemPreCompPtr;
+
+/**
+ * xsltTransformFunction:
+ * @ctxt: the XSLT transformation context
+ * @node: the input node
+ * @inst: the stylesheet node
+ * @comp: the compiled information from the stylesheet
+ *
+ * Signature of the function associated to elements part of the
+ * stylesheet language like xsl:if or xsl:apply-templates.
+ */
+typedef void (*xsltTransformFunction) (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst,
+ xsltElemPreCompPtr comp);
+
+/**
+ * xsltSortFunc:
+ * @ctxt: a transformation context
+ * @sorts: the node-set to sort
+ * @nbsorts: the number of sorts
+ *
+ * Signature of the function to use during sorting
+ */
+typedef void (*xsltSortFunc) (xsltTransformContextPtr ctxt, xmlNodePtr *sorts,
+ int nbsorts);
+
+typedef enum {
+ XSLT_FUNC_COPY=1,
+ XSLT_FUNC_SORT,
+ XSLT_FUNC_TEXT,
+ XSLT_FUNC_ELEMENT,
+ XSLT_FUNC_ATTRIBUTE,
+ XSLT_FUNC_COMMENT,
+ XSLT_FUNC_PI,
+ XSLT_FUNC_COPYOF,
+ XSLT_FUNC_VALUEOF,
+ XSLT_FUNC_NUMBER,
+ XSLT_FUNC_APPLYIMPORTS,
+ XSLT_FUNC_CALLTEMPLATE,
+ XSLT_FUNC_APPLYTEMPLATES,
+ XSLT_FUNC_CHOOSE,
+ XSLT_FUNC_IF,
+ XSLT_FUNC_FOREACH,
+ XSLT_FUNC_DOCUMENT,
+ XSLT_FUNC_WITHPARAM,
+ XSLT_FUNC_PARAM,
+ XSLT_FUNC_VARIABLE,
+ XSLT_FUNC_WHEN,
+ XSLT_FUNC_EXTENSION
+#ifdef XSLT_REFACTORED
+ ,
+ XSLT_FUNC_OTHERWISE,
+ XSLT_FUNC_FALLBACK,
+ XSLT_FUNC_MESSAGE,
+ XSLT_FUNC_INCLUDE,
+ XSLT_FUNC_ATTRSET,
+ XSLT_FUNC_LITERAL_RESULT_ELEMENT,
+ XSLT_FUNC_UNKOWN_FORWARDS_COMPAT
+#endif
+} xsltStyleType;
+
+/**
+ * xsltElemPreCompDeallocator:
+ * @comp: the #xsltElemPreComp to free up
+ *
+ * Deallocates an #xsltElemPreComp structure.
+ */
+typedef void (*xsltElemPreCompDeallocator) (xsltElemPreCompPtr comp);
+
+/**
+ * xsltElemPreComp:
+ *
+ * The basic structure for compiled items of the AST of the XSLT processor.
+ * This structure is also intended to be extended by extension implementors.
+ * TODO: This is somehow not nice, since it has a "free" field, which
+ * derived stylesheet-structs do not have.
+ */
+struct _xsltElemPreComp {
+ xsltElemPreCompPtr next; /* next item in the global chained
+ list hold by xsltStylesheet. */
+ xsltStyleType type; /* type of the element */
+ xsltTransformFunction func; /* handling function */
+ xmlNodePtr inst; /* the node in the stylesheet's tree
+ corresponding to this item */
+
+ /* end of common part */
+ xsltElemPreCompDeallocator free; /* the deallocator */
+};
+
+/**
+ * xsltStylePreComp:
+ *
+ * The abstract basic structure for items of the XSLT processor.
+ * This includes:
+ * 1) compiled forms of XSLT instructions (xsl:if, xsl:attribute, etc.)
+ * 2) compiled forms of literal result elements
+ * 3) compiled forms of extension elements
+ */
+typedef struct _xsltStylePreComp xsltStylePreComp;
+typedef xsltStylePreComp *xsltStylePreCompPtr;
+
+#ifdef XSLT_REFACTORED
+
+/*
+* Some pointer-list utility functions.
+*/
+XSLTPUBFUN xsltPointerListPtr XSLTCALL
+ xsltPointerListCreate (int initialSize);
+XSLTPUBFUN void XSLTCALL
+ xsltPointerListFree (xsltPointerListPtr list);
+XSLTPUBFUN void XSLTCALL
+ xsltPointerListClear (xsltPointerListPtr list);
+XSLTPUBFUN int XSLTCALL
+ xsltPointerListAddSize (xsltPointerListPtr list,
+ void *item,
+ int initialSize);
+
+/************************************************************************
+ * *
+ * Refactored structures *
+ * *
+ ************************************************************************/
+
+typedef struct _xsltNsListContainer xsltNsListContainer;
+typedef xsltNsListContainer *xsltNsListContainerPtr;
+struct _xsltNsListContainer {
+ xmlNsPtr *list;
+ int totalNumber;
+ int xpathNumber;
+};
+
+/**
+ * XSLT_ITEM_COMPATIBILITY_FIELDS:
+ *
+ * Fields for API compatibility to the structure
+ * _xsltElemPreComp which is used for extension functions.
+ * Note that @next is used for storage; it does not reflect a next
+ * sibling in the tree.
+ * TODO: Evaluate if we really need such a compatibility.
+ */
+#define XSLT_ITEM_COMPATIBILITY_FIELDS \
+ xsltElemPreCompPtr next;\
+ xsltStyleType type;\
+ xsltTransformFunction func;\
+ xmlNodePtr inst;
+
+/**
+ * XSLT_ITEM_NAVIGATION_FIELDS:
+ *
+ * Currently empty.
+ * TODO: It is intended to hold navigational fields in the future.
+ */
+#define XSLT_ITEM_NAVIGATION_FIELDS
+/*
+ xsltStylePreCompPtr parent;\
+ xsltStylePreCompPtr children;\
+ xsltStylePreCompPtr nextItem;
+*/
+
+/**
+ * XSLT_ITEM_NSINSCOPE_FIELDS:
+ *
+ * The in-scope namespaces.
+ */
+#define XSLT_ITEM_NSINSCOPE_FIELDS xsltNsListContainerPtr inScopeNs;
+
+/**
+ * XSLT_ITEM_COMMON_FIELDS:
+ *
+ * Common fields used for all items.
+ */
+#define XSLT_ITEM_COMMON_FIELDS \
+ XSLT_ITEM_COMPATIBILITY_FIELDS \
+ XSLT_ITEM_NAVIGATION_FIELDS \
+ XSLT_ITEM_NSINSCOPE_FIELDS
+
+/**
+ * _xsltStylePreComp:
+ *
+ * The abstract basic structure for items of the XSLT processor.
+ * This includes:
+ * 1) compiled forms of XSLT instructions (e.g. xsl:if, xsl:attribute, etc.)
+ * 2) compiled forms of literal result elements
+ * 3) various properties for XSLT instructions (e.g. xsl:when,
+ * xsl:with-param)
+ *
+ * REVISIT TODO: Keep this structure equal to the fields
+ * defined by XSLT_ITEM_COMMON_FIELDS
+ */
+struct _xsltStylePreComp {
+ xsltElemPreCompPtr next; /* next item in the global chained
+ list hold by xsltStylesheet */
+ xsltStyleType type; /* type of the item */
+ xsltTransformFunction func; /* handling function */
+ xmlNodePtr inst; /* the node in the stylesheet's tree
+ corresponding to this item. */
+ /* Currently no navigational fields. */
+ xsltNsListContainerPtr inScopeNs;
+};
+
+/**
+ * xsltStyleBasicEmptyItem:
+ *
+ * Abstract structure only used as a short-cut for
+ * XSLT items with no extra fields.
+ * NOTE that it is intended that this structure looks the same as
+ * _xsltStylePreComp.
+ */
+typedef struct _xsltStyleBasicEmptyItem xsltStyleBasicEmptyItem;
+typedef xsltStyleBasicEmptyItem *xsltStyleBasicEmptyItemPtr;
+
+struct _xsltStyleBasicEmptyItem {
+ XSLT_ITEM_COMMON_FIELDS
+};
+
+/**
+ * xsltStyleBasicExpressionItem:
+ *
+ * Abstract structure only used as a short-cut for
+ * XSLT items with just an expression.
+ */
+typedef struct _xsltStyleBasicExpressionItem xsltStyleBasicExpressionItem;
+typedef xsltStyleBasicExpressionItem *xsltStyleBasicExpressionItemPtr;
+
+struct _xsltStyleBasicExpressionItem {
+ XSLT_ITEM_COMMON_FIELDS
+
+ const xmlChar *select; /* TODO: Change this to "expression". */
+ xmlXPathCompExprPtr comp; /* TODO: Change this to compExpr. */
+};
+
+/************************************************************************
+ * *
+ * XSLT-instructions/declarations *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltStyleItemElement:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:element
+ * name = { qname }
+ * namespace = { uri-reference }
+ * use-attribute-sets = qnames>
+ * <!-- Content: template -->
+ * </xsl:element>
+ */
+typedef struct _xsltStyleItemElement xsltStyleItemElement;
+typedef xsltStyleItemElement *xsltStyleItemElementPtr;
+
+struct _xsltStyleItemElement {
+ XSLT_ITEM_COMMON_FIELDS
+
+ const xmlChar *use;
+ int has_use;
+ const xmlChar *name;
+ int has_name;
+ const xmlChar *ns;
+ const xmlChar *nsPrefix;
+ int has_ns;
+};
+
+/**
+ * xsltStyleItemAttribute:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:attribute
+ * name = { qname }
+ * namespace = { uri-reference }>
+ * <!-- Content: template -->
+ * </xsl:attribute>
+ */
+typedef struct _xsltStyleItemAttribute xsltStyleItemAttribute;
+typedef xsltStyleItemAttribute *xsltStyleItemAttributePtr;
+
+struct _xsltStyleItemAttribute {
+ XSLT_ITEM_COMMON_FIELDS
+ const xmlChar *name;
+ int has_name;
+ const xmlChar *ns;
+ const xmlChar *nsPrefix;
+ int has_ns;
+};
+
+/**
+ * xsltStyleItemText:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:text
+ * disable-output-escaping = "yes" | "no">
+ * <!-- Content: #PCDATA -->
+ * </xsl:text>
+ */
+typedef struct _xsltStyleItemText xsltStyleItemText;
+typedef xsltStyleItemText *xsltStyleItemTextPtr;
+
+struct _xsltStyleItemText {
+ XSLT_ITEM_COMMON_FIELDS
+ int noescape; /* text */
+};
+
+/**
+ * xsltStyleItemComment:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:comment>
+ * <!-- Content: template -->
+ * </xsl:comment>
+ */
+typedef xsltStyleBasicEmptyItem xsltStyleItemComment;
+typedef xsltStyleItemComment *xsltStyleItemCommentPtr;
+
+/**
+ * xsltStyleItemPI:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:processing-instruction
+ * name = { ncname }>
+ * <!-- Content: template -->
+ * </xsl:processing-instruction>
+ */
+typedef struct _xsltStyleItemPI xsltStyleItemPI;
+typedef xsltStyleItemPI *xsltStyleItemPIPtr;
+
+struct _xsltStyleItemPI {
+ XSLT_ITEM_COMMON_FIELDS
+ const xmlChar *name;
+ int has_name;
+};
+
+/**
+ * xsltStyleItemApplyImports:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:apply-imports />
+ */
+typedef xsltStyleBasicEmptyItem xsltStyleItemApplyImports;
+typedef xsltStyleItemApplyImports *xsltStyleItemApplyImportsPtr;
+
+/**
+ * xsltStyleItemApplyTemplates:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:apply-templates
+ * select = node-set-expression
+ * mode = qname>
+ * <!-- Content: (xsl:sort | xsl:with-param)* -->
+ * </xsl:apply-templates>
+ */
+typedef struct _xsltStyleItemApplyTemplates xsltStyleItemApplyTemplates;
+typedef xsltStyleItemApplyTemplates *xsltStyleItemApplyTemplatesPtr;
+
+struct _xsltStyleItemApplyTemplates {
+ XSLT_ITEM_COMMON_FIELDS
+
+ const xmlChar *mode; /* apply-templates */
+ const xmlChar *modeURI; /* apply-templates */
+ const xmlChar *select; /* sort, copy-of, value-of, apply-templates */
+ xmlXPathCompExprPtr comp; /* a precompiled XPath expression */
+ /* TODO: with-params */
+};
+
+/**
+ * xsltStyleItemCallTemplate:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:call-template
+ * name = qname>
+ * <!-- Content: xsl:with-param* -->
+ * </xsl:call-template>
+ */
+typedef struct _xsltStyleItemCallTemplate xsltStyleItemCallTemplate;
+typedef xsltStyleItemCallTemplate *xsltStyleItemCallTemplatePtr;
+
+struct _xsltStyleItemCallTemplate {
+ XSLT_ITEM_COMMON_FIELDS
+
+ xsltTemplatePtr templ; /* call-template */
+ const xmlChar *name; /* element, attribute, pi */
+ int has_name; /* element, attribute, pi */
+ const xmlChar *ns; /* element */
+ int has_ns; /* element */
+ /* TODO: with-params */
+};
+
+/**
+ * xsltStyleItemCopy:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:copy
+ * use-attribute-sets = qnames>
+ * <!-- Content: template -->
+ * </xsl:copy>
+ */
+typedef struct _xsltStyleItemCopy xsltStyleItemCopy;
+typedef xsltStyleItemCopy *xsltStyleItemCopyPtr;
+
+struct _xsltStyleItemCopy {
+ XSLT_ITEM_COMMON_FIELDS
+ const xmlChar *use; /* copy, element */
+ int has_use; /* copy, element */
+};
+
+/**
+ * xsltStyleItemIf:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:if
+ * test = boolean-expression>
+ * <!-- Content: template -->
+ * </xsl:if>
+ */
+typedef struct _xsltStyleItemIf xsltStyleItemIf;
+typedef xsltStyleItemIf *xsltStyleItemIfPtr;
+
+struct _xsltStyleItemIf {
+ XSLT_ITEM_COMMON_FIELDS
+
+ const xmlChar *test; /* if */
+ xmlXPathCompExprPtr comp; /* a precompiled XPath expression */
+};
+
+
+/**
+ * xsltStyleItemCopyOf:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:copy-of
+ * select = expression />
+ */
+typedef xsltStyleBasicExpressionItem xsltStyleItemCopyOf;
+typedef xsltStyleItemCopyOf *xsltStyleItemCopyOfPtr;
+
+/**
+ * xsltStyleItemValueOf:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:value-of
+ * select = string-expression
+ * disable-output-escaping = "yes" | "no" />
+ */
+typedef struct _xsltStyleItemValueOf xsltStyleItemValueOf;
+typedef xsltStyleItemValueOf *xsltStyleItemValueOfPtr;
+
+struct _xsltStyleItemValueOf {
+ XSLT_ITEM_COMMON_FIELDS
+
+ const xmlChar *select;
+ xmlXPathCompExprPtr comp; /* a precompiled XPath expression */
+ int noescape;
+};
+
+/**
+ * xsltStyleItemNumber:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:number
+ * level = "single" | "multiple" | "any"
+ * count = pattern
+ * from = pattern
+ * value = number-expression
+ * format = { string }
+ * lang = { nmtoken }
+ * letter-value = { "alphabetic" | "traditional" }
+ * grouping-separator = { char }
+ * grouping-size = { number } />
+ */
+typedef struct _xsltStyleItemNumber xsltStyleItemNumber;
+typedef xsltStyleItemNumber *xsltStyleItemNumberPtr;
+
+struct _xsltStyleItemNumber {
+ XSLT_ITEM_COMMON_FIELDS
+ xsltNumberData numdata; /* number */
+};
+
+/**
+ * xsltStyleItemChoose:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:choose>
+ * <!-- Content: (xsl:when+, xsl:otherwise?) -->
+ * </xsl:choose>
+ */
+typedef xsltStyleBasicEmptyItem xsltStyleItemChoose;
+typedef xsltStyleItemChoose *xsltStyleItemChoosePtr;
+
+/**
+ * xsltStyleItemFallback:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:fallback>
+ * <!-- Content: template -->
+ * </xsl:fallback>
+ */
+typedef xsltStyleBasicEmptyItem xsltStyleItemFallback;
+typedef xsltStyleItemFallback *xsltStyleItemFallbackPtr;
+
+/**
+ * xsltStyleItemForEach:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:for-each
+ * select = node-set-expression>
+ * <!-- Content: (xsl:sort*, template) -->
+ * </xsl:for-each>
+ */
+typedef xsltStyleBasicExpressionItem xsltStyleItemForEach;
+typedef xsltStyleItemForEach *xsltStyleItemForEachPtr;
+
+/**
+ * xsltStyleItemMessage:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:message
+ * terminate = "yes" | "no">
+ * <!-- Content: template -->
+ * </xsl:message>
+ */
+typedef struct _xsltStyleItemMessage xsltStyleItemMessage;
+typedef xsltStyleItemMessage *xsltStyleItemMessagePtr;
+
+struct _xsltStyleItemMessage {
+ XSLT_ITEM_COMMON_FIELDS
+ int terminate;
+};
+
+/**
+ * xsltStyleItemDocument:
+ *
+ * NOTE: This is not an instruction of XSLT 1.0.
+ */
+typedef struct _xsltStyleItemDocument xsltStyleItemDocument;
+typedef xsltStyleItemDocument *xsltStyleItemDocumentPtr;
+
+struct _xsltStyleItemDocument {
+ XSLT_ITEM_COMMON_FIELDS
+ int ver11; /* assigned: in xsltDocumentComp;
+ read: nowhere;
+ TODO: Check if we need. */
+ const xmlChar *filename; /* document URL */
+ int has_filename;
+};
+
+/************************************************************************
+ * *
+ * Non-instructions (actually properties of instructions/declarations) *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltStyleBasicItemVariable:
+ *
+ * Basic struct for xsl:variable, xsl:param and xsl:with-param.
+ * It's currently important to have equal fields, since
+ * xsltParseStylesheetCallerParam() is used with xsl:with-param from
+ * the xslt side and with xsl:param from the exslt side (in
+ * exsltFuncFunctionFunction()).
+ *
+ * FUTURE NOTE: In XSLT 2.0 xsl:param, xsl:variable and xsl:with-param
+ * have additional different fields.
+ */
+typedef struct _xsltStyleBasicItemVariable xsltStyleBasicItemVariable;
+typedef xsltStyleBasicItemVariable *xsltStyleBasicItemVariablePtr;
+
+struct _xsltStyleBasicItemVariable {
+ XSLT_ITEM_COMMON_FIELDS
+
+ const xmlChar *select;
+ xmlXPathCompExprPtr comp;
+
+ const xmlChar *name;
+ int has_name;
+ const xmlChar *ns;
+ int has_ns;
+};
+
+/**
+ * xsltStyleItemVariable:
+ *
+ * <!-- Category: top-level-element -->
+ * <xsl:param
+ * name = qname
+ * select = expression>
+ * <!-- Content: template -->
+ * </xsl:param>
+ */
+typedef xsltStyleBasicItemVariable xsltStyleItemVariable;
+typedef xsltStyleItemVariable *xsltStyleItemVariablePtr;
+
+/**
+ * xsltStyleItemParam:
+ *
+ * <!-- Category: top-level-element -->
+ * <xsl:param
+ * name = qname
+ * select = expression>
+ * <!-- Content: template -->
+ * </xsl:param>
+ */
+typedef struct _xsltStyleItemParam xsltStyleItemParam;
+typedef xsltStyleItemParam *xsltStyleItemParamPtr;
+
+struct _xsltStyleItemParam {
+ XSLT_ITEM_COMMON_FIELDS
+
+ const xmlChar *select;
+ xmlXPathCompExprPtr comp;
+
+ const xmlChar *name;
+ int has_name;
+ const xmlChar *ns;
+ int has_ns;
+};
+
+/**
+ * xsltStyleItemWithParam:
+ *
+ * <xsl:with-param
+ * name = qname
+ * select = expression>
+ * <!-- Content: template -->
+ * </xsl:with-param>
+ */
+typedef xsltStyleBasicItemVariable xsltStyleItemWithParam;
+typedef xsltStyleItemWithParam *xsltStyleItemWithParamPtr;
+
+/**
+ * xsltStyleItemSort:
+ *
+ * Reflects the XSLT xsl:sort item.
+ * Allowed parents: xsl:apply-templates, xsl:for-each
+ * <xsl:sort
+ * select = string-expression
+ * lang = { nmtoken }
+ * data-type = { "text" | "number" | qname-but-not-ncname }
+ * order = { "ascending" | "descending" }
+ * case-order = { "upper-first" | "lower-first" } />
+ */
+typedef struct _xsltStyleItemSort xsltStyleItemSort;
+typedef xsltStyleItemSort *xsltStyleItemSortPtr;
+
+struct _xsltStyleItemSort {
+ XSLT_ITEM_COMMON_FIELDS
+
+ const xmlChar *stype; /* sort */
+ int has_stype; /* sort */
+ int number; /* sort */
+ const xmlChar *order; /* sort */
+ int has_order; /* sort */
+ int descending; /* sort */
+ const xmlChar *lang; /* sort */
+ int has_lang; /* sort */
+ xsltLocale locale; /* sort */
+ const xmlChar *case_order; /* sort */
+ int lower_first; /* sort */
+
+ const xmlChar *use;
+ int has_use;
+
+ const xmlChar *select; /* sort, copy-of, value-of, apply-templates */
+
+ xmlXPathCompExprPtr comp; /* a precompiled XPath expression */
+};
+
+
+/**
+ * xsltStyleItemWhen:
+ *
+ * <xsl:when
+ * test = boolean-expression>
+ * <!-- Content: template -->
+ * </xsl:when>
+ * Allowed parent: xsl:choose
+ */
+typedef struct _xsltStyleItemWhen xsltStyleItemWhen;
+typedef xsltStyleItemWhen *xsltStyleItemWhenPtr;
+
+struct _xsltStyleItemWhen {
+ XSLT_ITEM_COMMON_FIELDS
+
+ const xmlChar *test;
+ xmlXPathCompExprPtr comp;
+};
+
+/**
+ * xsltStyleItemOtherwise:
+ *
+ * Allowed parent: xsl:choose
+ * <xsl:otherwise>
+ * <!-- Content: template -->
+ * </xsl:otherwise>
+ */
+typedef struct _xsltStyleItemOtherwise xsltStyleItemOtherwise;
+typedef xsltStyleItemOtherwise *xsltStyleItemOtherwisePtr;
+
+struct _xsltStyleItemOtherwise {
+ XSLT_ITEM_COMMON_FIELDS
+};
+
+typedef struct _xsltStyleItemInclude xsltStyleItemInclude;
+typedef xsltStyleItemInclude *xsltStyleItemIncludePtr;
+
+struct _xsltStyleItemInclude {
+ XSLT_ITEM_COMMON_FIELDS
+ xsltDocumentPtr include;
+};
+
+/************************************************************************
+ * *
+ * XSLT elements in forwards-compatible mode *
+ * *
+ ************************************************************************/
+
+typedef struct _xsltStyleItemUknown xsltStyleItemUknown;
+typedef xsltStyleItemUknown *xsltStyleItemUknownPtr;
+struct _xsltStyleItemUknown {
+ XSLT_ITEM_COMMON_FIELDS
+};
+
+
+/************************************************************************
+ * *
+ * Extension elements *
+ * *
+ ************************************************************************/
+
+/*
+ * xsltStyleItemExtElement:
+ *
+ * Reflects extension elements.
+ *
+ * NOTE: Due to the fact that the structure xsltElemPreComp is most
+ * probably already heavily in use out there by users, so we cannot
+ * easily change it, we'll create an intermediate structure which will
+ * hold an xsltElemPreCompPtr.
+ * BIG NOTE: The only problem I see here is that the user processes the
+ * content of the stylesheet tree, possibly he'll lookup the node->psvi
+ * fields in order to find subsequent extension functions.
+ * In this case, the user's code will break, since the node->psvi
+ * field will hold now the xsltStyleItemExtElementPtr and not
+ * the xsltElemPreCompPtr.
+ * However the place where the structure is anchored in the node-tree,
+ * namely node->psvi, has beed already once been moved from node->_private
+ * to node->psvi, so we have a precedent here, which, I think, should allow
+ * us to change such semantics without headaches.
+ */
+typedef struct _xsltStyleItemExtElement xsltStyleItemExtElement;
+typedef xsltStyleItemExtElement *xsltStyleItemExtElementPtr;
+struct _xsltStyleItemExtElement {
+ XSLT_ITEM_COMMON_FIELDS
+ xsltElemPreCompPtr item;
+};
+
+/************************************************************************
+ * *
+ * Literal result elements *
+ * *
+ ************************************************************************/
+
+typedef struct _xsltEffectiveNs xsltEffectiveNs;
+typedef xsltEffectiveNs *xsltEffectiveNsPtr;
+struct _xsltEffectiveNs {
+ xsltEffectiveNsPtr nextInStore; /* storage next */
+ xsltEffectiveNsPtr next; /* next item in the list */
+ const xmlChar *prefix;
+ const xmlChar *nsName;
+ /*
+ * Indicates if eclared on the literal result element; dunno if really
+ * needed.
+ */
+ int holdByElem;
+};
+
+/*
+ * Info for literal result elements.
+ * This will be set on the elem->psvi field and will be
+ * shared by literal result elements, which have the same
+ * excluded result namespaces; i.e., this *won't* be created uniquely
+ * for every literal result element.
+ */
+typedef struct _xsltStyleItemLRElementInfo xsltStyleItemLRElementInfo;
+typedef xsltStyleItemLRElementInfo *xsltStyleItemLRElementInfoPtr;
+struct _xsltStyleItemLRElementInfo {
+ XSLT_ITEM_COMMON_FIELDS
+ /*
+ * @effectiveNs is the set of effective ns-nodes
+ * on the literal result element, which will be added to the result
+ * element if not already existing in the result tree.
+ * This means that excluded namespaces (via exclude-result-prefixes,
+ * extension-element-prefixes and the XSLT namespace) not added
+ * to the set.
+ * Namespace-aliasing was applied on the @effectiveNs.
+ */
+ xsltEffectiveNsPtr effectiveNs;
+
+};
+
+#ifdef XSLT_REFACTORED
+
+typedef struct _xsltNsAlias xsltNsAlias;
+typedef xsltNsAlias *xsltNsAliasPtr;
+struct _xsltNsAlias {
+ xsltNsAliasPtr next; /* next in the list */
+ xmlNsPtr literalNs;
+ xmlNsPtr targetNs;
+ xmlDocPtr docOfTargetNs;
+};
+#endif
+
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+
+typedef struct _xsltNsMap xsltNsMap;
+typedef xsltNsMap *xsltNsMapPtr;
+struct _xsltNsMap {
+ xsltNsMapPtr next; /* next in the list */
+ xmlDocPtr doc;
+ xmlNodePtr elem; /* the element holding the ns-decl */
+ xmlNsPtr ns; /* the xmlNs structure holding the XML namespace name */
+ const xmlChar *origNsName; /* the original XML namespace name */
+ const xmlChar *newNsName; /* the mapped XML namespace name */
+};
+#endif
+
+/************************************************************************
+ * *
+ * Compile-time structures for *internal* use only *
+ * *
+ ************************************************************************/
+
+typedef struct _xsltPrincipalStylesheetData xsltPrincipalStylesheetData;
+typedef xsltPrincipalStylesheetData *xsltPrincipalStylesheetDataPtr;
+
+typedef struct _xsltNsList xsltNsList;
+typedef xsltNsList *xsltNsListPtr;
+struct _xsltNsList {
+ xsltNsListPtr next; /* next in the list */
+ xmlNsPtr ns;
+};
+
+/*
+* xsltVarInfo:
+*
+* Used at compilation time for parameters and variables.
+*/
+typedef struct _xsltVarInfo xsltVarInfo;
+typedef xsltVarInfo *xsltVarInfoPtr;
+struct _xsltVarInfo {
+ xsltVarInfoPtr next; /* next in the list */
+ xsltVarInfoPtr prev;
+ int depth; /* the depth in the tree */
+ const xmlChar *name;
+ const xmlChar *nsName;
+};
+
+/**
+ * xsltCompilerNodeInfo:
+ *
+ * Per-node information during compile-time.
+ */
+typedef struct _xsltCompilerNodeInfo xsltCompilerNodeInfo;
+typedef xsltCompilerNodeInfo *xsltCompilerNodeInfoPtr;
+struct _xsltCompilerNodeInfo {
+ xsltCompilerNodeInfoPtr next;
+ xsltCompilerNodeInfoPtr prev;
+ xmlNodePtr node;
+ int depth;
+ xsltTemplatePtr templ; /* The owning template */
+ int category; /* XSLT element, LR-element or
+ extension element */
+ xsltStyleType type;
+ xsltElemPreCompPtr item; /* The compiled information */
+ /* The current in-scope namespaces */
+ xsltNsListContainerPtr inScopeNs;
+ /* The current excluded result namespaces */
+ xsltPointerListPtr exclResultNs;
+ /* The current extension instruction namespaces */
+ xsltPointerListPtr extElemNs;
+
+ /* The current info for literal result elements. */
+ xsltStyleItemLRElementInfoPtr litResElemInfo;
+ /*
+ * Set to 1 if in-scope namespaces changed,
+ * or excluded result namespaces changed,
+ * or extension element namespaces changed.
+ * This will trigger creation of new infos
+ * for literal result elements.
+ */
+ int nsChanged;
+ int preserveWhitespace;
+ int stripWhitespace;
+ int isRoot; /* whether this is the stylesheet's root node */
+ int forwardsCompat; /* whether forwards-compatible mode is enabled */
+ /* whether the content of an extension element was processed */
+ int extContentHandled;
+ /* the type of the current child */
+ xsltStyleType curChildType;
+};
+
+/**
+ * XSLT_CCTXT:
+ *
+ * get pointer to compiler context
+ */
+#define XSLT_CCTXT(style) ((xsltCompilerCtxtPtr) style->compCtxt)
+
+typedef enum {
+ XSLT_ERROR_SEVERITY_ERROR = 0,
+ XSLT_ERROR_SEVERITY_WARNING
+} xsltErrorSeverityType;
+
+typedef struct _xsltCompilerCtxt xsltCompilerCtxt;
+typedef xsltCompilerCtxt *xsltCompilerCtxtPtr;
+struct _xsltCompilerCtxt {
+ void *errorCtxt; /* user specific error context */
+ /*
+ * used for error/warning reports; e.g. XSLT_ERROR_SEVERITY_WARNING */
+ xsltErrorSeverityType errSeverity;
+ int warnings; /* TODO: number of warnings found at
+ compilation */
+ int errors; /* TODO: number of errors found at
+ compilation */
+ xmlDictPtr dict;
+ xsltStylesheetPtr style;
+ int simplified; /* whether this is a simplified stylesheet */
+ /* TODO: structured/unstructured error contexts. */
+ int depth; /* Current depth of processing */
+
+ xsltCompilerNodeInfoPtr inode;
+ xsltCompilerNodeInfoPtr inodeList;
+ xsltCompilerNodeInfoPtr inodeLast;
+ xsltPointerListPtr tmpList; /* Used for various purposes */
+ /*
+ * The XSLT version as specified by the stylesheet's root element.
+ */
+ int isInclude;
+ int hasForwardsCompat; /* whether forwards-compatible mode was used
+ in a parsing episode */
+ int maxNodeInfos; /* TEMP TODO: just for the interest */
+ int maxLREs; /* TEMP TODO: just for the interest */
+ /*
+ * In order to keep the old behaviour, applying strict rules of
+ * the spec can be turned off. This has effect only on special
+ * mechanisms like whitespace-stripping in the stylesheet.
+ */
+ int strict;
+ xsltPrincipalStylesheetDataPtr psData;
+#ifdef XSLT_REFACTORED_XPATHCOMP
+ xmlXPathContextPtr xpathCtxt;
+#endif
+ xsltStyleItemUknownPtr unknownItem;
+ int hasNsAliases; /* Indicator if there was an xsl:namespace-alias. */
+ xsltNsAliasPtr nsAliases;
+ xsltVarInfoPtr ivars; /* Storage of local in-scope variables/params. */
+ xsltVarInfoPtr ivar; /* topmost local variable/param. */
+};
+
+#else /* XSLT_REFACTORED */
+/*
+* The old structures before refactoring.
+*/
+
+/**
+ * _xsltStylePreComp:
+ *
+ * The in-memory structure corresponding to XSLT stylesheet constructs
+ * precomputed data.
+ */
+struct _xsltStylePreComp {
+ xsltElemPreCompPtr next; /* chained list */
+ xsltStyleType type; /* type of the element */
+ xsltTransformFunction func; /* handling function */
+ xmlNodePtr inst; /* the instruction */
+
+ /*
+ * Pre computed values.
+ */
+
+ const xmlChar *stype; /* sort */
+ int has_stype; /* sort */
+ int number; /* sort */
+ const xmlChar *order; /* sort */
+ int has_order; /* sort */
+ int descending; /* sort */
+ const xmlChar *lang; /* sort */
+ int has_lang; /* sort */
+ xsltLocale locale; /* sort */
+ const xmlChar *case_order; /* sort */
+ int lower_first; /* sort */
+
+ const xmlChar *use; /* copy, element */
+ int has_use; /* copy, element */
+
+ int noescape; /* text */
+
+ const xmlChar *name; /* element, attribute, pi */
+ int has_name; /* element, attribute, pi */
+ const xmlChar *ns; /* element */
+ int has_ns; /* element */
+
+ const xmlChar *mode; /* apply-templates */
+ const xmlChar *modeURI; /* apply-templates */
+
+ const xmlChar *test; /* if */
+
+ xsltTemplatePtr templ; /* call-template */
+
+ const xmlChar *select; /* sort, copy-of, value-of, apply-templates */
+
+ int ver11; /* document */
+ const xmlChar *filename; /* document URL */
+ int has_filename; /* document */
+
+ xsltNumberData numdata; /* number */
+
+ xmlXPathCompExprPtr comp; /* a precompiled XPath expression */
+ xmlNsPtr *nsList; /* the namespaces in scope */
+ int nsNr; /* the number of namespaces in scope */
+};
+
+#endif /* XSLT_REFACTORED */
+
+
+/*
+ * The in-memory structure corresponding to an XSLT Variable
+ * or Param.
+ */
+typedef struct _xsltStackElem xsltStackElem;
+typedef xsltStackElem *xsltStackElemPtr;
+struct _xsltStackElem {
+ struct _xsltStackElem *next;/* chained list */
+ xsltStylePreCompPtr comp; /* the compiled form */
+ int computed; /* was the evaluation done */
+ const xmlChar *name; /* the local part of the name QName */
+ const xmlChar *nameURI; /* the URI part of the name QName */
+ const xmlChar *select; /* the eval string */
+ xmlNodePtr tree; /* the sequence constructor if no eval
+ string or the location */
+ xmlXPathObjectPtr value; /* The value if computed */
+ xmlDocPtr fragment; /* The Result Tree Fragments (needed for XSLT 1.0)
+ which are bound to the variable's lifetime. */
+ int level; /* the depth in the tree;
+ -1 if persistent (e.g. a given xsl:with-param) */
+ xsltTransformContextPtr context; /* The transformation context; needed to cache
+ the variables */
+ int flags;
+};
+
+#ifdef XSLT_REFACTORED
+
+struct _xsltPrincipalStylesheetData {
+ /*
+ * Namespace dictionary for ns-prefixes and ns-names:
+ * TODO: Shared between stylesheets, and XPath mechanisms.
+ * Not used yet.
+ */
+ xmlDictPtr namespaceDict;
+ /*
+ * Global list of in-scope namespaces.
+ */
+ xsltPointerListPtr inScopeNamespaces;
+ /*
+ * Global list of information for [xsl:]excluded-result-prefixes.
+ */
+ xsltPointerListPtr exclResultNamespaces;
+ /*
+ * Global list of information for [xsl:]extension-element-prefixes.
+ */
+ xsltPointerListPtr extElemNamespaces;
+ xsltEffectiveNsPtr effectiveNs;
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+ /*
+ * Namespace name map to get rid of string comparison of namespace names.
+ */
+ xsltNsMapPtr nsMap;
+#endif
+};
+
+
+#endif
+/*
+ * Note that we added a @compCtxt field to anchor an stylesheet compilation
+ * context, since, due to historical reasons, various compile-time function
+ * take only the stylesheet as argument and not a compilation context.
+ */
+struct _xsltStylesheet {
+ /*
+ * The stylesheet import relation is kept as a tree.
+ */
+ struct _xsltStylesheet *parent;
+ struct _xsltStylesheet *next;
+ struct _xsltStylesheet *imports;
+
+ xsltDocumentPtr docList; /* the include document list */
+
+ /*
+ * General data on the style sheet document.
+ */
+ xmlDocPtr doc; /* the parsed XML stylesheet */
+ xmlHashTablePtr stripSpaces;/* the hash table of the strip-space and
+ preserve space elements */
+ int stripAll; /* strip-space * (1) preserve-space * (-1) */
+ xmlHashTablePtr cdataSection;/* the hash table of the cdata-section */
+
+ /*
+ * Global variable or parameters.
+ */
+ xsltStackElemPtr variables; /* linked list of param and variables */
+
+ /*
+ * Template descriptions.
+ */
+ xsltTemplatePtr templates; /* the ordered list of templates */
+ void *templatesHash; /* hash table or wherever compiled templates
+ informations are stored */
+ void *rootMatch; /* template based on / */
+ void *keyMatch; /* template based on key() */
+ void *elemMatch; /* template based on * */
+ void *attrMatch; /* template based on @* */
+ void *parentMatch; /* template based on .. */
+ void *textMatch; /* template based on text() */
+ void *piMatch; /* template based on processing-instruction() */
+ void *commentMatch; /* template based on comment() */
+
+ /*
+ * Namespace aliases.
+ * NOTE: Not used in the refactored code.
+ */
+ xmlHashTablePtr nsAliases; /* the namespace alias hash tables */
+
+ /*
+ * Attribute sets.
+ */
+ xmlHashTablePtr attributeSets;/* the attribute sets hash tables */
+
+ /*
+ * Namespaces.
+ * TODO: Eliminate this.
+ */
+ xmlHashTablePtr nsHash; /* the set of namespaces in use:
+ ATTENTION: This is used for
+ execution of XPath expressions; unfortunately
+ it restricts the stylesheet to have distinct
+ prefixes.
+ TODO: We need to get rid of this.
+ */
+ void *nsDefs; /* ATTENTION TODO: This is currently used to store
+ xsltExtDefPtr (in extensions.c) and
+ *not* xmlNsPtr.
+ */
+
+ /*
+ * Key definitions.
+ */
+ void *keys; /* key definitions */
+
+ /*
+ * Output related stuff.
+ */
+ xmlChar *method; /* the output method */
+ xmlChar *methodURI; /* associated namespace if any */
+ xmlChar *version; /* version string */
+ xmlChar *encoding; /* encoding string */
+ int omitXmlDeclaration; /* omit-xml-declaration = "yes" | "no" */
+
+ /*
+ * Number formatting.
+ */
+ xsltDecimalFormatPtr decimalFormat;
+ int standalone; /* standalone = "yes" | "no" */
+ xmlChar *doctypePublic; /* doctype-public string */
+ xmlChar *doctypeSystem; /* doctype-system string */
+ int indent; /* should output being indented */
+ xmlChar *mediaType; /* media-type string */
+
+ /*
+ * Precomputed blocks.
+ */
+ xsltElemPreCompPtr preComps;/* list of precomputed blocks */
+ int warnings; /* number of warnings found at compilation */
+ int errors; /* number of errors found at compilation */
+
+ xmlChar *exclPrefix; /* last excluded prefixes */
+ xmlChar **exclPrefixTab; /* array of excluded prefixes */
+ int exclPrefixNr; /* number of excluded prefixes in scope */
+ int exclPrefixMax; /* size of the array */
+
+ void *_private; /* user defined data */
+
+ /*
+ * Extensions.
+ */
+ xmlHashTablePtr extInfos; /* the extension data */
+ int extrasNr; /* the number of extras required */
+
+ /*
+ * For keeping track of nested includes
+ */
+ xsltDocumentPtr includes; /* points to last nested include */
+
+ /*
+ * dictionary: shared between stylesheet, context and documents.
+ */
+ xmlDictPtr dict;
+ /*
+ * precompiled attribute value templates.
+ */
+ void *attVTs;
+ /*
+ * if namespace-alias has an alias for the default stylesheet prefix
+ * NOTE: Not used in the refactored code.
+ */
+ const xmlChar *defaultAlias;
+ /*
+ * bypass pre-processing (already done) (used in imports)
+ */
+ int nopreproc;
+ /*
+ * all document text strings were internalized
+ */
+ int internalized;
+ /*
+ * Literal Result Element as Stylesheet c.f. section 2.3
+ */
+ int literal_result;
+ /*
+ * The principal stylesheet
+ */
+ xsltStylesheetPtr principal;
+#ifdef XSLT_REFACTORED
+ /*
+ * Compilation context used during compile-time.
+ */
+ xsltCompilerCtxtPtr compCtxt; /* TODO: Change this to (void *). */
+
+ xsltPrincipalStylesheetDataPtr principalData;
+#endif
+ /*
+ * Forwards-compatible processing
+ */
+ int forwards_compatible;
+
+ xmlHashTablePtr namedTemplates; /* hash table of named templates */
+};
+
+typedef struct _xsltTransformCache xsltTransformCache;
+typedef xsltTransformCache *xsltTransformCachePtr;
+struct _xsltTransformCache {
+ xmlDocPtr RVT;
+ int nbRVT;
+ xsltStackElemPtr stackItems;
+ int nbStackItems;
+#ifdef XSLT_DEBUG_PROFILE_CACHE
+ int dbgCachedRVTs;
+ int dbgReusedRVTs;
+ int dbgCachedVars;
+ int dbgReusedVars;
+#endif
+};
+
+/*
+ * The in-memory structure corresponding to an XSLT Transformation.
+ */
+typedef enum {
+ XSLT_OUTPUT_XML = 0,
+ XSLT_OUTPUT_HTML,
+ XSLT_OUTPUT_TEXT
+} xsltOutputType;
+
+typedef enum {
+ XSLT_STATE_OK = 0,
+ XSLT_STATE_ERROR,
+ XSLT_STATE_STOPPED
+} xsltTransformState;
+
+struct _xsltTransformContext {
+ xsltStylesheetPtr style; /* the stylesheet used */
+ xsltOutputType type; /* the type of output */
+
+ xsltTemplatePtr templ; /* the current template */
+ int templNr; /* Nb of templates in the stack */
+ int templMax; /* Size of the templtes stack */
+ xsltTemplatePtr *templTab; /* the template stack */
+
+ xsltStackElemPtr vars; /* the current variable list */
+ int varsNr; /* Nb of variable list in the stack */
+ int varsMax; /* Size of the variable list stack */
+ xsltStackElemPtr *varsTab; /* the variable list stack */
+ int varsBase; /* the var base for current templ */
+
+ /*
+ * Extensions
+ */
+ xmlHashTablePtr extFunctions; /* the extension functions */
+ xmlHashTablePtr extElements; /* the extension elements */
+ xmlHashTablePtr extInfos; /* the extension data */
+
+ const xmlChar *mode; /* the current mode */
+ const xmlChar *modeURI; /* the current mode URI */
+
+ xsltDocumentPtr docList; /* the document list */
+
+ xsltDocumentPtr document; /* the current source document; can be NULL if an RTF */
+ xmlNodePtr node; /* the current node being processed */
+ xmlNodeSetPtr nodeList; /* the current node list */
+ /* xmlNodePtr current; the node */
+
+ xmlDocPtr output; /* the resulting document */
+ xmlNodePtr insert; /* the insertion node */
+
+ xmlXPathContextPtr xpathCtxt; /* the XPath context */
+ xsltTransformState state; /* the current state */
+
+ /*
+ * Global variables
+ */
+ xmlHashTablePtr globalVars; /* the global variables and params */
+
+ xmlNodePtr inst; /* the instruction in the stylesheet */
+
+ int xinclude; /* should XInclude be processed */
+
+ const char * outputFile; /* the output URI if known */
+
+ int profile; /* is this run profiled */
+ long prof; /* the current profiled value */
+ int profNr; /* Nb of templates in the stack */
+ int profMax; /* Size of the templtaes stack */
+ long *profTab; /* the profile template stack */
+
+ void *_private; /* user defined data */
+
+ int extrasNr; /* the number of extras used */
+ int extrasMax; /* the number of extras allocated */
+ xsltRuntimeExtraPtr extras; /* extra per runtime informations */
+
+ xsltDocumentPtr styleList; /* the stylesheet docs list */
+ void * sec; /* the security preferences if any */
+
+ xmlGenericErrorFunc error; /* a specific error handler */
+ void * errctx; /* context for the error handler */
+
+ xsltSortFunc sortfunc; /* a ctxt specific sort routine */
+
+ /*
+ * handling of temporary Result Value Tree
+ * (XSLT 1.0 term: "Result Tree Fragment")
+ */
+ xmlDocPtr tmpRVT; /* list of RVT without persistance */
+ xmlDocPtr persistRVT; /* list of persistant RVTs */
+ int ctxtflags; /* context processing flags */
+
+ /*
+ * Speed optimization when coalescing text nodes
+ */
+ const xmlChar *lasttext; /* last text node content */
+ unsigned int lasttsize; /* last text node size */
+ unsigned int lasttuse; /* last text node use */
+ /*
+ * Per Context Debugging
+ */
+ int debugStatus; /* the context level debug status */
+ unsigned long* traceCode; /* pointer to the variable holding the mask */
+
+ int parserOptions; /* parser options xmlParserOption */
+
+ /*
+ * dictionary: shared between stylesheet, context and documents.
+ */
+ xmlDictPtr dict;
+ xmlDocPtr tmpDoc; /* Obsolete; not used in the library. */
+ /*
+ * all document text strings are internalized
+ */
+ int internalized;
+ int nbKeys;
+ int hasTemplKeyPatterns;
+ xsltTemplatePtr currentTemplateRule; /* the Current Template Rule */
+ xmlNodePtr initialContextNode;
+ xmlDocPtr initialContextDoc;
+ xsltTransformCachePtr cache;
+ void *contextVariable; /* the current variable item */
+ xmlDocPtr localRVT; /* list of local tree fragments; will be freed when
+ the instruction which created the fragment
+ exits */
+ xmlDocPtr localRVTBase; /* Obsolete */
+ int keyInitLevel; /* Needed to catch recursive keys issues */
+ int depth; /* Needed to catch recursions */
+ int maxTemplateDepth;
+ int maxTemplateVars;
+};
+
+/**
+ * CHECK_STOPPED:
+ *
+ * Macro to check if the XSLT processing should be stopped.
+ * Will return from the function.
+ */
+#define CHECK_STOPPED if (ctxt->state == XSLT_STATE_STOPPED) return;
+
+/**
+ * CHECK_STOPPEDE:
+ *
+ * Macro to check if the XSLT processing should be stopped.
+ * Will goto the error: label.
+ */
+#define CHECK_STOPPEDE if (ctxt->state == XSLT_STATE_STOPPED) goto error;
+
+/**
+ * CHECK_STOPPED0:
+ *
+ * Macro to check if the XSLT processing should be stopped.
+ * Will return from the function with a 0 value.
+ */
+#define CHECK_STOPPED0 if (ctxt->state == XSLT_STATE_STOPPED) return(0);
+
+/*
+ * The macro XML_CAST_FPTR is a hack to avoid a gcc warning about
+ * possible incompatibilities between function pointers and object
+ * pointers. It is defined in libxml/hash.h within recent versions
+ * of libxml2, but is put here for compatibility.
+ */
+#ifndef XML_CAST_FPTR
+/**
+ * XML_CAST_FPTR:
+ * @fptr: pointer to a function
+ *
+ * Macro to do a casting from an object pointer to a
+ * function pointer without encountering a warning from
+ * gcc
+ *
+ * #define XML_CAST_FPTR(fptr) (*(void **)(&fptr))
+ * This macro violated ISO C aliasing rules (gcc4 on s390 broke)
+ * so it is disabled now
+ */
+
+#define XML_CAST_FPTR(fptr) fptr
+#endif
+/*
+ * Functions associated to the internal types
+xsltDecimalFormatPtr xsltDecimalFormatGetByName(xsltStylesheetPtr sheet,
+ xmlChar *name);
+ */
+XSLTPUBFUN xsltStylesheetPtr XSLTCALL
+ xsltNewStylesheet (void);
+XSLTPUBFUN xsltStylesheetPtr XSLTCALL
+ xsltParseStylesheetFile (const xmlChar* filename);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeStylesheet (xsltStylesheetPtr style);
+XSLTPUBFUN int XSLTCALL
+ xsltIsBlank (xmlChar *str);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeStackElemList (xsltStackElemPtr elem);
+XSLTPUBFUN xsltDecimalFormatPtr XSLTCALL
+ xsltDecimalFormatGetByName(xsltStylesheetPtr style,
+ xmlChar *name);
+
+XSLTPUBFUN xsltStylesheetPtr XSLTCALL
+ xsltParseStylesheetProcess(xsltStylesheetPtr ret,
+ xmlDocPtr doc);
+XSLTPUBFUN void XSLTCALL
+ xsltParseStylesheetOutput(xsltStylesheetPtr style,
+ xmlNodePtr cur);
+XSLTPUBFUN xsltStylesheetPtr XSLTCALL
+ xsltParseStylesheetDoc (xmlDocPtr doc);
+XSLTPUBFUN xsltStylesheetPtr XSLTCALL
+ xsltParseStylesheetImportedDoc(xmlDocPtr doc,
+ xsltStylesheetPtr style);
+XSLTPUBFUN xsltStylesheetPtr XSLTCALL
+ xsltLoadStylesheetPI (xmlDocPtr doc);
+XSLTPUBFUN void XSLTCALL
+ xsltNumberFormat (xsltTransformContextPtr ctxt,
+ xsltNumberDataPtr data,
+ xmlNodePtr node);
+XSLTPUBFUN xmlXPathError XSLTCALL
+ xsltFormatNumberConversion(xsltDecimalFormatPtr self,
+ xmlChar *format,
+ double number,
+ xmlChar **result);
+
+XSLTPUBFUN void XSLTCALL
+ xsltParseTemplateContent(xsltStylesheetPtr style,
+ xmlNodePtr templ);
+XSLTPUBFUN int XSLTCALL
+ xsltAllocateExtra (xsltStylesheetPtr style);
+XSLTPUBFUN int XSLTCALL
+ xsltAllocateExtraCtxt (xsltTransformContextPtr ctxt);
+/*
+ * Extra functions for Result Value Trees
+ */
+XSLTPUBFUN xmlDocPtr XSLTCALL
+ xsltCreateRVT (xsltTransformContextPtr ctxt);
+XSLTPUBFUN int XSLTCALL
+ xsltRegisterTmpRVT (xsltTransformContextPtr ctxt,
+ xmlDocPtr RVT);
+XSLTPUBFUN int XSLTCALL
+ xsltRegisterLocalRVT (xsltTransformContextPtr ctxt,
+ xmlDocPtr RVT);
+XSLTPUBFUN int XSLTCALL
+ xsltRegisterPersistRVT (xsltTransformContextPtr ctxt,
+ xmlDocPtr RVT);
+XSLTPUBFUN int XSLTCALL
+ xsltExtensionInstructionResultRegister(
+ xsltTransformContextPtr ctxt,
+ xmlXPathObjectPtr obj);
+XSLTPUBFUN int XSLTCALL
+ xsltExtensionInstructionResultFinalize(
+ xsltTransformContextPtr ctxt);
+XSLTPUBFUN int XSLTCALL
+ xsltFlagRVTs(
+ xsltTransformContextPtr ctxt,
+ xmlXPathObjectPtr obj,
+ void *val);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeRVTs (xsltTransformContextPtr ctxt);
+XSLTPUBFUN void XSLTCALL
+ xsltReleaseRVT (xsltTransformContextPtr ctxt,
+ xmlDocPtr RVT);
+/*
+ * Extra functions for Attribute Value Templates
+ */
+XSLTPUBFUN void XSLTCALL
+ xsltCompileAttr (xsltStylesheetPtr style,
+ xmlAttrPtr attr);
+XSLTPUBFUN xmlChar * XSLTCALL
+ xsltEvalAVT (xsltTransformContextPtr ctxt,
+ void *avt,
+ xmlNodePtr node);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeAVTList (void *avt);
+
+/*
+ * Extra function for successful xsltCleanupGlobals / xsltInit sequence.
+ */
+
+XSLTPUBFUN void XSLTCALL
+ xsltUninit (void);
+
+/************************************************************************
+ * *
+ * Compile-time functions for *internal* use only *
+ * *
+ ************************************************************************/
+
+#ifdef XSLT_REFACTORED
+XSLTPUBFUN void XSLTCALL
+ xsltParseSequenceConstructor(
+ xsltCompilerCtxtPtr cctxt,
+ xmlNodePtr start);
+XSLTPUBFUN int XSLTCALL
+ xsltParseAnyXSLTElem (xsltCompilerCtxtPtr cctxt,
+ xmlNodePtr elem);
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+XSLTPUBFUN int XSLTCALL
+ xsltRestoreDocumentNamespaces(
+ xsltNsMapPtr ns,
+ xmlDocPtr doc);
+#endif
+#endif /* XSLT_REFACTORED */
+
+/************************************************************************
+ * *
+ * Transformation-time functions for *internal* use only *
+ * *
+ ************************************************************************/
+XSLTPUBFUN int XSLTCALL
+ xsltInitCtxtKey (xsltTransformContextPtr ctxt,
+ xsltDocumentPtr doc,
+ xsltKeyDefPtr keyd);
+XSLTPUBFUN int XSLTCALL
+ xsltInitAllDocKeys (xsltTransformContextPtr ctxt);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_H__ */
+
diff --git a/libxslt/xsltconfig.h.in b/libxslt/xsltconfig.h.in
new file mode 100644
index 0000000..6e4e328
--- /dev/null
+++ b/libxslt/xsltconfig.h.in
@@ -0,0 +1,183 @@
+/*
+ * Summary: compile-time version informations for the XSLT engine
+ * Description: compile-time version informations for the XSLT engine
+ * this module is autogenerated.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLTCONFIG_H__
+#define __XML_XSLTCONFIG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * LIBXSLT_DOTTED_VERSION:
+ *
+ * the version string like "1.2.3"
+ */
+#define LIBXSLT_DOTTED_VERSION "@VERSION@"
+
+/**
+ * LIBXSLT_VERSION:
+ *
+ * the version number: 1.2.3 value is 10203
+ */
+#define LIBXSLT_VERSION @LIBXSLT_VERSION_NUMBER@
+
+/**
+ * LIBXSLT_VERSION_STRING:
+ *
+ * the version number string, 1.2.3 value is "10203"
+ */
+#define LIBXSLT_VERSION_STRING "@LIBXSLT_VERSION_NUMBER@"
+
+/**
+ * LIBXSLT_VERSION_EXTRA:
+ *
+ * extra version information, used to show a CVS compilation
+ */
+#define LIBXSLT_VERSION_EXTRA "@LIBXSLT_VERSION_EXTRA@"
+
+/**
+ * WITH_XSLT_DEBUG:
+ *
+ * Activate the compilation of the debug reporting. Speed penalty
+ * is insignifiant and being able to run xsltpoc -v is useful. On
+ * by default unless --without-debug is passed to configure
+ */
+#if @WITH_XSLT_DEBUG@
+#define WITH_XSLT_DEBUG
+#endif
+
+#if @WITH_MEM_DEBUG@
+/**
+ * DEBUG_MEMORY:
+ *
+ * should be activated only when debugging libxslt. It replaces the
+ * allocator with a collect and debug shell to the libc allocator.
+ * Use configure --with-mem-debug to activate it on both library
+ */
+#define DEBUG_MEMORY
+
+/**
+ * DEBUG_MEMORY_LOCATION:
+ *
+ * should be activated only when debugging libxslt.
+ * DEBUG_MEMORY_LOCATION should be activated only when libxml has
+ * been configured with --with-debug-mem too
+ */
+#define DEBUG_MEMORY_LOCATION
+#endif
+
+/**
+ * XSLT_NEED_TRIO:
+ *
+ * should be activated if the existing libc library lacks some of the
+ * string formatting function, in that case reuse the Trio ones already
+ * compiled in the libxml2 library.
+ */
+
+#if @WITH_TRIO@
+#define XSLT_NEED_TRIO
+#endif
+#ifdef __VMS
+#define HAVE_MATH_H 1
+#define HAVE_SYS_STAT_H 1
+#ifndef XSLT_NEED_TRIO
+#define XSLT_NEED_TRIO
+#endif
+#endif
+
+#ifdef XSLT_NEED_TRIO
+#define TRIO_REPLACE_STDIO
+#endif
+
+/**
+ * WITH_XSLT_DEBUGGER:
+ *
+ * Activate the compilation of the debugger support. Speed penalty
+ * is insignifiant.
+ * On by default unless --without-debugger is passed to configure
+ */
+#if @WITH_DEBUGGER@
+#ifndef WITH_DEBUGGER
+#define WITH_DEBUGGER
+#endif
+#endif
+
+/**
+ * WITH_MODULES:
+ *
+ * Whether module support is configured into libxslt
+ * Note: no default module path for win32 platforms
+ */
+#if @WITH_MODULES@
+#ifndef WITH_MODULES
+#define WITH_MODULES
+#endif
+#define LIBXSLT_DEFAULT_PLUGINS_PATH() "@LIBXSLT_DEFAULT_PLUGINS_PATH@"
+#endif
+
+/**
+ * Locale support
+ */
+#if @XSLT_LOCALE_XLOCALE@
+#ifndef XSLT_LOCALE_XLOCALE
+#define XSLT_LOCALE_XLOCALE
+#endif
+#elif @XSLT_LOCALE_WINAPI@
+#ifndef XSLT_LOCALE_WINAPI
+#define XSLT_LOCALE_WINAPI
+#endif
+#endif
+
+/**
+ * ATTRIBUTE_UNUSED:
+ *
+ * This macro is used to flag unused function parameters to GCC
+ */
+#ifdef __GNUC__
+#ifdef HAVE_ANSIDECL_H
+#include <ansidecl.h>
+#endif
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED __attribute__((unused))
+#endif
+#else
+#define ATTRIBUTE_UNUSED
+#endif
+
+/**
+ * LIBXSLT_ATTR_FORMAT:
+ *
+ * This macro is used to indicate to GCC the parameters are printf-like
+ */
+#ifdef __GNUC__
+#define LIBXSLT_ATTR_FORMAT(fmt,args) __attribute__((__format__(__printf__,fmt,args)))
+#else
+#define LIBXSLT_ATTR_FORMAT(fmt,args)
+#endif
+
+/**
+ * LIBXSLT_PUBLIC:
+ *
+ * This macro is used to declare PUBLIC variables for Cygwin and for MSC on Windows
+ */
+#if !defined LIBXSLT_PUBLIC
+#if (defined(__CYGWIN__) || defined _MSC_VER) && !defined IN_LIBXSLT && !defined LIBXSLT_STATIC
+#define LIBXSLT_PUBLIC __declspec(dllimport)
+#else
+#define LIBXSLT_PUBLIC
+#endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLTCONFIG_H__ */
diff --git a/libxslt/xsltexports.h b/libxslt/xsltexports.h
new file mode 100644
index 0000000..825c122
--- /dev/null
+++ b/libxslt/xsltexports.h
@@ -0,0 +1,142 @@
+/*
+ * Summary: macros for marking symbols as exportable/importable.
+ * Description: macros for marking symbols as exportable/importable.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Igor Zlatkovic <igor@zlatkovic.com>
+ */
+
+#ifndef __XSLT_EXPORTS_H__
+#define __XSLT_EXPORTS_H__
+
+/**
+ * XSLTPUBFUN:
+ * XSLTPUBFUN, XSLTPUBVAR, XSLTCALL
+ *
+ * Macros which declare an exportable function, an exportable variable and
+ * the calling convention used for functions.
+ *
+ * Please use an extra block for every platform/compiler combination when
+ * modifying this, rather than overlong #ifdef lines. This helps
+ * readability as well as the fact that different compilers on the same
+ * platform might need different definitions.
+ */
+
+/**
+ * XSLTPUBFUN:
+ *
+ * Macros which declare an exportable function
+ */
+#define XSLTPUBFUN
+/**
+ * XSLTPUBVAR:
+ *
+ * Macros which declare an exportable variable
+ */
+#define XSLTPUBVAR extern
+/**
+ * XSLTCALL:
+ *
+ * Macros which declare the called convention for exported functions
+ */
+#define XSLTCALL
+
+/** DOC_DISABLE */
+
+/* Windows platform with MS compiler */
+#if defined(_WIN32) && defined(_MSC_VER)
+ #undef XSLTPUBFUN
+ #undef XSLTPUBVAR
+ #undef XSLTCALL
+ #if defined(IN_LIBXSLT) && !defined(LIBXSLT_STATIC)
+ #define XSLTPUBFUN __declspec(dllexport)
+ #define XSLTPUBVAR __declspec(dllexport)
+ #else
+ #define XSLTPUBFUN
+ #if !defined(LIBXSLT_STATIC)
+ #define XSLTPUBVAR __declspec(dllimport) extern
+ #else
+ #define XSLTPUBVAR extern
+ #endif
+ #endif
+ #define XSLTCALL __cdecl
+ #if !defined _REENTRANT
+ #define _REENTRANT
+ #endif
+#endif
+
+/* Windows platform with Borland compiler */
+#if defined(_WIN32) && defined(__BORLANDC__)
+ #undef XSLTPUBFUN
+ #undef XSLTPUBVAR
+ #undef XSLTCALL
+ #if defined(IN_LIBXSLT) && !defined(LIBXSLT_STATIC)
+ #define XSLTPUBFUN __declspec(dllexport)
+ #define XSLTPUBVAR __declspec(dllexport) extern
+ #else
+ #define XSLTPUBFUN
+ #if !defined(LIBXSLT_STATIC)
+ #define XSLTPUBVAR __declspec(dllimport) extern
+ #else
+ #define XSLTPUBVAR extern
+ #endif
+ #endif
+ #define XSLTCALL __cdecl
+ #if !defined _REENTRANT
+ #define _REENTRANT
+ #endif
+#endif
+
+/* Windows platform with GNU compiler (Mingw) */
+#if defined(_WIN32) && defined(__MINGW32__)
+ #undef XSLTPUBFUN
+ #undef XSLTPUBVAR
+ #undef XSLTCALL
+/*
+ #if defined(IN_LIBXSLT) && !defined(LIBXSLT_STATIC)
+*/
+ #if !defined(LIBXSLT_STATIC)
+ #define XSLTPUBFUN __declspec(dllexport)
+ #define XSLTPUBVAR __declspec(dllexport) extern
+ #else
+ #define XSLTPUBFUN
+ #if !defined(LIBXSLT_STATIC)
+ #define XSLTPUBVAR __declspec(dllimport) extern
+ #else
+ #define XSLTPUBVAR extern
+ #endif
+ #endif
+ #define XSLTCALL __cdecl
+ #if !defined _REENTRANT
+ #define _REENTRANT
+ #endif
+#endif
+
+/* Cygwin platform, GNU compiler */
+#if defined(_WIN32) && defined(__CYGWIN__)
+ #undef XSLTPUBFUN
+ #undef XSLTPUBVAR
+ #undef XSLTCALL
+ #if defined(IN_LIBXSLT) && !defined(LIBXSLT_STATIC)
+ #define XSLTPUBFUN __declspec(dllexport)
+ #define XSLTPUBVAR __declspec(dllexport)
+ #else
+ #define XSLTPUBFUN
+ #if !defined(LIBXSLT_STATIC)
+ #define XSLTPUBVAR __declspec(dllimport) extern
+ #else
+ #define XSLTPUBVAR
+ #endif
+ #endif
+ #define XSLTCALL __cdecl
+#endif
+
+/* Compatibility */
+#if !defined(LIBXSLT_PUBLIC)
+#define LIBXSLT_PUBLIC XSLTPUBVAR
+#endif
+
+#endif /* __XSLT_EXPORTS_H__ */
+
+
diff --git a/libxslt/xsltlocale.c b/libxslt/xsltlocale.c
new file mode 100644
index 0000000..b5fe986
--- /dev/null
+++ b/libxslt/xsltlocale.c
@@ -0,0 +1,525 @@
+/*
+ * xsltlocale.c: locale handling
+ *
+ * Reference:
+ * RFC 3066: Tags for the Identification of Languages
+ * http://www.ietf.org/rfc/rfc3066.txt
+ * ISO 639-1, ISO 3166-1
+ *
+ * Author: Nick Wellnhofer
+ * winapi port: Roumen Petrov
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#include <string.h>
+#include <libxml/xmlmemory.h>
+
+#include "xsltlocale.h"
+#include "xsltutils.h"
+
+#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ <= 2
+#define newlocale __newlocale
+#define freelocale __freelocale
+#define strxfrm_l __strxfrm_l
+#define LC_COLLATE_MASK (1 << LC_COLLATE)
+#endif
+
+#define TOUPPER(c) (c & ~0x20)
+#define TOLOWER(c) (c | 0x20)
+#define ISALPHA(c) ((unsigned)(TOUPPER(c) - 'A') < 26)
+
+/*without terminating null character*/
+#define XSLTMAX_ISO639LANGLEN 8
+#define XSLTMAX_ISO3166CNTRYLEN 8
+ /* <lang>-<cntry> */
+#define XSLTMAX_LANGTAGLEN (XSLTMAX_ISO639LANGLEN+1+XSLTMAX_ISO3166CNTRYLEN)
+
+static const xmlChar* xsltDefaultRegion(const xmlChar *localeName);
+
+#ifdef XSLT_LOCALE_WINAPI
+xmlRMutexPtr xsltLocaleMutex = NULL;
+
+struct xsltRFC1766Info_s {
+ /*note typedef unsigned char xmlChar !*/
+ xmlChar tag[XSLTMAX_LANGTAGLEN+1];
+ /*note typedef LCID xsltLocale !*/
+ xsltLocale lcid;
+};
+typedef struct xsltRFC1766Info_s xsltRFC1766Info;
+
+static int xsltLocaleListSize = 0;
+static xsltRFC1766Info *xsltLocaleList = NULL;
+
+
+static xsltLocale
+xslt_locale_WINAPI(const xmlChar *languageTag) {
+ int k;
+ xsltRFC1766Info *p = xsltLocaleList;
+
+ for (k=0; k<xsltLocaleListSize; k++, p++)
+ if (xmlStrcmp(p->tag, languageTag) == 0) return p->lcid;
+ return((xsltLocale)0);
+}
+
+static void xsltEnumSupportedLocales(void);
+#endif
+
+/**
+ * xsltFreeLocales:
+ *
+ * Cleanup function for the locale support on shutdown
+ */
+void
+xsltFreeLocales(void) {
+#ifdef XSLT_LOCALE_WINAPI
+ xmlRMutexLock(xsltLocaleMutex);
+ xmlFree(xsltLocaleList);
+ xsltLocaleList = NULL;
+ xmlRMutexUnlock(xsltLocaleMutex);
+#endif
+}
+
+/**
+ * xsltNewLocale:
+ * @languageTag: RFC 3066 language tag
+ *
+ * Creates a new locale of an opaque system dependent type based on the
+ * language tag.
+ *
+ * Returns the locale or NULL on error or if no matching locale was found
+ */
+xsltLocale
+xsltNewLocale(const xmlChar *languageTag) {
+#ifdef XSLT_LOCALE_XLOCALE
+ xsltLocale locale;
+ char localeName[XSLTMAX_LANGTAGLEN+6]; /* 6 chars for ".utf8\0" */
+ const xmlChar *p = languageTag;
+ const char *region = NULL;
+ char *q = localeName;
+ int i, llen;
+
+ /* Convert something like "pt-br" to "pt_BR.utf8" */
+
+ if (languageTag == NULL)
+ return(NULL);
+
+ for (i=0; i<XSLTMAX_ISO639LANGLEN && ISALPHA(*p); ++i)
+ *q++ = TOLOWER(*p++);
+
+ if (i == 0)
+ return(NULL);
+
+ llen = i;
+
+ if (*p) {
+ if (*p++ != '-')
+ return(NULL);
+ *q++ = '_';
+
+ for (i=0; i<XSLTMAX_ISO3166CNTRYLEN && ISALPHA(*p); ++i)
+ *q++ = TOUPPER(*p++);
+
+ if (i == 0 || *p)
+ return(NULL);
+
+ memcpy(q, ".utf8", 6);
+ locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
+ if (locale != NULL)
+ return(locale);
+
+ /* Continue without using country code */
+
+ q = localeName + llen;
+ }
+
+ /* Try locale without territory, e.g. for Esperanto (eo) */
+
+ memcpy(q, ".utf8", 6);
+ locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
+ if (locale != NULL)
+ return(locale);
+
+ /* Try to find most common country for language */
+
+ if (llen != 2)
+ return(NULL);
+
+ region = (char *)xsltDefaultRegion((xmlChar *)localeName);
+ if (region == NULL)
+ return(NULL);
+
+ q = localeName + llen;
+ *q++ = '_';
+ *q++ = region[0];
+ *q++ = region[1];
+ memcpy(q, ".utf8", 6);
+ locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
+
+ return(locale);
+#endif
+
+#ifdef XSLT_LOCALE_WINAPI
+{
+ xsltLocale locale = (xsltLocale)0;
+ xmlChar localeName[XSLTMAX_LANGTAGLEN+1];
+ xmlChar *q = localeName;
+ const xmlChar *p = languageTag;
+ int i, llen;
+ const xmlChar *region = NULL;
+
+ if (languageTag == NULL) goto end;
+
+ xsltEnumSupportedLocales();
+
+ for (i=0; i<XSLTMAX_ISO639LANGLEN && ISALPHA(*p); ++i)
+ *q++ = TOLOWER(*p++);
+ if (i == 0) goto end;
+
+ llen = i;
+ *q++ = '-';
+ if (*p) { /*if country tag is given*/
+ if (*p++ != '-') goto end;
+
+ for (i=0; i<XSLTMAX_ISO3166CNTRYLEN && ISALPHA(*p); ++i)
+ *q++ = TOUPPER(*p++);
+ if (i == 0 || *p) goto end;
+
+ *q = '\0';
+ locale = xslt_locale_WINAPI(localeName);
+ if (locale != (xsltLocale)0) goto end;
+ }
+ /* Try to find most common country for language */
+ region = xsltDefaultRegion(localeName);
+ if (region == NULL) goto end;
+
+ strcpy(localeName + llen + 1, region);
+ locale = xslt_locale_WINAPI(localeName);
+end:
+ return(locale);
+}
+#endif
+
+#ifdef XSLT_LOCALE_NONE
+ return(NULL);
+#endif
+}
+
+static const xmlChar*
+xsltDefaultRegion(const xmlChar *localeName) {
+ xmlChar c;
+ /* region should be xmlChar, but gcc warns on all string assignments */
+ const char *region = NULL;
+
+ c = localeName[1];
+ /* This is based on the locales from glibc 2.3.3 */
+
+ switch (localeName[0]) {
+ case 'a':
+ if (c == 'a' || c == 'm') region = "ET";
+ else if (c == 'f') region = "ZA";
+ else if (c == 'n') region = "ES";
+ else if (c == 'r') region = "AE";
+ else if (c == 'z') region = "AZ";
+ break;
+ case 'b':
+ if (c == 'e') region = "BY";
+ else if (c == 'g') region = "BG";
+ else if (c == 'n') region = "BD";
+ else if (c == 'r') region = "FR";
+ else if (c == 's') region = "BA";
+ break;
+ case 'c':
+ if (c == 'a') region = "ES";
+ else if (c == 's') region = "CZ";
+ else if (c == 'y') region = "GB";
+ break;
+ case 'd':
+ if (c == 'a') region = "DK";
+ else if (c == 'e') region = "DE";
+ break;
+ case 'e':
+ if (c == 'l') region = "GR";
+ else if (c == 'n' || c == 'o') region = "US";
+ else if (c == 's' || c == 'u') region = "ES";
+ else if (c == 't') region = "EE";
+ break;
+ case 'f':
+ if (c == 'a') region = "IR";
+ else if (c == 'i') region = "FI";
+ else if (c == 'o') region = "FO";
+ else if (c == 'r') region = "FR";
+ break;
+ case 'g':
+ if (c == 'a') region = "IE";
+ else if (c == 'l') region = "ES";
+ else if (c == 'v') region = "GB";
+ break;
+ case 'h':
+ if (c == 'e') region = "IL";
+ else if (c == 'i') region = "IN";
+ else if (c == 'r') region = "HT";
+ else if (c == 'u') region = "HU";
+ break;
+ case 'i':
+ if (c == 'd') region = "ID";
+ else if (c == 's') region = "IS";
+ else if (c == 't') region = "IT";
+ else if (c == 'w') region = "IL";
+ break;
+ case 'j':
+ if (c == 'a') region = "JP";
+ break;
+ case 'k':
+ if (c == 'l') region = "GL";
+ else if (c == 'o') region = "KR";
+ else if (c == 'w') region = "GB";
+ break;
+ case 'l':
+ if (c == 't') region = "LT";
+ else if (c == 'v') region = "LV";
+ break;
+ case 'm':
+ if (c == 'k') region = "MK";
+ else if (c == 'l' || c == 'r') region = "IN";
+ else if (c == 'n') region = "MN";
+ else if (c == 's') region = "MY";
+ else if (c == 't') region = "MT";
+ break;
+ case 'n':
+ if (c == 'b' || c == 'n' || c == 'o') region = "NO";
+ else if (c == 'e') region = "NP";
+ else if (c == 'l') region = "NL";
+ break;
+ case 'o':
+ if (c == 'm') region = "ET";
+ break;
+ case 'p':
+ if (c == 'a') region = "IN";
+ else if (c == 'l') region = "PL";
+ else if (c == 't') region = "PT";
+ break;
+ case 'r':
+ if (c == 'o') region = "RO";
+ else if (c == 'u') region = "RU";
+ break;
+ case 's':
+ switch (c) {
+ case 'e': region = "NO"; break;
+ case 'h': region = "YU"; break;
+ case 'k': region = "SK"; break;
+ case 'l': region = "SI"; break;
+ case 'o': region = "ET"; break;
+ case 'q': region = "AL"; break;
+ case 't': region = "ZA"; break;
+ case 'v': region = "SE"; break;
+ }
+ break;
+ case 't':
+ if (c == 'a' || c == 'e') region = "IN";
+ else if (c == 'h') region = "TH";
+ else if (c == 'i') region = "ER";
+ else if (c == 'r') region = "TR";
+ else if (c == 't') region = "RU";
+ break;
+ case 'u':
+ if (c == 'k') region = "UA";
+ else if (c == 'r') region = "PK";
+ break;
+ case 'v':
+ if (c == 'i') region = "VN";
+ break;
+ case 'w':
+ if (c == 'a') region = "BE";
+ break;
+ case 'x':
+ if (c == 'h') region = "ZA";
+ break;
+ case 'z':
+ if (c == 'h') region = "CN";
+ else if (c == 'u') region = "ZA";
+ break;
+ }
+ return((xmlChar *)region);
+}
+
+/**
+ * xsltFreeLocale:
+ * @locale: the locale to free
+ *
+ * Frees a locale created with xsltNewLocale
+ */
+void
+xsltFreeLocale(xsltLocale locale) {
+#ifdef XSLT_LOCALE_XLOCALE
+ freelocale(locale);
+#endif
+}
+
+/**
+ * xsltStrxfrm:
+ * @locale: locale created with xsltNewLocale
+ * @string: UTF-8 string to transform
+ *
+ * Transforms a string according to locale. The transformed string must then be
+ * compared with xsltLocaleStrcmp and freed with xmlFree.
+ *
+ * Returns the transformed string or NULL on error
+ */
+xsltLocaleChar *
+xsltStrxfrm(xsltLocale locale, const xmlChar *string)
+{
+#ifdef XSLT_LOCALE_NONE
+ return(NULL);
+#else
+ size_t xstrlen, r;
+ xsltLocaleChar *xstr;
+
+#ifdef XSLT_LOCALE_XLOCALE
+ xstrlen = strxfrm_l(NULL, (const char *)string, 0, locale) + 1;
+ xstr = (xsltLocaleChar *) xmlMalloc(xstrlen);
+ if (xstr == NULL) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltStrxfrm : out of memory error\n");
+ return(NULL);
+ }
+
+ r = strxfrm_l((char *)xstr, (const char *)string, xstrlen, locale);
+#endif
+
+#ifdef XSLT_LOCALE_WINAPI
+ xstrlen = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0);
+ if (xstrlen == 0) {
+ xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : MultiByteToWideChar check failed\n");
+ return(NULL);
+ }
+ xstr = (xsltLocaleChar*) xmlMalloc(xstrlen * sizeof(xsltLocaleChar));
+ if (xstr == NULL) {
+ xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : out of memory\n");
+ return(NULL);
+ }
+ r = MultiByteToWideChar(CP_UTF8, 0, string, -1, xstr, xstrlen);
+ if (r == 0) {
+ xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : MultiByteToWideChar failed\n");
+ xmlFree(xstr);
+ return(NULL);
+ }
+ return(xstr);
+#endif /* XSLT_LOCALE_WINAPI */
+
+ if (r >= xstrlen) {
+ xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : strxfrm failed\n");
+ xmlFree(xstr);
+ return(NULL);
+ }
+
+ return(xstr);
+#endif /* XSLT_LOCALE_NONE */
+}
+
+/**
+ * xsltLocaleStrcmp:
+ * @locale: a locale identifier
+ * @str1: a string transformed with xsltStrxfrm
+ * @str2: a string transformed with xsltStrxfrm
+ *
+ * Compares two strings transformed with xsltStrxfrm
+ *
+ * Returns a value < 0 if str1 sorts before str2,
+ * a value > 0 if str1 sorts after str2,
+ * 0 if str1 and str2 are equal wrt sorting
+ */
+int
+xsltLocaleStrcmp(xsltLocale locale, const xsltLocaleChar *str1, const xsltLocaleChar *str2) {
+ (void)locale;
+#ifdef XSLT_LOCALE_WINAPI
+{
+ int ret;
+ if (str1 == str2) return(0);
+ if (str1 == NULL) return(-1);
+ if (str2 == NULL) return(1);
+ ret = CompareStringW(locale, 0, str1, -1, str2, -1);
+ if (ret == 0) {
+ xsltTransformError(NULL, NULL, NULL, "xsltLocaleStrcmp : CompareStringW fail\n");
+ return(0);
+ }
+ return(ret - 2);
+}
+#else
+ return(xmlStrcmp(str1, str2));
+#endif
+}
+
+#ifdef XSLT_LOCALE_WINAPI
+/**
+ * xsltCountSupportedLocales:
+ * @lcid: not used
+ *
+ * callback used to count locales
+ *
+ * Returns TRUE
+ */
+BOOL CALLBACK
+xsltCountSupportedLocales(LPSTR lcid) {
+ (void) lcid;
+ ++xsltLocaleListSize;
+ return(TRUE);
+}
+
+/**
+ * xsltIterateSupportedLocales:
+ * @lcid: not used
+ *
+ * callback used to track locales
+ *
+ * Returns TRUE if not at the end of the array
+ */
+BOOL CALLBACK
+xsltIterateSupportedLocales(LPSTR lcid) {
+ static int count = 0;
+ xmlChar iso639lang [XSLTMAX_ISO639LANGLEN +1];
+ xmlChar iso3136ctry[XSLTMAX_ISO3166CNTRYLEN+1];
+ int k, l;
+ xsltRFC1766Info *p = xsltLocaleList + count;
+
+ k = sscanf(lcid, "%lx", (long*)&p->lcid);
+ if (k < 1) goto end;
+ /*don't count terminating null character*/
+ k = GetLocaleInfoA(p->lcid, LOCALE_SISO639LANGNAME , iso639lang , sizeof(iso639lang ));
+ if (--k < 1) goto end;
+ l = GetLocaleInfoA(p->lcid, LOCALE_SISO3166CTRYNAME, iso3136ctry, sizeof(iso3136ctry));
+ if (--l < 1) goto end;
+
+ { /*fill results*/
+ xmlChar *q = p->tag;
+ memcpy(q, iso639lang, k);
+ q += k;
+ *q++ = '-';
+ memcpy(q, iso3136ctry, l);
+ q += l;
+ *q = '\0';
+ }
+ ++count;
+end:
+ return((count < xsltLocaleListSize) ? TRUE : FALSE);
+}
+
+
+static void
+xsltEnumSupportedLocales(void) {
+ xmlRMutexLock(xsltLocaleMutex);
+ if (xsltLocaleListSize <= 0) {
+ size_t len;
+
+ EnumSystemLocalesA(xsltCountSupportedLocales, LCID_SUPPORTED);
+
+ len = xsltLocaleListSize * sizeof(xsltRFC1766Info);
+ xsltLocaleList = xmlMalloc(len);
+ memset(xsltLocaleList, 0, len);
+ EnumSystemLocalesA(xsltIterateSupportedLocales, LCID_SUPPORTED);
+ }
+ xmlRMutexUnlock(xsltLocaleMutex);
+}
+
+#endif /*def XSLT_LOCALE_WINAPI*/
diff --git a/libxslt/xsltlocale.h b/libxslt/xsltlocale.h
new file mode 100644
index 0000000..8a9ca15
--- /dev/null
+++ b/libxslt/xsltlocale.h
@@ -0,0 +1,67 @@
+/*
+ * Summary: Locale handling
+ * Description: Interfaces for locale handling. Needed for language dependent
+ * sorting.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Nick Wellnhofer
+ */
+
+#ifndef __XML_XSLTLOCALE_H__
+#define __XML_XSLTLOCALE_H__
+
+#include <libxml/xmlstring.h>
+#include "xsltexports.h"
+
+#ifdef XSLT_LOCALE_XLOCALE
+
+#include <locale.h>
+#include <xlocale.h>
+
+#ifdef __GLIBC__
+/*locale_t is defined only if _GNU_SOURCE is defined*/
+typedef __locale_t xsltLocale;
+#else
+typedef locale_t xsltLocale;
+#endif
+typedef xmlChar xsltLocaleChar;
+
+#elif defined(XSLT_LOCALE_WINAPI)
+
+#include <windows.h>
+#include <winnls.h>
+
+typedef LCID xsltLocale;
+typedef wchar_t xsltLocaleChar;
+
+#else
+
+/*
+ * XSLT_LOCALE_NONE:
+ * Macro indicating that locale are not supported
+ */
+#ifndef XSLT_LOCALE_NONE
+#define XSLT_LOCALE_NONE
+#endif
+
+typedef void *xsltLocale;
+typedef xmlChar xsltLocaleChar;
+
+#endif
+
+XSLTPUBFUN xsltLocale XSLTCALL
+ xsltNewLocale (const xmlChar *langName);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeLocale (xsltLocale locale);
+XSLTPUBFUN xsltLocaleChar * XSLTCALL
+ xsltStrxfrm (xsltLocale locale,
+ const xmlChar *string);
+XSLTPUBFUN int XSLTCALL
+ xsltLocaleStrcmp (xsltLocale locale,
+ const xsltLocaleChar *str1,
+ const xsltLocaleChar *str2);
+XSLTPUBFUN void XSLTCALL
+ xsltFreeLocales (void);
+
+#endif /* __XML_XSLTLOCALE_H__ */
diff --git a/libxslt/xsltutils.c b/libxslt/xsltutils.c
new file mode 100644
index 0000000..c250ccf
--- /dev/null
+++ b/libxslt/xsltutils.c
@@ -0,0 +1,2483 @@
+/*
+ * xsltutils.c: Utilities for the XSL Transformation 1.0 engine
+ *
+ * Reference:
+ * http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
+
+#ifndef XSLT_NEED_TRIO
+#include <stdio.h>
+#else
+#include <trio.h>
+#endif
+
+#include <string.h>
+#include <time.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <stdarg.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/HTMLtree.h>
+#include <libxml/xmlerror.h>
+#include <libxml/xmlIO.h>
+#include "xsltutils.h"
+#include "templates.h"
+#include "xsltInternals.h"
+#include "imports.h"
+#include "transform.h"
+
+/* gettimeofday on Windows ??? */
+#if defined(WIN32) && !defined(__CYGWIN__)
+#ifdef _MSC_VER
+#include <winsock2.h>
+#pragma comment(lib, "ws2_32.lib")
+#define gettimeofday(p1,p2)
+#define HAVE_GETTIMEOFDAY
+#define XSLT_WIN32_PERFORMANCE_COUNTER
+#endif /* _MS_VER */
+#endif /* WIN32 */
+
+/************************************************************************
+ * *
+ * Convenience function *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltGetCNsProp:
+ * @style: the stylesheet
+ * @node: the node
+ * @name: the attribute name
+ * @nameSpace: the URI of the namespace
+ *
+ * Similar to xmlGetNsProp() but with a slightly different semantic
+ *
+ * Search and get the value of an attribute associated to a node
+ * This attribute has to be anchored in the namespace specified,
+ * or has no namespace and the element is in that namespace.
+ *
+ * This does the entity substitution.
+ * This function looks in DTD attribute declaration for #FIXED or
+ * default declaration values unless DTD use has been turned off.
+ *
+ * Returns the attribute value or NULL if not found. The string is allocated
+ * in the stylesheet dictionary.
+ */
+const xmlChar *
+xsltGetCNsProp(xsltStylesheetPtr style, xmlNodePtr node,
+ const xmlChar *name, const xmlChar *nameSpace) {
+ xmlAttrPtr prop;
+ xmlDocPtr doc;
+ xmlNsPtr ns;
+ xmlChar *tmp;
+ const xmlChar *ret;
+
+ if ((node == NULL) || (style == NULL) || (style->dict == NULL))
+ return(NULL);
+
+ if (nameSpace == NULL)
+ return xmlGetProp(node, name);
+
+ if (node->type == XML_NAMESPACE_DECL)
+ return(NULL);
+ if (node->type == XML_ELEMENT_NODE)
+ prop = node->properties;
+ else
+ prop = NULL;
+ while (prop != NULL) {
+ /*
+ * One need to have
+ * - same attribute names
+ * - and the attribute carrying that namespace
+ */
+ if ((xmlStrEqual(prop->name, name)) &&
+ (((prop->ns == NULL) && (node->ns != NULL) &&
+ (xmlStrEqual(node->ns->href, nameSpace))) ||
+ ((prop->ns != NULL) &&
+ (xmlStrEqual(prop->ns->href, nameSpace))))) {
+
+ tmp = xmlNodeListGetString(node->doc, prop->children, 1);
+ if (tmp == NULL)
+ ret = xmlDictLookup(style->dict, BAD_CAST "", 0);
+ else {
+ ret = xmlDictLookup(style->dict, tmp, -1);
+ xmlFree(tmp);
+ }
+ return ret;
+ }
+ prop = prop->next;
+ }
+ tmp = NULL;
+ /*
+ * Check if there is a default declaration in the internal
+ * or external subsets
+ */
+ doc = node->doc;
+ if (doc != NULL) {
+ if (doc->intSubset != NULL) {
+ xmlAttributePtr attrDecl;
+
+ attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
+ if ((attrDecl == NULL) && (doc->extSubset != NULL))
+ attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
+
+ if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) {
+ /*
+ * The DTD declaration only allows a prefix search
+ */
+ ns = xmlSearchNs(doc, node, attrDecl->prefix);
+ if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace)))
+ return(xmlDictLookup(style->dict,
+ attrDecl->defaultValue, -1));
+ }
+ }
+ }
+ return(NULL);
+}
+/**
+ * xsltGetNsProp:
+ * @node: the node
+ * @name: the attribute name
+ * @nameSpace: the URI of the namespace
+ *
+ * Similar to xmlGetNsProp() but with a slightly different semantic
+ *
+ * Search and get the value of an attribute associated to a node
+ * This attribute has to be anchored in the namespace specified,
+ * or has no namespace and the element is in that namespace.
+ *
+ * This does the entity substitution.
+ * This function looks in DTD attribute declaration for #FIXED or
+ * default declaration values unless DTD use has been turned off.
+ *
+ * Returns the attribute value or NULL if not found.
+ * It's up to the caller to free the memory.
+ */
+xmlChar *
+xsltGetNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) {
+ xmlAttrPtr prop;
+ xmlDocPtr doc;
+ xmlNsPtr ns;
+
+ if (node == NULL)
+ return(NULL);
+
+ if (nameSpace == NULL)
+ return xmlGetProp(node, name);
+
+ if (node->type == XML_NAMESPACE_DECL)
+ return(NULL);
+ if (node->type == XML_ELEMENT_NODE)
+ prop = node->properties;
+ else
+ prop = NULL;
+ /*
+ * TODO: Substitute xmlGetProp() for xmlGetNsProp(), since the former
+ * is not namespace-aware and will return an attribute with equal
+ * name regardless of its namespace.
+ * Example:
+ * <xsl:element foo:name="myName"/>
+ * So this would return "myName" even if an attribute @name
+ * in the XSLT was requested.
+ */
+ while (prop != NULL) {
+ /*
+ * One need to have
+ * - same attribute names
+ * - and the attribute carrying that namespace
+ */
+ if ((xmlStrEqual(prop->name, name)) &&
+ (((prop->ns == NULL) && (node->ns != NULL) &&
+ (xmlStrEqual(node->ns->href, nameSpace))) ||
+ ((prop->ns != NULL) &&
+ (xmlStrEqual(prop->ns->href, nameSpace))))) {
+ xmlChar *ret;
+
+ ret = xmlNodeListGetString(node->doc, prop->children, 1);
+ if (ret == NULL) return(xmlStrdup((xmlChar *)""));
+ return(ret);
+ }
+ prop = prop->next;
+ }
+
+ /*
+ * Check if there is a default declaration in the internal
+ * or external subsets
+ */
+ doc = node->doc;
+ if (doc != NULL) {
+ if (doc->intSubset != NULL) {
+ xmlAttributePtr attrDecl;
+
+ attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
+ if ((attrDecl == NULL) && (doc->extSubset != NULL))
+ attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
+
+ if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) {
+ /*
+ * The DTD declaration only allows a prefix search
+ */
+ ns = xmlSearchNs(doc, node, attrDecl->prefix);
+ if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace)))
+ return(xmlStrdup(attrDecl->defaultValue));
+ }
+ }
+ }
+ return(NULL);
+}
+
+/**
+ * xsltGetUTF8Char:
+ * @utf: a sequence of UTF-8 encoded bytes
+ * @len: a pointer to @bytes len
+ *
+ * Read one UTF8 Char from @utf
+ * Function copied from libxml2 xmlGetUTF8Char() ... to discard ultimately
+ * and use the original API
+ *
+ * Returns the char value or -1 in case of error and update @len with the
+ * number of bytes used
+ */
+int
+xsltGetUTF8Char(const unsigned char *utf, int *len) {
+ unsigned int c;
+
+ if (utf == NULL)
+ goto error;
+ if (len == NULL)
+ goto error;
+ if (*len < 1)
+ goto error;
+
+ c = utf[0];
+ if (c & 0x80) {
+ if (*len < 2)
+ goto error;
+ if ((utf[1] & 0xc0) != 0x80)
+ goto error;
+ if ((c & 0xe0) == 0xe0) {
+ if (*len < 3)
+ goto error;
+ if ((utf[2] & 0xc0) != 0x80)
+ goto error;
+ if ((c & 0xf0) == 0xf0) {
+ if (*len < 4)
+ goto error;
+ if ((c & 0xf8) != 0xf0 || (utf[3] & 0xc0) != 0x80)
+ goto error;
+ *len = 4;
+ /* 4-byte code */
+ c = (utf[0] & 0x7) << 18;
+ c |= (utf[1] & 0x3f) << 12;
+ c |= (utf[2] & 0x3f) << 6;
+ c |= utf[3] & 0x3f;
+ } else {
+ /* 3-byte code */
+ *len = 3;
+ c = (utf[0] & 0xf) << 12;
+ c |= (utf[1] & 0x3f) << 6;
+ c |= utf[2] & 0x3f;
+ }
+ } else {
+ /* 2-byte code */
+ *len = 2;
+ c = (utf[0] & 0x1f) << 6;
+ c |= utf[1] & 0x3f;
+ }
+ } else {
+ /* 1-byte code */
+ *len = 1;
+ }
+ return(c);
+
+error:
+ if (len != NULL)
+ *len = 0;
+ return(-1);
+}
+
+#ifdef XSLT_REFACTORED
+
+/**
+ * xsltPointerListAddSize:
+ * @list: the pointer list structure
+ * @item: the item to be stored
+ * @initialSize: the initial size of the list
+ *
+ * Adds an item to the list.
+ *
+ * Returns the position of the added item in the list or
+ * -1 in case of an error.
+ */
+int
+xsltPointerListAddSize(xsltPointerListPtr list,
+ void *item,
+ int initialSize)
+{
+ if (list->items == NULL) {
+ if (initialSize <= 0)
+ initialSize = 1;
+ list->items = (void **) xmlMalloc(
+ initialSize * sizeof(void *));
+ if (list->items == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltPointerListAddSize: memory allocation failure.\n");
+ return(-1);
+ }
+ list->number = 0;
+ list->size = initialSize;
+ } else if (list->size <= list->number) {
+ list->size *= 2;
+ list->items = (void **) xmlRealloc(list->items,
+ list->size * sizeof(void *));
+ if (list->items == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltPointerListAddSize: memory re-allocation failure.\n");
+ list->size = 0;
+ return(-1);
+ }
+ }
+ list->items[list->number++] = item;
+ return(0);
+}
+
+/**
+ * xsltPointerListCreate:
+ * @initialSize: the initial size for the list
+ *
+ * Creates an xsltPointerList structure.
+ *
+ * Returns a xsltPointerList structure or NULL in case of an error.
+ */
+xsltPointerListPtr
+xsltPointerListCreate(int initialSize)
+{
+ xsltPointerListPtr ret;
+
+ ret = xmlMalloc(sizeof(xsltPointerList));
+ if (ret == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltPointerListCreate: memory allocation failure.\n");
+ return (NULL);
+ }
+ memset(ret, 0, sizeof(xsltPointerList));
+ if (initialSize > 0) {
+ xsltPointerListAddSize(ret, NULL, initialSize);
+ ret->number = 0;
+ }
+ return (ret);
+}
+
+/**
+ * xsltPointerListFree:
+ * @list: pointer to the list to be freed
+ *
+ * Frees the xsltPointerList structure. This does not free
+ * the content of the list.
+ */
+void
+xsltPointerListFree(xsltPointerListPtr list)
+{
+ if (list == NULL)
+ return;
+ if (list->items != NULL)
+ xmlFree(list->items);
+ xmlFree(list);
+}
+
+/**
+ * xsltPointerListClear:
+ * @list: pointer to the list to be cleared
+ *
+ * Resets the list, but does not free the allocated array
+ * and does not free the content of the list.
+ */
+void
+xsltPointerListClear(xsltPointerListPtr list)
+{
+ if (list->items != NULL) {
+ xmlFree(list->items);
+ list->items = NULL;
+ }
+ list->number = 0;
+ list->size = 0;
+}
+
+#endif /* XSLT_REFACTORED */
+
+/************************************************************************
+ * *
+ * Handling of XSLT stylesheets messages *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltMessage:
+ * @ctxt: an XSLT processing context
+ * @node: The current node
+ * @inst: The node containing the message instruction
+ *
+ * Process and xsl:message construct
+ */
+void
+xsltMessage(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst) {
+ xmlGenericErrorFunc error = xsltGenericError;
+ void *errctx = xsltGenericErrorContext;
+ xmlChar *prop, *message;
+ int terminate = 0;
+
+ if ((ctxt == NULL) || (inst == NULL))
+ return;
+
+ if (ctxt->error != NULL) {
+ error = ctxt->error;
+ errctx = ctxt->errctx;
+ }
+
+ prop = xmlGetNsProp(inst, (const xmlChar *)"terminate", NULL);
+ if (prop != NULL) {
+ if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
+ terminate = 1;
+ } else if (xmlStrEqual(prop, (const xmlChar *)"no")) {
+ terminate = 0;
+ } else {
+ xsltTransformError(ctxt, NULL, inst,
+ "xsl:message : terminate expecting 'yes' or 'no'\n");
+ }
+ xmlFree(prop);
+ }
+ message = xsltEvalTemplateString(ctxt, node, inst);
+ if (message != NULL) {
+ int len = xmlStrlen(message);
+
+ error(errctx, "%s", (const char *)message);
+ if ((len > 0) && (message[len - 1] != '\n'))
+ error(errctx, "\n");
+ xmlFree(message);
+ }
+ if (terminate)
+ ctxt->state = XSLT_STATE_STOPPED;
+}
+
+/************************************************************************
+ * *
+ * Handling of out of context errors *
+ * *
+ ************************************************************************/
+
+#define XSLT_GET_VAR_STR(msg, str) { \
+ int size; \
+ int chars; \
+ char *larger; \
+ va_list ap; \
+ \
+ str = (char *) xmlMalloc(150); \
+ if (str == NULL) \
+ return; \
+ \
+ size = 150; \
+ \
+ while (size < 64000) { \
+ va_start(ap, msg); \
+ chars = vsnprintf(str, size, msg, ap); \
+ va_end(ap); \
+ if ((chars > -1) && (chars < size)) \
+ break; \
+ if (chars > -1) \
+ size += chars + 1; \
+ else \
+ size += 100; \
+ if ((larger = (char *) xmlRealloc(str, size)) == NULL) {\
+ xmlFree(str); \
+ return; \
+ } \
+ str = larger; \
+ } \
+}
+/**
+ * xsltGenericErrorDefaultFunc:
+ * @ctx: an error context
+ * @msg: the message to display/transmit
+ * @...: extra parameters for the message display
+ *
+ * Default handler for out of context error messages.
+ */
+static void LIBXSLT_ATTR_FORMAT(2,3)
+xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
+ va_list args;
+
+ if (xsltGenericErrorContext == NULL)
+ xsltGenericErrorContext = (void *) stderr;
+
+ va_start(args, msg);
+ vfprintf((FILE *)xsltGenericErrorContext, msg, args);
+ va_end(args);
+}
+
+xmlGenericErrorFunc xsltGenericError = xsltGenericErrorDefaultFunc;
+void *xsltGenericErrorContext = NULL;
+
+
+/**
+ * xsltSetGenericErrorFunc:
+ * @ctx: the new error handling context
+ * @handler: the new handler function
+ *
+ * Function to reset the handler and the error context for out of
+ * context error messages.
+ * This simply means that @handler will be called for subsequent
+ * error messages while not parsing nor validating. And @ctx will
+ * be passed as first argument to @handler
+ * One can simply force messages to be emitted to another FILE * than
+ * stderr by setting @ctx to this file handle and @handler to NULL.
+ */
+void
+xsltSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) {
+ xsltGenericErrorContext = ctx;
+ if (handler != NULL)
+ xsltGenericError = handler;
+ else
+ xsltGenericError = xsltGenericErrorDefaultFunc;
+}
+
+/**
+ * xsltGenericDebugDefaultFunc:
+ * @ctx: an error context
+ * @msg: the message to display/transmit
+ * @...: extra parameters for the message display
+ *
+ * Default handler for out of context error messages.
+ */
+static void LIBXSLT_ATTR_FORMAT(2,3)
+xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
+ va_list args;
+
+ if (xsltGenericDebugContext == NULL)
+ return;
+
+ va_start(args, msg);
+ vfprintf((FILE *)xsltGenericDebugContext, msg, args);
+ va_end(args);
+}
+
+xmlGenericErrorFunc xsltGenericDebug = xsltGenericDebugDefaultFunc;
+void *xsltGenericDebugContext = NULL;
+
+
+/**
+ * xsltSetGenericDebugFunc:
+ * @ctx: the new error handling context
+ * @handler: the new handler function
+ *
+ * Function to reset the handler and the error context for out of
+ * context error messages.
+ * This simply means that @handler will be called for subsequent
+ * error messages while not parsing or validating. And @ctx will
+ * be passed as first argument to @handler
+ * One can simply force messages to be emitted to another FILE * than
+ * stderr by setting @ctx to this file handle and @handler to NULL.
+ */
+void
+xsltSetGenericDebugFunc(void *ctx, xmlGenericErrorFunc handler) {
+ xsltGenericDebugContext = ctx;
+ if (handler != NULL)
+ xsltGenericDebug = handler;
+ else
+ xsltGenericDebug = xsltGenericDebugDefaultFunc;
+}
+
+/**
+ * xsltPrintErrorContext:
+ * @ctxt: the transformation context
+ * @style: the stylesheet
+ * @node: the current node being processed
+ *
+ * Display the context of an error.
+ */
+void
+xsltPrintErrorContext(xsltTransformContextPtr ctxt,
+ xsltStylesheetPtr style, xmlNodePtr node) {
+ int line = 0;
+ const xmlChar *file = NULL;
+ const xmlChar *name = NULL;
+ const char *type = "error";
+ xmlGenericErrorFunc error = xsltGenericError;
+ void *errctx = xsltGenericErrorContext;
+
+ if (ctxt != NULL) {
+ if (ctxt->state == XSLT_STATE_OK)
+ ctxt->state = XSLT_STATE_ERROR;
+ if (ctxt->error != NULL) {
+ error = ctxt->error;
+ errctx = ctxt->errctx;
+ }
+ }
+ if ((node == NULL) && (ctxt != NULL))
+ node = ctxt->inst;
+
+ if (node != NULL) {
+ if ((node->type == XML_DOCUMENT_NODE) ||
+ (node->type == XML_HTML_DOCUMENT_NODE)) {
+ xmlDocPtr doc = (xmlDocPtr) node;
+
+ file = doc->URL;
+ } else {
+ line = xmlGetLineNo(node);
+ if ((node->doc != NULL) && (node->doc->URL != NULL))
+ file = node->doc->URL;
+ if (node->name != NULL)
+ name = node->name;
+ }
+ }
+
+ if (ctxt != NULL)
+ type = "runtime error";
+ else if (style != NULL) {
+#ifdef XSLT_REFACTORED
+ if (XSLT_CCTXT(style)->errSeverity == XSLT_ERROR_SEVERITY_WARNING)
+ type = "compilation warning";
+ else
+ type = "compilation error";
+#else
+ type = "compilation error";
+#endif
+ }
+
+ if ((file != NULL) && (line != 0) && (name != NULL))
+ error(errctx, "%s: file %s line %d element %s\n",
+ type, file, line, name);
+ else if ((file != NULL) && (name != NULL))
+ error(errctx, "%s: file %s element %s\n", type, file, name);
+ else if ((file != NULL) && (line != 0))
+ error(errctx, "%s: file %s line %d\n", type, file, line);
+ else if (file != NULL)
+ error(errctx, "%s: file %s\n", type, file);
+ else if (name != NULL)
+ error(errctx, "%s: element %s\n", type, name);
+ else
+ error(errctx, "%s\n", type);
+}
+
+/**
+ * xsltSetTransformErrorFunc:
+ * @ctxt: the XSLT transformation context
+ * @ctx: the new error handling context
+ * @handler: the new handler function
+ *
+ * Function to reset the handler and the error context for out of
+ * context error messages specific to a given XSLT transromation.
+ *
+ * This simply means that @handler will be called for subsequent
+ * error messages while running the transformation.
+ */
+void
+xsltSetTransformErrorFunc(xsltTransformContextPtr ctxt,
+ void *ctx, xmlGenericErrorFunc handler)
+{
+ ctxt->error = handler;
+ ctxt->errctx = ctx;
+}
+
+/**
+ * xsltTransformError:
+ * @ctxt: an XSLT transformation context
+ * @style: the XSLT stylesheet used
+ * @node: the current node in the stylesheet
+ * @msg: the message to display/transmit
+ * @...: extra parameters for the message display
+ *
+ * Display and format an error messages, gives file, line, position and
+ * extra parameters, will use the specific transformation context if available
+ */
+void
+xsltTransformError(xsltTransformContextPtr ctxt,
+ xsltStylesheetPtr style,
+ xmlNodePtr node,
+ const char *msg, ...) {
+ xmlGenericErrorFunc error = xsltGenericError;
+ void *errctx = xsltGenericErrorContext;
+ char * str;
+
+ if (ctxt != NULL) {
+ if (ctxt->state == XSLT_STATE_OK)
+ ctxt->state = XSLT_STATE_ERROR;
+ if (ctxt->error != NULL) {
+ error = ctxt->error;
+ errctx = ctxt->errctx;
+ }
+ }
+ if ((node == NULL) && (ctxt != NULL))
+ node = ctxt->inst;
+ xsltPrintErrorContext(ctxt, style, node);
+ XSLT_GET_VAR_STR(msg, str);
+ error(errctx, "%s", str);
+ if (str != NULL)
+ xmlFree(str);
+}
+
+/************************************************************************
+ * *
+ * QNames *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltSplitQName:
+ * @dict: a dictionary
+ * @name: the full QName
+ * @prefix: the return value
+ *
+ * Split QNames into prefix and local names, both allocated from a dictionary.
+ *
+ * Returns: the localname or NULL in case of error.
+ */
+const xmlChar *
+xsltSplitQName(xmlDictPtr dict, const xmlChar *name, const xmlChar **prefix) {
+ int len = 0;
+ const xmlChar *ret = NULL;
+
+ *prefix = NULL;
+ if ((name == NULL) || (dict == NULL)) return(NULL);
+ if (name[0] == ':')
+ return(xmlDictLookup(dict, name, -1));
+ while ((name[len] != 0) && (name[len] != ':')) len++;
+ if (name[len] == 0) return(xmlDictLookup(dict, name, -1));
+ *prefix = xmlDictLookup(dict, name, len);
+ ret = xmlDictLookup(dict, &name[len + 1], -1);
+ return(ret);
+}
+
+/**
+ * xsltGetQNameURI:
+ * @node: the node holding the QName
+ * @name: pointer to the initial QName value
+ *
+ * This function analyzes @name, if the name contains a prefix,
+ * the function seaches the associated namespace in scope for it.
+ * It will also replace @name value with the NCName, the old value being
+ * freed.
+ * Errors in the prefix lookup are signalled by setting @name to NULL.
+ *
+ * NOTE: the namespace returned is a pointer to the place where it is
+ * defined and hence has the same lifespan as the document holding it.
+ *
+ * Returns the namespace URI if there is a prefix, or NULL if @name is
+ * not prefixed.
+ */
+const xmlChar *
+xsltGetQNameURI(xmlNodePtr node, xmlChar ** name)
+{
+ int len = 0;
+ xmlChar *qname;
+ xmlNsPtr ns;
+
+ if (name == NULL)
+ return(NULL);
+ qname = *name;
+ if ((qname == NULL) || (*qname == 0))
+ return(NULL);
+ if (node == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "QName: no element for namespace lookup %s\n",
+ qname);
+ xmlFree(qname);
+ *name = NULL;
+ return(NULL);
+ }
+
+ /* nasty but valid */
+ if (qname[0] == ':')
+ return(NULL);
+
+ /*
+ * we are not trying to validate but just to cut, and yes it will
+ * work even if this is a set of UTF-8 encoded chars
+ */
+ while ((qname[len] != 0) && (qname[len] != ':'))
+ len++;
+
+ if (qname[len] == 0)
+ return(NULL);
+
+ /*
+ * handle xml: separately, this one is magical
+ */
+ if ((qname[0] == 'x') && (qname[1] == 'm') &&
+ (qname[2] == 'l') && (qname[3] == ':')) {
+ if (qname[4] == 0)
+ return(NULL);
+ *name = xmlStrdup(&qname[4]);
+ xmlFree(qname);
+ return(XML_XML_NAMESPACE);
+ }
+
+ qname[len] = 0;
+ ns = xmlSearchNs(node->doc, node, qname);
+ if (ns == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "%s:%s : no namespace bound to prefix %s\n",
+ qname, &qname[len + 1], qname);
+ *name = NULL;
+ xmlFree(qname);
+ return(NULL);
+ }
+ *name = xmlStrdup(&qname[len + 1]);
+ xmlFree(qname);
+ return(ns->href);
+}
+
+/**
+ * xsltGetQNameURI2:
+ * @style: stylesheet pointer
+ * @node: the node holding the QName
+ * @name: pointer to the initial QName value
+ *
+ * This function is similar to xsltGetQNameURI, but is used when
+ * @name is a dictionary entry.
+ *
+ * Returns the namespace URI if there is a prefix, or NULL if @name is
+ * not prefixed.
+ */
+const xmlChar *
+xsltGetQNameURI2(xsltStylesheetPtr style, xmlNodePtr node,
+ const xmlChar **name) {
+ int len = 0;
+ xmlChar *qname;
+ xmlNsPtr ns;
+
+ if (name == NULL)
+ return(NULL);
+ qname = (xmlChar *)*name;
+ if ((qname == NULL) || (*qname == 0))
+ return(NULL);
+ if (node == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "QName: no element for namespace lookup %s\n",
+ qname);
+ *name = NULL;
+ return(NULL);
+ }
+
+ /*
+ * we are not trying to validate but just to cut, and yes it will
+ * work even if this is a set of UTF-8 encoded chars
+ */
+ while ((qname[len] != 0) && (qname[len] != ':'))
+ len++;
+
+ if (qname[len] == 0)
+ return(NULL);
+
+ /*
+ * handle xml: separately, this one is magical
+ */
+ if ((qname[0] == 'x') && (qname[1] == 'm') &&
+ (qname[2] == 'l') && (qname[3] == ':')) {
+ if (qname[4] == 0)
+ return(NULL);
+ *name = xmlDictLookup(style->dict, &qname[4], -1);
+ return(XML_XML_NAMESPACE);
+ }
+
+ qname = xmlStrndup(*name, len);
+ ns = xmlSearchNs(node->doc, node, qname);
+ if (ns == NULL) {
+ if (style) {
+ xsltTransformError(NULL, style, node,
+ "No namespace bound to prefix '%s'.\n",
+ qname);
+ style->errors++;
+ } else {
+ xsltGenericError(xsltGenericErrorContext,
+ "%s : no namespace bound to prefix %s\n",
+ *name, qname);
+ }
+ *name = NULL;
+ xmlFree(qname);
+ return(NULL);
+ }
+ *name = xmlDictLookup(style->dict, (*name)+len+1, -1);
+ xmlFree(qname);
+ return(ns->href);
+}
+
+/************************************************************************
+ * *
+ * Sorting *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltDocumentSortFunction:
+ * @list: the node set
+ *
+ * reorder the current node list @list accordingly to the document order
+ * This function is slow, obsolete and should not be used anymore.
+ */
+void
+xsltDocumentSortFunction(xmlNodeSetPtr list) {
+ int i, j;
+ int len, tst;
+ xmlNodePtr node;
+
+ if (list == NULL)
+ return;
+ len = list->nodeNr;
+ if (len <= 1)
+ return;
+ /* TODO: sort is really not optimized, does it needs to ? */
+ for (i = 0;i < len -1;i++) {
+ for (j = i + 1; j < len; j++) {
+ tst = xmlXPathCmpNodes(list->nodeTab[i], list->nodeTab[j]);
+ if (tst == -1) {
+ node = list->nodeTab[i];
+ list->nodeTab[i] = list->nodeTab[j];
+ list->nodeTab[j] = node;
+ }
+ }
+ }
+}
+
+/**
+ * xsltComputeSortResult:
+ * @ctxt: a XSLT process context
+ * @sort: node list
+ *
+ * reorder the current node list accordingly to the set of sorting
+ * requirement provided by the array of nodes.
+ *
+ * Returns a ordered XPath nodeset or NULL in case of error.
+ */
+xmlXPathObjectPtr *
+xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemSortPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+ xmlXPathObjectPtr *results = NULL;
+ xmlNodeSetPtr list = NULL;
+ xmlXPathObjectPtr res;
+ int len = 0;
+ int i;
+ xmlNodePtr oldNode;
+ xmlNodePtr oldInst;
+ int oldPos, oldSize ;
+ int oldNsNr;
+ xmlNsPtr *oldNamespaces;
+
+ comp = sort->psvi;
+ if (comp == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsl:sort : compilation failed\n");
+ return(NULL);
+ }
+
+ if ((comp->select == NULL) || (comp->comp == NULL))
+ return(NULL);
+
+ list = ctxt->nodeList;
+ if ((list == NULL) || (list->nodeNr <= 1))
+ return(NULL);
+
+ len = list->nodeNr;
+
+ /* TODO: xsl:sort lang attribute */
+ /* TODO: xsl:sort case-order attribute */
+
+
+ results = xmlMalloc(len * sizeof(xmlXPathObjectPtr));
+ if (results == NULL) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltComputeSortResult: memory allocation failure\n");
+ return(NULL);
+ }
+
+ oldNode = ctxt->node;
+ oldInst = ctxt->inst;
+ oldPos = ctxt->xpathCtxt->proximityPosition;
+ oldSize = ctxt->xpathCtxt->contextSize;
+ oldNsNr = ctxt->xpathCtxt->nsNr;
+ oldNamespaces = ctxt->xpathCtxt->namespaces;
+ for (i = 0;i < len;i++) {
+ ctxt->inst = sort;
+ ctxt->xpathCtxt->contextSize = len;
+ ctxt->xpathCtxt->proximityPosition = i + 1;
+ ctxt->node = list->nodeTab[i];
+ ctxt->xpathCtxt->node = ctxt->node;
+#ifdef XSLT_REFACTORED
+ if (comp->inScopeNs != NULL) {
+ ctxt->xpathCtxt->namespaces = comp->inScopeNs->list;
+ ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber;
+ } else {
+ ctxt->xpathCtxt->namespaces = NULL;
+ ctxt->xpathCtxt->nsNr = 0;
+ }
+#else
+ ctxt->xpathCtxt->namespaces = comp->nsList;
+ ctxt->xpathCtxt->nsNr = comp->nsNr;
+#endif
+ res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
+ if (res != NULL) {
+ if (res->type != XPATH_STRING)
+ res = xmlXPathConvertString(res);
+ if (comp->number)
+ res = xmlXPathConvertNumber(res);
+ res->index = i; /* Save original pos for dupl resolv */
+ if (comp->number) {
+ if (res->type == XPATH_NUMBER) {
+ results[i] = res;
+ } else {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltComputeSortResult: select didn't evaluate to a number\n");
+#endif
+ results[i] = NULL;
+ }
+ } else {
+ if (res->type == XPATH_STRING) {
+ if (comp->locale != (xsltLocale)0) {
+ xmlChar *str = res->stringval;
+ res->stringval = (xmlChar *) xsltStrxfrm(comp->locale, str);
+ xmlFree(str);
+ }
+
+ results[i] = res;
+ } else {
+#ifdef WITH_XSLT_DEBUG_PROCESS
+ xsltGenericDebug(xsltGenericDebugContext,
+ "xsltComputeSortResult: select didn't evaluate to a string\n");
+#endif
+ results[i] = NULL;
+ }
+ }
+ } else {
+ ctxt->state = XSLT_STATE_STOPPED;
+ results[i] = NULL;
+ }
+ }
+ ctxt->node = oldNode;
+ ctxt->inst = oldInst;
+ ctxt->xpathCtxt->contextSize = oldSize;
+ ctxt->xpathCtxt->proximityPosition = oldPos;
+ ctxt->xpathCtxt->nsNr = oldNsNr;
+ ctxt->xpathCtxt->namespaces = oldNamespaces;
+
+ return(results);
+}
+
+/**
+ * xsltDefaultSortFunction:
+ * @ctxt: a XSLT process context
+ * @sorts: array of sort nodes
+ * @nbsorts: the number of sorts in the array
+ *
+ * reorder the current node list accordingly to the set of sorting
+ * requirement provided by the arry of nodes.
+ */
+void
+xsltDefaultSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts,
+ int nbsorts) {
+#ifdef XSLT_REFACTORED
+ xsltStyleItemSortPtr comp;
+#else
+ xsltStylePreCompPtr comp;
+#endif
+ xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT];
+ xmlXPathObjectPtr *results = NULL, *res;
+ xmlNodeSetPtr list = NULL;
+ int descending, number, desc, numb;
+ int len = 0;
+ int i, j, incr;
+ int tst;
+ int depth;
+ xmlNodePtr node;
+ xmlXPathObjectPtr tmp;
+ int tempstype[XSLT_MAX_SORT], temporder[XSLT_MAX_SORT];
+
+ if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) ||
+ (nbsorts >= XSLT_MAX_SORT))
+ return;
+ if (sorts[0] == NULL)
+ return;
+ comp = sorts[0]->psvi;
+ if (comp == NULL)
+ return;
+
+ list = ctxt->nodeList;
+ if ((list == NULL) || (list->nodeNr <= 1))
+ return; /* nothing to do */
+
+ for (j = 0; j < nbsorts; j++) {
+ comp = sorts[j]->psvi;
+ tempstype[j] = 0;
+ if ((comp->stype == NULL) && (comp->has_stype != 0)) {
+ comp->stype =
+ xsltEvalAttrValueTemplate(ctxt, sorts[j],
+ (const xmlChar *) "data-type",
+ XSLT_NAMESPACE);
+ if (comp->stype != NULL) {
+ tempstype[j] = 1;
+ if (xmlStrEqual(comp->stype, (const xmlChar *) "text"))
+ comp->number = 0;
+ else if (xmlStrEqual(comp->stype, (const xmlChar *) "number"))
+ comp->number = 1;
+ else {
+ xsltTransformError(ctxt, NULL, sorts[j],
+ "xsltDoSortFunction: no support for data-type = %s\n",
+ comp->stype);
+ comp->number = 0; /* use default */
+ }
+ }
+ }
+ temporder[j] = 0;
+ if ((comp->order == NULL) && (comp->has_order != 0)) {
+ comp->order = xsltEvalAttrValueTemplate(ctxt, sorts[j],
+ (const xmlChar *) "order",
+ XSLT_NAMESPACE);
+ if (comp->order != NULL) {
+ temporder[j] = 1;
+ if (xmlStrEqual(comp->order, (const xmlChar *) "ascending"))
+ comp->descending = 0;
+ else if (xmlStrEqual(comp->order,
+ (const xmlChar *) "descending"))
+ comp->descending = 1;
+ else {
+ xsltTransformError(ctxt, NULL, sorts[j],
+ "xsltDoSortFunction: invalid value %s for order\n",
+ comp->order);
+ comp->descending = 0; /* use default */
+ }
+ }
+ }
+ }
+
+ len = list->nodeNr;
+
+ resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]);
+ for (i = 1;i < XSLT_MAX_SORT;i++)
+ resultsTab[i] = NULL;
+
+ results = resultsTab[0];
+
+ comp = sorts[0]->psvi;
+ descending = comp->descending;
+ number = comp->number;
+ if (results == NULL)
+ return;
+
+ /* Shell's sort of node-set */
+ for (incr = len / 2; incr > 0; incr /= 2) {
+ for (i = incr; i < len; i++) {
+ j = i - incr;
+ if (results[i] == NULL)
+ continue;
+
+ while (j >= 0) {
+ if (results[j] == NULL)
+ tst = 1;
+ else {
+ if (number) {
+ /* We make NaN smaller than number in accordance
+ with XSLT spec */
+ if (xmlXPathIsNaN(results[j]->floatval)) {
+ if (xmlXPathIsNaN(results[j + incr]->floatval))
+ tst = 0;
+ else
+ tst = -1;
+ } else if (xmlXPathIsNaN(results[j + incr]->floatval))
+ tst = 1;
+ else if (results[j]->floatval ==
+ results[j + incr]->floatval)
+ tst = 0;
+ else if (results[j]->floatval >
+ results[j + incr]->floatval)
+ tst = 1;
+ else tst = -1;
+ } else if(comp->locale != (xsltLocale)0) {
+ tst = xsltLocaleStrcmp(
+ comp->locale,
+ (xsltLocaleChar *) results[j]->stringval,
+ (xsltLocaleChar *) results[j + incr]->stringval);
+ } else {
+ tst = xmlStrcmp(results[j]->stringval,
+ results[j + incr]->stringval);
+ }
+ if (descending)
+ tst = -tst;
+ }
+ if (tst == 0) {
+ /*
+ * Okay we need to use multi level sorts
+ */
+ depth = 1;
+ while (depth < nbsorts) {
+ if (sorts[depth] == NULL)
+ break;
+ comp = sorts[depth]->psvi;
+ if (comp == NULL)
+ break;
+ desc = comp->descending;
+ numb = comp->number;
+
+ /*
+ * Compute the result of the next level for the
+ * full set, this might be optimized ... or not
+ */
+ if (resultsTab[depth] == NULL)
+ resultsTab[depth] = xsltComputeSortResult(ctxt,
+ sorts[depth]);
+ res = resultsTab[depth];
+ if (res == NULL)
+ break;
+ if (res[j] == NULL) {
+ if (res[j+incr] != NULL)
+ tst = 1;
+ } else {
+ if (numb) {
+ /* We make NaN smaller than number in
+ accordance with XSLT spec */
+ if (xmlXPathIsNaN(res[j]->floatval)) {
+ if (xmlXPathIsNaN(res[j +
+ incr]->floatval))
+ tst = 0;
+ else
+ tst = -1;
+ } else if (xmlXPathIsNaN(res[j + incr]->
+ floatval))
+ tst = 1;
+ else if (res[j]->floatval == res[j + incr]->
+ floatval)
+ tst = 0;
+ else if (res[j]->floatval >
+ res[j + incr]->floatval)
+ tst = 1;
+ else tst = -1;
+ } else if(comp->locale != (xsltLocale)0) {
+ tst = xsltLocaleStrcmp(
+ comp->locale,
+ (xsltLocaleChar *) res[j]->stringval,
+ (xsltLocaleChar *) res[j + incr]->stringval);
+ } else {
+ tst = xmlStrcmp(res[j]->stringval,
+ res[j + incr]->stringval);
+ }
+ if (desc)
+ tst = -tst;
+ }
+
+ /*
+ * if we still can't differenciate at this level
+ * try one level deeper.
+ */
+ if (tst != 0)
+ break;
+ depth++;
+ }
+ }
+ if (tst == 0) {
+ tst = results[j]->index > results[j + incr]->index;
+ }
+ if (tst > 0) {
+ tmp = results[j];
+ results[j] = results[j + incr];
+ results[j + incr] = tmp;
+ node = list->nodeTab[j];
+ list->nodeTab[j] = list->nodeTab[j + incr];
+ list->nodeTab[j + incr] = node;
+ depth = 1;
+ while (depth < nbsorts) {
+ if (sorts[depth] == NULL)
+ break;
+ if (resultsTab[depth] == NULL)
+ break;
+ res = resultsTab[depth];
+ tmp = res[j];
+ res[j] = res[j + incr];
+ res[j + incr] = tmp;
+ depth++;
+ }
+ j -= incr;
+ } else
+ break;
+ }
+ }
+ }
+
+ for (j = 0; j < nbsorts; j++) {
+ comp = sorts[j]->psvi;
+ if (tempstype[j] == 1) {
+ /* The data-type needs to be recomputed each time */
+ xmlFree((void *)(comp->stype));
+ comp->stype = NULL;
+ }
+ if (temporder[j] == 1) {
+ /* The order needs to be recomputed each time */
+ xmlFree((void *)(comp->order));
+ comp->order = NULL;
+ }
+ if (resultsTab[j] != NULL) {
+ for (i = 0;i < len;i++)
+ xmlXPathFreeObject(resultsTab[j][i]);
+ xmlFree(resultsTab[j]);
+ }
+ }
+}
+
+
+static xsltSortFunc xsltSortFunction = xsltDefaultSortFunction;
+
+/**
+ * xsltDoSortFunction:
+ * @ctxt: a XSLT process context
+ * @sorts: array of sort nodes
+ * @nbsorts: the number of sorts in the array
+ *
+ * reorder the current node list accordingly to the set of sorting
+ * requirement provided by the arry of nodes.
+ * This is a wrapper function, the actual function used is specified
+ * using xsltSetCtxtSortFunc() to set the context specific sort function,
+ * or xsltSetSortFunc() to set the global sort function.
+ * If a sort function is set on the context, this will get called.
+ * Otherwise the global sort function is called.
+ */
+void
+xsltDoSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr * sorts,
+ int nbsorts)
+{
+ if (ctxt->sortfunc != NULL)
+ (ctxt->sortfunc)(ctxt, sorts, nbsorts);
+ else if (xsltSortFunction != NULL)
+ xsltSortFunction(ctxt, sorts, nbsorts);
+}
+
+/**
+ * xsltSetSortFunc:
+ * @handler: the new handler function
+ *
+ * Function to reset the global handler for XSLT sorting.
+ * If the handler is NULL, the default sort function will be used.
+ */
+void
+xsltSetSortFunc(xsltSortFunc handler) {
+ if (handler != NULL)
+ xsltSortFunction = handler;
+ else
+ xsltSortFunction = xsltDefaultSortFunction;
+}
+
+/**
+ * xsltSetCtxtSortFunc:
+ * @ctxt: a XSLT process context
+ * @handler: the new handler function
+ *
+ * Function to set the handler for XSLT sorting
+ * for the specified context.
+ * If the handler is NULL, then the global
+ * sort function will be called
+ */
+void
+xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt, xsltSortFunc handler) {
+ ctxt->sortfunc = handler;
+}
+
+/************************************************************************
+ * *
+ * Parsing options *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltSetCtxtParseOptions:
+ * @ctxt: a XSLT process context
+ * @options: a combination of libxml2 xmlParserOption
+ *
+ * Change the default parser option passed by the XSLT engine to the
+ * parser when using document() loading.
+ *
+ * Returns the previous options or -1 in case of error
+ */
+int
+xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt, int options)
+{
+ int oldopts;
+
+ if (ctxt == NULL)
+ return(-1);
+ oldopts = ctxt->parserOptions;
+ if (ctxt->xinclude)
+ oldopts |= XML_PARSE_XINCLUDE;
+ ctxt->parserOptions = options;
+ if (options & XML_PARSE_XINCLUDE)
+ ctxt->xinclude = 1;
+ else
+ ctxt->xinclude = 0;
+ return(oldopts);
+}
+
+/************************************************************************
+ * *
+ * Output *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltSaveResultTo:
+ * @buf: an output buffer
+ * @result: the result xmlDocPtr
+ * @style: the stylesheet
+ *
+ * Save the result @result obtained by applying the @style stylesheet
+ * to an I/O output channel @buf
+ *
+ * Returns the number of byte written or -1 in case of failure.
+ */
+int
+xsltSaveResultTo(xmlOutputBufferPtr buf, xmlDocPtr result,
+ xsltStylesheetPtr style) {
+ const xmlChar *encoding;
+ int base;
+ const xmlChar *method;
+ int indent;
+
+ if ((buf == NULL) || (result == NULL) || (style == NULL))
+ return(-1);
+ if ((result->children == NULL) ||
+ ((result->children->type == XML_DTD_NODE) &&
+ (result->children->next == NULL)))
+ return(0);
+
+ if ((style->methodURI != NULL) &&
+ ((style->method == NULL) ||
+ (!xmlStrEqual(style->method, (const xmlChar *) "xhtml")))) {
+ xsltGenericError(xsltGenericErrorContext,
+ "xsltSaveResultTo : unknown ouput method\n");
+ return(-1);
+ }
+
+ base = buf->written;
+
+ XSLT_GET_IMPORT_PTR(method, style, method)
+ XSLT_GET_IMPORT_PTR(encoding, style, encoding)
+ XSLT_GET_IMPORT_INT(indent, style, indent);
+
+ if ((method == NULL) && (result->type == XML_HTML_DOCUMENT_NODE))
+ method = (const xmlChar *) "html";
+
+ if ((method != NULL) &&
+ (xmlStrEqual(method, (const xmlChar *) "html"))) {
+ if (encoding != NULL) {
+ htmlSetMetaEncoding(result, (const xmlChar *) encoding);
+ } else {
+ htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
+ }
+ if (indent == -1)
+ indent = 1;
+ htmlDocContentDumpFormatOutput(buf, result, (const char *) encoding,
+ indent);
+ xmlOutputBufferFlush(buf);
+ } else if ((method != NULL) &&
+ (xmlStrEqual(method, (const xmlChar *) "xhtml"))) {
+ if (encoding != NULL) {
+ htmlSetMetaEncoding(result, (const xmlChar *) encoding);
+ } else {
+ htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
+ }
+ htmlDocContentDumpOutput(buf, result, (const char *) encoding);
+ xmlOutputBufferFlush(buf);
+ } else if ((method != NULL) &&
+ (xmlStrEqual(method, (const xmlChar *) "text"))) {
+ xmlNodePtr cur;
+
+ cur = result->children;
+ while (cur != NULL) {
+ if (cur->type == XML_TEXT_NODE)
+ xmlOutputBufferWriteString(buf, (const char *) cur->content);
+
+ /*
+ * Skip to next node
+ */
+ if (cur->children != NULL) {
+ if ((cur->children->type != XML_ENTITY_DECL) &&
+ (cur->children->type != XML_ENTITY_REF_NODE) &&
+ (cur->children->type != XML_ENTITY_NODE)) {
+ cur = cur->children;
+ continue;
+ }
+ }
+ if (cur->next != NULL) {
+ cur = cur->next;
+ continue;
+ }
+
+ do {
+ cur = cur->parent;
+ if (cur == NULL)
+ break;
+ if (cur == (xmlNodePtr) style->doc) {
+ cur = NULL;
+ break;
+ }
+ if (cur->next != NULL) {
+ cur = cur->next;
+ break;
+ }
+ } while (cur != NULL);
+ }
+ xmlOutputBufferFlush(buf);
+ } else {
+ int omitXmlDecl;
+ int standalone;
+
+ XSLT_GET_IMPORT_INT(omitXmlDecl, style, omitXmlDeclaration);
+ XSLT_GET_IMPORT_INT(standalone, style, standalone);
+
+ if (omitXmlDecl != 1) {
+ xmlOutputBufferWriteString(buf, "<?xml version=");
+ if (result->version != NULL) {
+ xmlOutputBufferWriteString(buf, "\"");
+ xmlOutputBufferWriteString(buf, (const char *)result->version);
+ xmlOutputBufferWriteString(buf, "\"");
+ } else
+ xmlOutputBufferWriteString(buf, "\"1.0\"");
+ if (encoding == NULL) {
+ if (result->encoding != NULL)
+ encoding = result->encoding;
+ else if (result->charset != XML_CHAR_ENCODING_UTF8)
+ encoding = (const xmlChar *)
+ xmlGetCharEncodingName((xmlCharEncoding)
+ result->charset);
+ }
+ if (encoding != NULL) {
+ xmlOutputBufferWriteString(buf, " encoding=");
+ xmlOutputBufferWriteString(buf, "\"");
+ xmlOutputBufferWriteString(buf, (const char *) encoding);
+ xmlOutputBufferWriteString(buf, "\"");
+ }
+ switch (standalone) {
+ case 0:
+ xmlOutputBufferWriteString(buf, " standalone=\"no\"");
+ break;
+ case 1:
+ xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
+ break;
+ default:
+ break;
+ }
+ xmlOutputBufferWriteString(buf, "?>\n");
+ }
+ if (result->children != NULL) {
+ xmlNodePtr child = result->children;
+
+ while (child != NULL) {
+ xmlNodeDumpOutput(buf, result, child, 0, (indent == 1),
+ (const char *) encoding);
+ if (indent && ((child->type == XML_DTD_NODE) ||
+ ((child->type == XML_COMMENT_NODE) &&
+ (child->next != NULL))))
+ xmlOutputBufferWriteString(buf, "\n");
+ child = child->next;
+ }
+ if (indent)
+ xmlOutputBufferWriteString(buf, "\n");
+ }
+ xmlOutputBufferFlush(buf);
+ }
+ return(buf->written - base);
+}
+
+/**
+ * xsltSaveResultToFilename:
+ * @URL: a filename or URL
+ * @result: the result xmlDocPtr
+ * @style: the stylesheet
+ * @compression: the compression factor (0 - 9 included)
+ *
+ * Save the result @result obtained by applying the @style stylesheet
+ * to a file or @URL
+ *
+ * Returns the number of byte written or -1 in case of failure.
+ */
+int
+xsltSaveResultToFilename(const char *URL, xmlDocPtr result,
+ xsltStylesheetPtr style, int compression) {
+ xmlOutputBufferPtr buf;
+ const xmlChar *encoding;
+ int ret;
+
+ if ((URL == NULL) || (result == NULL) || (style == NULL))
+ return(-1);
+ if (result->children == NULL)
+ return(0);
+
+ XSLT_GET_IMPORT_PTR(encoding, style, encoding)
+ if (encoding != NULL) {
+ xmlCharEncodingHandlerPtr encoder;
+
+ encoder = xmlFindCharEncodingHandler((char *)encoding);
+ if ((encoder != NULL) &&
+ (xmlStrEqual((const xmlChar *)encoder->name,
+ (const xmlChar *) "UTF-8")))
+ encoder = NULL;
+ buf = xmlOutputBufferCreateFilename(URL, encoder, compression);
+ } else {
+ buf = xmlOutputBufferCreateFilename(URL, NULL, compression);
+ }
+ if (buf == NULL)
+ return(-1);
+ xsltSaveResultTo(buf, result, style);
+ ret = xmlOutputBufferClose(buf);
+ return(ret);
+}
+
+/**
+ * xsltSaveResultToFile:
+ * @file: a FILE * I/O
+ * @result: the result xmlDocPtr
+ * @style: the stylesheet
+ *
+ * Save the result @result obtained by applying the @style stylesheet
+ * to an open FILE * I/O.
+ * This does not close the FILE @file
+ *
+ * Returns the number of bytes written or -1 in case of failure.
+ */
+int
+xsltSaveResultToFile(FILE *file, xmlDocPtr result, xsltStylesheetPtr style) {
+ xmlOutputBufferPtr buf;
+ const xmlChar *encoding;
+ int ret;
+
+ if ((file == NULL) || (result == NULL) || (style == NULL))
+ return(-1);
+ if (result->children == NULL)
+ return(0);
+
+ XSLT_GET_IMPORT_PTR(encoding, style, encoding)
+ if (encoding != NULL) {
+ xmlCharEncodingHandlerPtr encoder;
+
+ encoder = xmlFindCharEncodingHandler((char *)encoding);
+ if ((encoder != NULL) &&
+ (xmlStrEqual((const xmlChar *)encoder->name,
+ (const xmlChar *) "UTF-8")))
+ encoder = NULL;
+ buf = xmlOutputBufferCreateFile(file, encoder);
+ } else {
+ buf = xmlOutputBufferCreateFile(file, NULL);
+ }
+
+ if (buf == NULL)
+ return(-1);
+ xsltSaveResultTo(buf, result, style);
+ ret = xmlOutputBufferClose(buf);
+ return(ret);
+}
+
+/**
+ * xsltSaveResultToFd:
+ * @fd: a file descriptor
+ * @result: the result xmlDocPtr
+ * @style: the stylesheet
+ *
+ * Save the result @result obtained by applying the @style stylesheet
+ * to an open file descriptor
+ * This does not close the descriptor.
+ *
+ * Returns the number of bytes written or -1 in case of failure.
+ */
+int
+xsltSaveResultToFd(int fd, xmlDocPtr result, xsltStylesheetPtr style) {
+ xmlOutputBufferPtr buf;
+ const xmlChar *encoding;
+ int ret;
+
+ if ((fd < 0) || (result == NULL) || (style == NULL))
+ return(-1);
+ if (result->children == NULL)
+ return(0);
+
+ XSLT_GET_IMPORT_PTR(encoding, style, encoding)
+ if (encoding != NULL) {
+ xmlCharEncodingHandlerPtr encoder;
+
+ encoder = xmlFindCharEncodingHandler((char *)encoding);
+ if ((encoder != NULL) &&
+ (xmlStrEqual((const xmlChar *)encoder->name,
+ (const xmlChar *) "UTF-8")))
+ encoder = NULL;
+ buf = xmlOutputBufferCreateFd(fd, encoder);
+ } else {
+ buf = xmlOutputBufferCreateFd(fd, NULL);
+ }
+ if (buf == NULL)
+ return(-1);
+ xsltSaveResultTo(buf, result, style);
+ ret = xmlOutputBufferClose(buf);
+ return(ret);
+}
+
+/**
+ * xsltSaveResultToString:
+ * @doc_txt_ptr: Memory pointer for allocated XML text
+ * @doc_txt_len: Length of the generated XML text
+ * @result: the result xmlDocPtr
+ * @style: the stylesheet
+ *
+ * Save the result @result obtained by applying the @style stylesheet
+ * to a new allocated string.
+ *
+ * Returns 0 in case of success and -1 in case of error
+ */
+int
+xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len,
+ xmlDocPtr result, xsltStylesheetPtr style) {
+ xmlOutputBufferPtr buf;
+ const xmlChar *encoding;
+
+ *doc_txt_ptr = NULL;
+ *doc_txt_len = 0;
+ if (result->children == NULL)
+ return(0);
+
+ XSLT_GET_IMPORT_PTR(encoding, style, encoding)
+ if (encoding != NULL) {
+ xmlCharEncodingHandlerPtr encoder;
+
+ encoder = xmlFindCharEncodingHandler((char *)encoding);
+ if ((encoder != NULL) &&
+ (xmlStrEqual((const xmlChar *)encoder->name,
+ (const xmlChar *) "UTF-8")))
+ encoder = NULL;
+ buf = xmlAllocOutputBuffer(encoder);
+ } else {
+ buf = xmlAllocOutputBuffer(NULL);
+ }
+ if (buf == NULL)
+ return(-1);
+ xsltSaveResultTo(buf, result, style);
+#ifdef LIBXML2_NEW_BUFFER
+ if (buf->conv != NULL) {
+ *doc_txt_len = xmlBufUse(buf->conv);
+ *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->conv), *doc_txt_len);
+ } else {
+ *doc_txt_len = xmlBufUse(buf->buffer);
+ *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->buffer), *doc_txt_len);
+ }
+#else
+ if (buf->conv != NULL) {
+ *doc_txt_len = buf->conv->use;
+ *doc_txt_ptr = xmlStrndup(buf->conv->content, *doc_txt_len);
+ } else {
+ *doc_txt_len = buf->buffer->use;
+ *doc_txt_ptr = xmlStrndup(buf->buffer->content, *doc_txt_len);
+ }
+#endif
+ (void)xmlOutputBufferClose(buf);
+ return 0;
+}
+
+/************************************************************************
+ * *
+ * Generating profiling informations *
+ * *
+ ************************************************************************/
+
+static long calibration = -1;
+
+/**
+ * xsltCalibrateTimestamps:
+ *
+ * Used for to calibrate the xsltTimestamp() function
+ * Should work if launched at startup and we don't loose our quantum :-)
+ *
+ * Returns the number of milliseconds used by xsltTimestamp()
+ */
+static long
+xsltCalibrateTimestamps(void) {
+ register int i;
+
+ for (i = 0;i < 999;i++)
+ xsltTimestamp();
+ return(xsltTimestamp() / 1000);
+}
+
+/**
+ * xsltCalibrateAdjust:
+ * @delta: a negative dealy value found
+ *
+ * Used for to correct the calibration for xsltTimestamp()
+ */
+void
+xsltCalibrateAdjust(long delta) {
+ calibration += delta;
+}
+
+/**
+ * xsltTimestamp:
+ *
+ * Used for gathering profiling data
+ *
+ * Returns the number of tenth of milliseconds since the beginning of the
+ * profiling
+ */
+long
+xsltTimestamp(void)
+{
+#ifdef XSLT_WIN32_PERFORMANCE_COUNTER
+ BOOL ok;
+ LARGE_INTEGER performanceCount;
+ LARGE_INTEGER performanceFrequency;
+ LONGLONG quadCount;
+ double seconds;
+ static LONGLONG startupQuadCount = 0;
+ static LONGLONG startupQuadFreq = 0;
+
+ ok = QueryPerformanceCounter(&performanceCount);
+ if (!ok)
+ return 0;
+ quadCount = performanceCount.QuadPart;
+ if (calibration < 0) {
+ calibration = 0;
+ ok = QueryPerformanceFrequency(&performanceFrequency);
+ if (!ok)
+ return 0;
+ startupQuadFreq = performanceFrequency.QuadPart;
+ startupQuadCount = quadCount;
+ return (0);
+ }
+ if (startupQuadFreq == 0)
+ return 0;
+ seconds = (quadCount - startupQuadCount) / (double) startupQuadFreq;
+ return (long) (seconds * XSLT_TIMESTAMP_TICS_PER_SEC);
+
+#else /* XSLT_WIN32_PERFORMANCE_COUNTER */
+#ifdef HAVE_CLOCK_GETTIME
+# if defined(CLOCK_MONOTONIC)
+# define XSLT_CLOCK CLOCK_MONOTONIC
+# elif defined(CLOCK_HIGHRES)
+# define XSLT_CLOCK CLOCK_HIGHRES
+# else
+# define XSLT_CLOCK CLOCK_REALTIME
+# endif
+ static struct timespec startup;
+ struct timespec cur;
+ long tics;
+
+ if (calibration < 0) {
+ clock_gettime(XSLT_CLOCK, &startup);
+ calibration = 0;
+ calibration = xsltCalibrateTimestamps();
+ clock_gettime(XSLT_CLOCK, &startup);
+ return (0);
+ }
+
+ clock_gettime(XSLT_CLOCK, &cur);
+ tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
+ tics += (cur.tv_nsec - startup.tv_nsec) /
+ (1000000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
+
+ tics -= calibration;
+ return(tics);
+
+#elif HAVE_GETTIMEOFDAY
+ static struct timeval startup;
+ struct timeval cur;
+ long tics;
+
+ if (calibration < 0) {
+ gettimeofday(&startup, NULL);
+ calibration = 0;
+ calibration = xsltCalibrateTimestamps();
+ gettimeofday(&startup, NULL);
+ return (0);
+ }
+
+ gettimeofday(&cur, NULL);
+ tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
+ tics += (cur.tv_usec - startup.tv_usec) /
+ (1000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
+
+ tics -= calibration;
+ return(tics);
+#else
+
+ /* Neither gettimeofday() nor Win32 performance counter available */
+
+ return (0);
+
+#endif /* HAVE_GETTIMEOFDAY */
+#endif /* XSLT_WIN32_PERFORMANCE_COUNTER */
+}
+
+static char *
+pretty_templ_match(xsltTemplatePtr templ) {
+ static char dst[1001];
+ char *src = (char *)templ->match;
+ int i=0,j;
+
+ /* strip white spaces */
+ for (j=0; i<1000 && src[j]; i++,j++) {
+ for(;src[j]==' ';j++);
+ dst[i]=src[j];
+ }
+ if(i<998 && templ->mode) {
+ /* append [mode] */
+ dst[i++]='[';
+ src=(char *)templ->mode;
+ for (j=0; i<999 && src[j]; i++,j++) {
+ dst[i]=src[j];
+ }
+ dst[i++]=']';
+ }
+ dst[i]='\0';
+ return dst;
+}
+
+#define MAX_TEMPLATES 10000
+
+/**
+ * xsltSaveProfiling:
+ * @ctxt: an XSLT context
+ * @output: a FILE * for saving the informations
+ *
+ * Save the profiling informations on @output
+ */
+void
+xsltSaveProfiling(xsltTransformContextPtr ctxt, FILE *output) {
+ int nb, i,j,k,l;
+ int max;
+ int total;
+ unsigned long totalt;
+ xsltTemplatePtr *templates;
+ xsltStylesheetPtr style;
+ xsltTemplatePtr templ1,templ2;
+ int *childt;
+
+ if ((output == NULL) || (ctxt == NULL))
+ return;
+ if (ctxt->profile == 0)
+ return;
+
+ nb = 0;
+ max = MAX_TEMPLATES;
+ templates = xmlMalloc(max * sizeof(xsltTemplatePtr));
+ if (templates == NULL)
+ return;
+
+ style = ctxt->style;
+ while (style != NULL) {
+ templ1 = style->templates;
+ while (templ1 != NULL) {
+ if (nb >= max)
+ break;
+
+ if (templ1->nbCalls > 0)
+ templates[nb++] = templ1;
+ templ1 = templ1->next;
+ }
+
+ style = xsltNextImport(style);
+ }
+
+ for (i = 0;i < nb -1;i++) {
+ for (j = i + 1; j < nb; j++) {
+ if ((templates[i]->time <= templates[j]->time) ||
+ ((templates[i]->time == templates[j]->time) &&
+ (templates[i]->nbCalls <= templates[j]->nbCalls))) {
+ templ1 = templates[j];
+ templates[j] = templates[i];
+ templates[i] = templ1;
+ }
+ }
+ }
+
+
+ /* print flat profile */
+
+ fprintf(output, "%6s%20s%20s%10s Calls Tot 100us Avg\n\n",
+ "number", "match", "name", "mode");
+ total = 0;
+ totalt = 0;
+ for (i = 0;i < nb;i++) {
+ templ1 = templates[i];
+ fprintf(output, "%5d ", i);
+ if (templ1->match != NULL) {
+ if (xmlStrlen(templ1->match) > 20)
+ fprintf(output, "%s\n%26s", templ1->match, "");
+ else
+ fprintf(output, "%20s", templ1->match);
+ } else {
+ fprintf(output, "%20s", "");
+ }
+ if (templ1->name != NULL) {
+ if (xmlStrlen(templ1->name) > 20)
+ fprintf(output, "%s\n%46s", templ1->name, "");
+ else
+ fprintf(output, "%20s", templ1->name);
+ } else {
+ fprintf(output, "%20s", "");
+ }
+ if (templ1->mode != NULL) {
+ if (xmlStrlen(templ1->mode) > 10)
+ fprintf(output, "%s\n%56s", templ1->mode, "");
+ else
+ fprintf(output, "%10s", templ1->mode);
+ } else {
+ fprintf(output, "%10s", "");
+ }
+ fprintf(output, " %6d", templ1->nbCalls);
+ fprintf(output, " %6ld %6ld\n", templ1->time,
+ templ1->time / templ1->nbCalls);
+ total += templ1->nbCalls;
+ totalt += templ1->time;
+ }
+ fprintf(output, "\n%30s%26s %6d %6ld\n", "Total", "", total, totalt);
+
+
+ /* print call graph */
+
+ childt = xmlMalloc((nb + 1) * sizeof(int));
+ if (childt == NULL)
+ return;
+
+ /* precalculate children times */
+ for (i = 0; i < nb; i++) {
+ templ1 = templates[i];
+
+ childt[i] = 0;
+ for (k = 0; k < nb; k++) {
+ templ2 = templates[k];
+ for (l = 0; l < templ2->templNr; l++) {
+ if (templ2->templCalledTab[l] == templ1) {
+ childt[i] +=templ2->time;
+ }
+ }
+ }
+ }
+ childt[i] = 0;
+
+ fprintf(output, "\nindex %% time self children called name\n");
+
+ for (i = 0; i < nb; i++) {
+ char ix_str[20], timep_str[20], times_str[20], timec_str[20], called_str[20];
+ unsigned long t;
+
+ templ1 = templates[i];
+ /* callers */
+ for (j = 0; j < templ1->templNr; j++) {
+ templ2 = templ1->templCalledTab[j];
+ for (k = 0; k < nb; k++) {
+ if (templates[k] == templ2)
+ break;
+ }
+ t=templ2?templ2->time:totalt;
+ snprintf(times_str,sizeof(times_str),"%8.3f",(float)t/XSLT_TIMESTAMP_TICS_PER_SEC);
+ snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC);
+ snprintf(called_str,sizeof(called_str),"%6d/%d",
+ templ1->templCountTab[j], /* number of times caller calls 'this' */
+ templ1->nbCalls); /* total number of calls to 'this' */
+
+ fprintf(output, " %-8s %-8s %-12s %s [%d]\n",
+ times_str,timec_str,called_str,
+ (templ2?(templ2->name?(char *)templ2->name:pretty_templ_match(templ2)):"-"),k);
+ }
+ /* this */
+ snprintf(ix_str,sizeof(ix_str),"[%d]",i);
+ snprintf(timep_str,sizeof(timep_str),"%6.2f",(float)templ1->time*100.0/totalt);
+ snprintf(times_str,sizeof(times_str),"%8.3f",(float)templ1->time/XSLT_TIMESTAMP_TICS_PER_SEC);
+ snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[i]/XSLT_TIMESTAMP_TICS_PER_SEC);
+ fprintf(output, "%-5s %-6s %-8s %-8s %6d %s [%d]\n",
+ ix_str, timep_str,times_str,timec_str,
+ templ1->nbCalls,
+ templ1->name?(char *)templ1->name:pretty_templ_match(templ1),i);
+ /* callees
+ * - go over templates[0..nb] and their templCalledTab[]
+ * - print those where we in the the call-stack
+ */
+ total = 0;
+ for (k = 0; k < nb; k++) {
+ templ2 = templates[k];
+ for (l = 0; l < templ2->templNr; l++) {
+ if (templ2->templCalledTab[l] == templ1) {
+ total+=templ2->templCountTab[l];
+ }
+ }
+ }
+ for (k = 0; k < nb; k++) {
+ templ2 = templates[k];
+ for (l = 0; l < templ2->templNr; l++) {
+ if (templ2->templCalledTab[l] == templ1) {
+ snprintf(times_str,sizeof(times_str),"%8.3f",(float)templ2->time/XSLT_TIMESTAMP_TICS_PER_SEC);
+ snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC);
+ snprintf(called_str,sizeof(called_str),"%6d/%d",
+ templ2->templCountTab[l], /* number of times 'this' calls callee */
+ total); /* total number of calls from 'this' */
+ fprintf(output, " %-8s %-8s %-12s %s [%d]\n",
+ times_str,timec_str,called_str,
+ templ2->name?(char *)templ2->name:pretty_templ_match(templ2),k);
+ }
+ }
+ }
+ fprintf(output, "-----------------------------------------------\n");
+ }
+
+ fprintf(output, "\f\nIndex by function name\n");
+ for (i = 0; i < nb; i++) {
+ templ1 = templates[i];
+ fprintf(output, "[%d] %s (%s:%d)\n",
+ i, templ1->name?(char *)templ1->name:pretty_templ_match(templ1),
+ templ1->style->doc->URL,templ1->elem->line);
+ }
+
+ fprintf(output, "\f\n");
+ xmlFree(childt);
+
+ xmlFree(templates);
+}
+
+/************************************************************************
+ * *
+ * Fetching profiling informations *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltGetProfileInformation:
+ * @ctxt: a transformation context
+ *
+ * This function should be called after the transformation completed
+ * to extract template processing profiling informations if availble.
+ * The informations are returned as an XML document tree like
+ * <?xml version="1.0"?>
+ * <profile>
+ * <template rank="1" match="*" name=""
+ * mode="" calls="6" time="48" average="8"/>
+ * <template rank="2" match="item2|item3" name=""
+ * mode="" calls="10" time="30" average="3"/>
+ * <template rank="3" match="item1" name=""
+ * mode="" calls="5" time="17" average="3"/>
+ * </profile>
+ * The caller will need to free up the returned tree with xmlFreeDoc()
+ *
+ * Returns the xmlDocPtr corresponding to the result or NULL if not available.
+ */
+
+xmlDocPtr
+xsltGetProfileInformation(xsltTransformContextPtr ctxt)
+{
+ xmlDocPtr ret = NULL;
+ xmlNodePtr root, child;
+ char buf[100];
+
+ xsltStylesheetPtr style;
+ xsltTemplatePtr *templates;
+ xsltTemplatePtr templ;
+ int nb = 0, max = 0, i, j;
+
+ if (!ctxt)
+ return NULL;
+
+ if (!ctxt->profile)
+ return NULL;
+
+ nb = 0;
+ max = 10000;
+ templates =
+ (xsltTemplatePtr *) xmlMalloc(max * sizeof(xsltTemplatePtr));
+ if (templates == NULL)
+ return NULL;
+
+ /*
+ * collect all the templates in an array
+ */
+ style = ctxt->style;
+ while (style != NULL) {
+ templ = style->templates;
+ while (templ != NULL) {
+ if (nb >= max)
+ break;
+
+ if (templ->nbCalls > 0)
+ templates[nb++] = templ;
+ templ = templ->next;
+ }
+
+ style = (xsltStylesheetPtr) xsltNextImport(style);
+ }
+
+ /*
+ * Sort the array by time spent
+ */
+ for (i = 0; i < nb - 1; i++) {
+ for (j = i + 1; j < nb; j++) {
+ if ((templates[i]->time <= templates[j]->time) ||
+ ((templates[i]->time == templates[j]->time) &&
+ (templates[i]->nbCalls <= templates[j]->nbCalls))) {
+ templ = templates[j];
+ templates[j] = templates[i];
+ templates[i] = templ;
+ }
+ }
+ }
+
+ /*
+ * Generate a document corresponding to the results.
+ */
+ ret = xmlNewDoc(BAD_CAST "1.0");
+ root = xmlNewDocNode(ret, NULL, BAD_CAST "profile", NULL);
+ xmlDocSetRootElement(ret, root);
+
+ for (i = 0; i < nb; i++) {
+ child = xmlNewChild(root, NULL, BAD_CAST "template", NULL);
+ snprintf(buf, sizeof(buf), "%d", i + 1);
+ xmlSetProp(child, BAD_CAST "rank", BAD_CAST buf);
+ xmlSetProp(child, BAD_CAST "match", BAD_CAST templates[i]->match);
+ xmlSetProp(child, BAD_CAST "name", BAD_CAST templates[i]->name);
+ xmlSetProp(child, BAD_CAST "mode", BAD_CAST templates[i]->mode);
+
+ snprintf(buf, sizeof(buf), "%d", templates[i]->nbCalls);
+ xmlSetProp(child, BAD_CAST "calls", BAD_CAST buf);
+
+ snprintf(buf, sizeof(buf), "%ld", templates[i]->time);
+ xmlSetProp(child, BAD_CAST "time", BAD_CAST buf);
+
+ snprintf(buf, sizeof(buf), "%ld", templates[i]->time / templates[i]->nbCalls);
+ xmlSetProp(child, BAD_CAST "average", BAD_CAST buf);
+ };
+
+ xmlFree(templates);
+
+ return ret;
+}
+
+/************************************************************************
+ * *
+ * Hooks for libxml2 XPath *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltXPathCompileFlags:
+ * @style: the stylesheet
+ * @str: the XPath expression
+ * @flags: extra compilation flags to pass down to libxml2 XPath
+ *
+ * Compile an XPath expression
+ *
+ * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
+ * the caller has to free the object.
+ */
+xmlXPathCompExprPtr
+xsltXPathCompileFlags(xsltStylesheetPtr style, const xmlChar *str, int flags) {
+ xmlXPathContextPtr xpathCtxt;
+ xmlXPathCompExprPtr ret;
+
+ if (style != NULL) {
+#ifdef XSLT_REFACTORED_XPATHCOMP
+ if (XSLT_CCTXT(style)) {
+ /*
+ * Proposed by Jerome Pesenti
+ * --------------------------
+ * For better efficiency we'll reuse the compilation
+ * context's XPath context. For the common stylesheet using
+ * XPath expressions this will reduce compilation time to
+ * about 50%.
+ *
+ * See http://mail.gnome.org/archives/xslt/2006-April/msg00037.html
+ */
+ xpathCtxt = XSLT_CCTXT(style)->xpathCtxt;
+ xpathCtxt->doc = style->doc;
+ } else
+ xpathCtxt = xmlXPathNewContext(style->doc);
+#else
+ xpathCtxt = xmlXPathNewContext(style->doc);
+#endif
+ if (xpathCtxt == NULL)
+ return NULL;
+ xpathCtxt->dict = style->dict;
+ } else {
+ xpathCtxt = xmlXPathNewContext(NULL);
+ if (xpathCtxt == NULL)
+ return NULL;
+ }
+ xpathCtxt->flags = flags;
+
+ /*
+ * Compile the expression.
+ */
+ ret = xmlXPathCtxtCompile(xpathCtxt, str);
+
+#ifdef XSLT_REFACTORED_XPATHCOMP
+ if ((style == NULL) || (! XSLT_CCTXT(style))) {
+ xmlXPathFreeContext(xpathCtxt);
+ }
+#else
+ xmlXPathFreeContext(xpathCtxt);
+#endif
+ /*
+ * TODO: there is a lot of optimizations which should be possible
+ * like variable slot precomputations, function precomputations, etc.
+ */
+
+ return(ret);
+}
+
+/**
+ * xsltXPathCompile:
+ * @style: the stylesheet
+ * @str: the XPath expression
+ *
+ * Compile an XPath expression
+ *
+ * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
+ * the caller has to free the object.
+ */
+xmlXPathCompExprPtr
+xsltXPathCompile(xsltStylesheetPtr style, const xmlChar *str) {
+ return(xsltXPathCompileFlags(style, str, 0));
+}
+
+/************************************************************************
+ * *
+ * Hooks for the debugger *
+ * *
+ ************************************************************************/
+
+/*
+ * There is currently only 3 debugging callback defined
+ * Debugger callbacks are disabled by default
+ */
+#define XSLT_CALLBACK_NUMBER 3
+
+typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks;
+typedef xsltDebuggerCallbacks *xsltDebuggerCallbacksPtr;
+struct _xsltDebuggerCallbacks {
+ xsltHandleDebuggerCallback handler;
+ xsltAddCallCallback add;
+ xsltDropCallCallback drop;
+};
+
+static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks = {
+ NULL, /* handler */
+ NULL, /* add */
+ NULL /* drop */
+};
+
+int xslDebugStatus;
+
+/**
+ * xsltSetDebuggerStatus:
+ * @value : the value to be set
+ *
+ * This function sets the value of xslDebugStatus.
+ */
+void
+xsltSetDebuggerStatus(int value)
+{
+ xslDebugStatus = value;
+}
+
+/**
+ * xsltGetDebuggerStatus:
+ *
+ * Get xslDebugStatus.
+ *
+ * Returns the value of xslDebugStatus.
+ */
+int
+xsltGetDebuggerStatus(void)
+{
+ return(xslDebugStatus);
+}
+
+/**
+ * xsltSetDebuggerCallbacks:
+ * @no : number of callbacks
+ * @block : the block of callbacks
+ *
+ * This function allow to plug a debugger into the XSLT library
+ * @block points to a block of memory containing the address of @no
+ * callback routines.
+ *
+ * Returns 0 in case of success and -1 in case of error
+ */
+int
+xsltSetDebuggerCallbacks(int no, void *block)
+{
+ xsltDebuggerCallbacksPtr callbacks;
+
+ if ((block == NULL) || (no != XSLT_CALLBACK_NUMBER))
+ return(-1);
+
+ callbacks = (xsltDebuggerCallbacksPtr) block;
+ xsltDebuggerCurrentCallbacks.handler = callbacks->handler;
+ xsltDebuggerCurrentCallbacks.add = callbacks->add;
+ xsltDebuggerCurrentCallbacks.drop = callbacks->drop;
+ return(0);
+}
+
+/**
+ * xslHandleDebugger:
+ * @cur : source node being executed
+ * @node : data node being processed
+ * @templ : temlate that applies to node
+ * @ctxt : the xslt transform context
+ *
+ * If either cur or node are a breakpoint, or xslDebugStatus in state
+ * where debugging must occcur at this time then transfer control
+ * to the xslDebugBreak function
+ */
+void
+xslHandleDebugger(xmlNodePtr cur, xmlNodePtr node, xsltTemplatePtr templ,
+ xsltTransformContextPtr ctxt)
+{
+ if (xsltDebuggerCurrentCallbacks.handler != NULL)
+ xsltDebuggerCurrentCallbacks.handler(cur, node, templ, ctxt);
+}
+
+/**
+ * xslAddCall:
+ * @templ : current template being applied
+ * @source : the source node being processed
+ *
+ * Add template "call" to call stack
+ * Returns : 1 on sucess 0 otherwise an error may be printed if
+ * WITH_XSLT_DEBUG_BREAKPOINTS is defined
+ */
+int
+xslAddCall(xsltTemplatePtr templ, xmlNodePtr source)
+{
+ if (xsltDebuggerCurrentCallbacks.add != NULL)
+ return(xsltDebuggerCurrentCallbacks.add(templ, source));
+ return(0);
+}
+
+/**
+ * xslDropCall:
+ *
+ * Drop the topmost item off the call stack
+ */
+void
+xslDropCall(void)
+{
+ if (xsltDebuggerCurrentCallbacks.drop != NULL)
+ xsltDebuggerCurrentCallbacks.drop();
+}
+
diff --git a/libxslt/xsltutils.h b/libxslt/xsltutils.h
new file mode 100644
index 0000000..789865a
--- /dev/null
+++ b/libxslt/xsltutils.h
@@ -0,0 +1,313 @@
+/*
+ * Summary: set of utilities for the XSLT engine
+ * Description: interfaces for the utilities module of the XSLT engine.
+ * things like message handling, profiling, and other
+ * generally useful routines.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLTUTILS_H__
+#define __XML_XSLTUTILS_H__
+
+#include <libxslt/xsltconfig.h>
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#include <libxml/xpath.h>
+#include <libxml/dict.h>
+#include <libxml/xmlerror.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * XSLT_TODO:
+ *
+ * Macro to flag unimplemented blocks.
+ */
+#define XSLT_TODO \
+ xsltGenericError(xsltGenericErrorContext, \
+ "Unimplemented block at %s:%d\n", \
+ __FILE__, __LINE__);
+
+/**
+ * XSLT_STRANGE:
+ *
+ * Macro to flag that a problem was detected internally.
+ */
+#define XSLT_STRANGE \
+ xsltGenericError(xsltGenericErrorContext, \
+ "Internal error at %s:%d\n", \
+ __FILE__, __LINE__);
+
+/**
+ * IS_XSLT_ELEM:
+ *
+ * Checks that the element pertains to XSLT namespace.
+ */
+#define IS_XSLT_ELEM(n) \
+ (((n) != NULL) && ((n)->type == XML_ELEMENT_NODE) && \
+ ((n)->ns != NULL) && (xmlStrEqual((n)->ns->href, XSLT_NAMESPACE)))
+
+/**
+ * IS_XSLT_NAME:
+ *
+ * Checks the value of an element in XSLT namespace.
+ */
+#define IS_XSLT_NAME(n, val) \
+ (xmlStrEqual((n)->name, (const xmlChar *) (val)))
+
+/**
+ * IS_XSLT_REAL_NODE:
+ *
+ * Check that a node is a 'real' one: document, element, text or attribute.
+ */
+#define IS_XSLT_REAL_NODE(n) \
+ (((n) != NULL) && \
+ (((n)->type == XML_ELEMENT_NODE) || \
+ ((n)->type == XML_TEXT_NODE) || \
+ ((n)->type == XML_CDATA_SECTION_NODE) || \
+ ((n)->type == XML_ATTRIBUTE_NODE) || \
+ ((n)->type == XML_DOCUMENT_NODE) || \
+ ((n)->type == XML_HTML_DOCUMENT_NODE) || \
+ ((n)->type == XML_COMMENT_NODE) || \
+ ((n)->type == XML_PI_NODE)))
+
+/*
+ * Our own version of namespaced atributes lookup.
+ */
+XSLTPUBFUN xmlChar * XSLTCALL
+ xsltGetNsProp (xmlNodePtr node,
+ const xmlChar *name,
+ const xmlChar *nameSpace);
+XSLTPUBFUN const xmlChar * XSLTCALL
+ xsltGetCNsProp (xsltStylesheetPtr style,
+ xmlNodePtr node,
+ const xmlChar *name,
+ const xmlChar *nameSpace);
+XSLTPUBFUN int XSLTCALL
+ xsltGetUTF8Char (const unsigned char *utf,
+ int *len);
+
+/*
+ * XSLT Debug Tracing Tracing Types
+ */
+typedef enum {
+ XSLT_TRACE_ALL = -1,
+ XSLT_TRACE_NONE = 0,
+ XSLT_TRACE_COPY_TEXT = 1<<0,
+ XSLT_TRACE_PROCESS_NODE = 1<<1,
+ XSLT_TRACE_APPLY_TEMPLATE = 1<<2,
+ XSLT_TRACE_COPY = 1<<3,
+ XSLT_TRACE_COMMENT = 1<<4,
+ XSLT_TRACE_PI = 1<<5,
+ XSLT_TRACE_COPY_OF = 1<<6,
+ XSLT_TRACE_VALUE_OF = 1<<7,
+ XSLT_TRACE_CALL_TEMPLATE = 1<<8,
+ XSLT_TRACE_APPLY_TEMPLATES = 1<<9,
+ XSLT_TRACE_CHOOSE = 1<<10,
+ XSLT_TRACE_IF = 1<<11,
+ XSLT_TRACE_FOR_EACH = 1<<12,
+ XSLT_TRACE_STRIP_SPACES = 1<<13,
+ XSLT_TRACE_TEMPLATES = 1<<14,
+ XSLT_TRACE_KEYS = 1<<15,
+ XSLT_TRACE_VARIABLES = 1<<16
+} xsltDebugTraceCodes;
+
+/**
+ * XSLT_TRACE:
+ *
+ * Control the type of xsl debugtrace messages emitted.
+ */
+#define XSLT_TRACE(ctxt,code,call) \
+ if (ctxt->traceCode && (*(ctxt->traceCode) & code)) \
+ call
+
+XSLTPUBFUN void XSLTCALL
+ xsltDebugSetDefaultTrace(xsltDebugTraceCodes val);
+XSLTPUBFUN xsltDebugTraceCodes XSLTCALL
+ xsltDebugGetDefaultTrace(void);
+
+/*
+ * XSLT specific error and debug reporting functions.
+ */
+XSLTPUBVAR xmlGenericErrorFunc xsltGenericError;
+XSLTPUBVAR void *xsltGenericErrorContext;
+XSLTPUBVAR xmlGenericErrorFunc xsltGenericDebug;
+XSLTPUBVAR void *xsltGenericDebugContext;
+
+XSLTPUBFUN void XSLTCALL
+ xsltPrintErrorContext (xsltTransformContextPtr ctxt,
+ xsltStylesheetPtr style,
+ xmlNodePtr node);
+XSLTPUBFUN void XSLTCALL
+ xsltMessage (xsltTransformContextPtr ctxt,
+ xmlNodePtr node,
+ xmlNodePtr inst);
+XSLTPUBFUN void XSLTCALL
+ xsltSetGenericErrorFunc (void *ctx,
+ xmlGenericErrorFunc handler);
+XSLTPUBFUN void XSLTCALL
+ xsltSetGenericDebugFunc (void *ctx,
+ xmlGenericErrorFunc handler);
+XSLTPUBFUN void XSLTCALL
+ xsltSetTransformErrorFunc (xsltTransformContextPtr ctxt,
+ void *ctx,
+ xmlGenericErrorFunc handler);
+XSLTPUBFUN void XSLTCALL
+ xsltTransformError (xsltTransformContextPtr ctxt,
+ xsltStylesheetPtr style,
+ xmlNodePtr node,
+ const char *msg,
+ ...) LIBXSLT_ATTR_FORMAT(4,5);
+
+XSLTPUBFUN int XSLTCALL
+ xsltSetCtxtParseOptions (xsltTransformContextPtr ctxt,
+ int options);
+/*
+ * Sorting.
+ */
+
+XSLTPUBFUN void XSLTCALL
+ xsltDocumentSortFunction (xmlNodeSetPtr list);
+XSLTPUBFUN void XSLTCALL
+ xsltSetSortFunc (xsltSortFunc handler);
+XSLTPUBFUN void XSLTCALL
+ xsltSetCtxtSortFunc (xsltTransformContextPtr ctxt,
+ xsltSortFunc handler);
+XSLTPUBFUN void XSLTCALL
+ xsltDefaultSortFunction (xsltTransformContextPtr ctxt,
+ xmlNodePtr *sorts,
+ int nbsorts);
+XSLTPUBFUN void XSLTCALL
+ xsltDoSortFunction (xsltTransformContextPtr ctxt,
+ xmlNodePtr * sorts,
+ int nbsorts);
+XSLTPUBFUN xmlXPathObjectPtr * XSLTCALL
+ xsltComputeSortResult (xsltTransformContextPtr ctxt,
+ xmlNodePtr sort);
+
+/*
+ * QNames handling.
+ */
+
+XSLTPUBFUN const xmlChar * XSLTCALL
+ xsltSplitQName (xmlDictPtr dict,
+ const xmlChar *name,
+ const xmlChar **prefix);
+XSLTPUBFUN const xmlChar * XSLTCALL
+ xsltGetQNameURI (xmlNodePtr node,
+ xmlChar **name);
+
+XSLTPUBFUN const xmlChar * XSLTCALL
+ xsltGetQNameURI2 (xsltStylesheetPtr style,
+ xmlNodePtr node,
+ const xmlChar **name);
+
+/*
+ * Output, reuse libxml I/O buffers.
+ */
+XSLTPUBFUN int XSLTCALL
+ xsltSaveResultTo (xmlOutputBufferPtr buf,
+ xmlDocPtr result,
+ xsltStylesheetPtr style);
+XSLTPUBFUN int XSLTCALL
+ xsltSaveResultToFilename (const char *URI,
+ xmlDocPtr result,
+ xsltStylesheetPtr style,
+ int compression);
+XSLTPUBFUN int XSLTCALL
+ xsltSaveResultToFile (FILE *file,
+ xmlDocPtr result,
+ xsltStylesheetPtr style);
+XSLTPUBFUN int XSLTCALL
+ xsltSaveResultToFd (int fd,
+ xmlDocPtr result,
+ xsltStylesheetPtr style);
+XSLTPUBFUN int XSLTCALL
+ xsltSaveResultToString (xmlChar **doc_txt_ptr,
+ int * doc_txt_len,
+ xmlDocPtr result,
+ xsltStylesheetPtr style);
+
+/*
+ * XPath interface
+ */
+XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL
+ xsltXPathCompile (xsltStylesheetPtr style,
+ const xmlChar *str);
+XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL
+ xsltXPathCompileFlags (xsltStylesheetPtr style,
+ const xmlChar *str,
+ int flags);
+
+/*
+ * Profiling.
+ */
+XSLTPUBFUN void XSLTCALL
+ xsltSaveProfiling (xsltTransformContextPtr ctxt,
+ FILE *output);
+XSLTPUBFUN xmlDocPtr XSLTCALL
+ xsltGetProfileInformation (xsltTransformContextPtr ctxt);
+
+XSLTPUBFUN long XSLTCALL
+ xsltTimestamp (void);
+XSLTPUBFUN void XSLTCALL
+ xsltCalibrateAdjust (long delta);
+
+/**
+ * XSLT_TIMESTAMP_TICS_PER_SEC:
+ *
+ * Sampling precision for profiling
+ */
+#define XSLT_TIMESTAMP_TICS_PER_SEC 100000l
+
+/*
+ * Hooks for the debugger.
+ */
+
+typedef enum {
+ XSLT_DEBUG_NONE = 0, /* no debugging allowed */
+ XSLT_DEBUG_INIT,
+ XSLT_DEBUG_STEP,
+ XSLT_DEBUG_STEPOUT,
+ XSLT_DEBUG_NEXT,
+ XSLT_DEBUG_STOP,
+ XSLT_DEBUG_CONT,
+ XSLT_DEBUG_RUN,
+ XSLT_DEBUG_RUN_RESTART,
+ XSLT_DEBUG_QUIT
+} xsltDebugStatusCodes;
+
+XSLTPUBVAR int xslDebugStatus;
+
+typedef void (*xsltHandleDebuggerCallback) (xmlNodePtr cur, xmlNodePtr node,
+ xsltTemplatePtr templ, xsltTransformContextPtr ctxt);
+typedef int (*xsltAddCallCallback) (xsltTemplatePtr templ, xmlNodePtr source);
+typedef void (*xsltDropCallCallback) (void);
+
+XSLTPUBFUN void XSLTCALL
+ xsltSetDebuggerStatus (int value);
+XSLTPUBFUN int XSLTCALL
+ xsltGetDebuggerStatus (void);
+XSLTPUBFUN int XSLTCALL
+ xsltSetDebuggerCallbacks (int no, void *block);
+XSLTPUBFUN int XSLTCALL
+ xslAddCall (xsltTemplatePtr templ,
+ xmlNodePtr source);
+XSLTPUBFUN void XSLTCALL
+ xslDropCall (void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLTUTILS_H__ */
+
+
diff --git a/libxslt/xsltwin32config.h b/libxslt/xsltwin32config.h
new file mode 100644
index 0000000..35c714e
--- /dev/null
+++ b/libxslt/xsltwin32config.h
@@ -0,0 +1,105 @@
+/*
+ * Summary: compile-time version informations for the XSLT engine
+ * when compiled on windows
+ * Description: compile-time version informations for the XSLT engine
+ * when compiled on windows. This file is generated.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLTWIN32CONFIG_H__
+#define __XML_XSLTWIN32CONFIG_H__
+
+#include "win32config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * LIBXSLT_DOTTED_VERSION:
+ *
+ * the version string like "1.2.3"
+ */
+#define LIBXSLT_DOTTED_VERSION "1.1.29"
+
+/**
+ * LIBXSLT_VERSION:
+ *
+ * the version number: 1.2.3 value is 1002003
+ */
+#define LIBXSLT_VERSION 10129
+
+/**
+ * LIBXSLT_VERSION_STRING:
+ *
+ * the version number string, 1.2.3 value is "1002003"
+ */
+#define LIBXSLT_VERSION_STRING "10129"
+
+/**
+ * LIBXSLT_VERSION_EXTRA:
+ *
+ * extra version information, used to show a CVS compilation
+ */
+#define LIBXSLT_VERSION_EXTRA "-win32"
+
+/**
+ * WITH_XSLT_DEBUG:
+ *
+ * Activate the compilation of the debug reporting. Speed penalty
+ * is insignifiant and being able to run xsltpoc -v is useful. On
+ * by default
+ */
+#if 1
+#define WITH_XSLT_DEBUG
+#endif
+
+/**
+ * WITH_MODULES:
+ *
+ * Whether module support is configured into libxslt
+ */
+#if 1
+#ifndef WITH_MODULES
+#define WITH_MODULES
+#endif
+#define LIBXSLT_PLUGINS_PATH() getenv("LIBXSLT_PLUGINS_PATH")
+#endif
+
+#if 0
+/**
+ * DEBUG_MEMORY:
+ *
+ * should be activated only when debugging libxslt. It replaces the
+ * allocator with a collect and debug shell to the libc allocator.
+ * Use configure --with-mem-debug to activate it on both library
+ */
+#define DEBUG_MEMORY
+
+/**
+ * DEBUG_MEMORY_LOCATION:
+ *
+ * should be activated only when debugging libxslt.
+ * DEBUG_MEMORY_LOCATION should be activated only when libxml has
+ * been configured with --with-debug-mem too
+ */
+#define DEBUG_MEMORY_LOCATION
+#endif
+
+/**
+ * ATTRIBUTE_UNUSED:
+ *
+ * This macro is used to flag unused function parameters to GCC, useless here
+ */
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLTWIN32CONFIG_H__ */
diff --git a/libxslt/xsltwin32config.h.in b/libxslt/xsltwin32config.h.in
new file mode 100644
index 0000000..cceeb6b
--- /dev/null
+++ b/libxslt/xsltwin32config.h.in
@@ -0,0 +1,105 @@
+/*
+ * Summary: compile-time version informations for the XSLT engine
+ * when compiled on windows
+ * Description: compile-time version informations for the XSLT engine
+ * when compiled on windows. This file is generated.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLTWIN32CONFIG_H__
+#define __XML_XSLTWIN32CONFIG_H__
+
+#include "win32config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * LIBXSLT_DOTTED_VERSION:
+ *
+ * the version string like "1.2.3"
+ */
+#define LIBXSLT_DOTTED_VERSION "@VERSION@"
+
+/**
+ * LIBXSLT_VERSION:
+ *
+ * the version number: 1.2.3 value is 1002003
+ */
+#define LIBXSLT_VERSION @LIBXSLT_VERSION_NUMBER@
+
+/**
+ * LIBXSLT_VERSION_STRING:
+ *
+ * the version number string, 1.2.3 value is "1002003"
+ */
+#define LIBXSLT_VERSION_STRING "@LIBXSLT_VERSION_NUMBER@"
+
+/**
+ * LIBXSLT_VERSION_EXTRA:
+ *
+ * extra version information, used to show a CVS compilation
+ */
+#define LIBXSLT_VERSION_EXTRA "-win32"
+
+/**
+ * WITH_XSLT_DEBUG:
+ *
+ * Activate the compilation of the debug reporting. Speed penalty
+ * is insignifiant and being able to run xsltpoc -v is useful. On
+ * by default
+ */
+#if 1
+#define WITH_XSLT_DEBUG
+#endif
+
+/**
+ * WITH_MODULES:
+ *
+ * Whether module support is configured into libxslt
+ */
+#if @WITH_MODULES@
+#ifndef WITH_MODULES
+#define WITH_MODULES
+#endif
+#define LIBXSLT_PLUGINS_PATH() getenv("LIBXSLT_PLUGINS_PATH")
+#endif
+
+#if 0
+/**
+ * DEBUG_MEMORY:
+ *
+ * should be activated only when debugging libxslt. It replaces the
+ * allocator with a collect and debug shell to the libc allocator.
+ * Use configure --with-mem-debug to activate it on both library
+ */
+#define DEBUG_MEMORY
+
+/**
+ * DEBUG_MEMORY_LOCATION:
+ *
+ * should be activated only when debugging libxslt.
+ * DEBUG_MEMORY_LOCATION should be activated only when libxml has
+ * been configured with --with-debug-mem too
+ */
+#define DEBUG_MEMORY_LOCATION
+#endif
+
+/**
+ * ATTRIBUTE_UNUSED:
+ *
+ * This macro is used to flag unused function parameters to GCC, useless here
+ */
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLTWIN32CONFIG_H__ */