Add barebone libxslt files to Android to support Android webkit.

This from libxslt version 2.7.7.
No change is made, just directly from open source.
Other currenlty omitted files can be added later if necessary.

Change-Id: I4a2ee1c4dfa330065380f7ad446164c5deb69105
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..094ebbc
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,20 @@
+Daniel Veillard:
+   daniel@veillard.com
+   DV on #gnome IRC channel
+   http://veillard.com/
+   Used to work at W3C, now Red Hat
+   co-chair of W3C XML Linking WG
+   invited expert on the W3C XML Core WG
+   Author of libxml upon which this library is based.
+
+Bjorn Reese:
+   breese@users.sourceforge.net
+   http://home1.stofanet.dk/breese/
+   Software developer at http://www.systematic.dk/
+   Member of the XML-MTF Mapping WG.
+
+William Brack <wbrack@mmm.com.hk>
+
+Thomas Broyer <tbroyer@ltgt.net>
+
+Igor Zlatkovic <igor@zlatkovic.com> for the Windows port
diff --git a/Copyright b/Copyright
new file mode 100644
index 0000000..627eeff
--- /dev/null
+++ b/Copyright
@@ -0,0 +1,53 @@
+Licence for libxslt except libexslt
+----------------------------------------------------------------------
+ Copyright (C) 2001-2002 Daniel Veillard.  All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is fur-
+nished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
+NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+DANIEL VEILLARD BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
+NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of Daniel Veillard shall not
+be used in advertising or otherwise to promote the sale, use or other deal-
+ings in this Software without prior written authorization from him.
+
+----------------------------------------------------------------------
+
+Licence for libexslt
+----------------------------------------------------------------------
+ Copyright (C) 2001-2002 Thomas Broyer, Charlie Bozeman and Daniel Veillard.
+ All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is fur-
+nished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
+NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
+NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the authors shall not
+be used in advertising or otherwise to promote the sale, use or other deal-
+ings in this Software without prior written authorization from him.
+----------------------------------------------------------------------
diff --git a/README b/README
new file mode 100644
index 0000000..85bf80e
--- /dev/null
+++ b/README
@@ -0,0 +1,24 @@
+
+     XSLT support for libxml2 (XML toolkit from the GNOME project)
+
+Full documentation is available on-line at
+    http://xmlsoft.org/XSLT/
+
+This code is released under the MIT Licence see the Copyright file.
+ 
+To report bugs, follow the instructions at:
+  http://xmlsoft.org/XSLT/bugs.html
+
+A mailing-list xslt@gnome.org is available, to subscribe:
+    http://mail.gnome.org/mailman/listinfo/xslt
+
+The list archive is at:
+    http://mail.gnome.org/archives/xslt/
+
+All technical answers asked privately will be automatically answered on
+the list and archived for public access unless pricacy is explicitely
+required and justified.
+
+Daniel Veillard
+
+$Id$
diff --git a/libxslt/attributes.c b/libxslt/attributes.c
new file mode 100644
index 0000000..ce47df7
--- /dev/null
+++ b/libxslt/attributes.c
@@ -0,0 +1,1139 @@
+/*
+ * 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
+
+/*
+ * TODO: merge attribute sets from different import precedence.
+ *       all this should be precomputed just before the transformation
+ *       starts or at first hit with a cache in the context.
+ *       The simple way for now would be to not allow redefinition of
+ *       attributes once generated in the output tree, possibly costlier.
+ */
+
+/*
+ * Useful macros
+ */
+#ifdef IS_BLANK
+#undef IS_BLANK
+#endif
+
+#define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) ||	\
+                     ((c) == 0x0D))
+
+#define IS_BLANK_NODE(n)						\
+    (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
+
+
+/*
+ * The in-memory structure corresponding to an XSLT Attribute in
+ * an attribute set
+ */
+
+
+typedef struct _xsltAttrElem xsltAttrElem;
+typedef xsltAttrElem *xsltAttrElemPtr;
+struct _xsltAttrElem {
+    struct _xsltAttrElem *next;/* chained list */
+    xmlNodePtr attr;	/* the xsl:attribute definition */
+    const xmlChar *set; /* or the attribute set */
+    const xmlChar *ns;  /* and its namespace */
+};
+
+/************************************************************************
+ *									*
+ *			XSLT Attribute handling				*
+ *									*
+ ************************************************************************/
+
+/**
+ * xsltNewAttrElem:
+ * @attr:  the new xsl:attribute node
+ *
+ * Create a new XSLT AttrElem
+ *
+ * Returns the newly allocated xsltAttrElemPtr or NULL in case of error
+ */
+static xsltAttrElemPtr
+xsltNewAttrElem(xmlNodePtr attr) {
+    xsltAttrElemPtr cur;
+
+    cur = (xsltAttrElemPtr) xmlMalloc(sizeof(xsltAttrElem));
+    if (cur == NULL) {
+        xsltGenericError(xsltGenericErrorContext,
+		"xsltNewAttrElem : malloc failed\n");
+	return(NULL);
+    }
+    memset(cur, 0, sizeof(xsltAttrElem));
+    cur->attr = attr;
+    return(cur);
+}
+
+/**
+ * xsltFreeAttrElem:
+ * @attr:  an XSLT AttrElem
+ *
+ * Free up the memory allocated by @attr
+ */
+static void
+xsltFreeAttrElem(xsltAttrElemPtr attr) {
+    xmlFree(attr);
+}
+
+/**
+ * xsltFreeAttrElemList:
+ * @list:  an XSLT AttrElem list
+ *
+ * Free up the memory allocated by @list
+ */
+static void
+xsltFreeAttrElemList(xsltAttrElemPtr list) {
+    xsltAttrElemPtr next;
+    
+    while (list != NULL) {
+	next = list->next;
+	xsltFreeAttrElem(list);
+	list = next;
+    }
+}
+
+#ifdef XSLT_REFACTORED
+    /*
+    * This was moved to xsltParseStylesheetAttributeSet().
+    */
+#else
+/**
+ * xsltAddAttrElemList:
+ * @list:  an XSLT AttrElem list
+ * @attr:  the new xsl:attribute node
+ *
+ * Add the new attribute to the list.
+ *
+ * Returns the new list pointer
+ */
+static xsltAttrElemPtr
+xsltAddAttrElemList(xsltAttrElemPtr list, xmlNodePtr attr) {
+    xsltAttrElemPtr next, cur;
+
+    if (attr == NULL)
+	return(list);
+    if (list == NULL)
+	return(xsltNewAttrElem(attr));
+    cur = list;
+    while (cur != NULL) {	
+	next = cur->next;
+	if (cur->attr == attr)
+	    return(cur);
+	if (cur->next == NULL) {
+	    cur->next = xsltNewAttrElem(attr);
+	    return(list);
+	}
+	cur = next;
+    }
+    return(list);
+}
+#endif /* XSLT_REFACTORED */
+
+/**
+ * xsltMergeAttrElemList:
+ * @list:  an XSLT AttrElem list
+ * @old:  another XSLT AttrElem list
+ *
+ * Add all the attributes from list @old to list @list,
+ * but drop redefinition of existing values.
+ *
+ * Returns the new list pointer
+ */
+static xsltAttrElemPtr
+xsltMergeAttrElemList(xsltStylesheetPtr style,
+		      xsltAttrElemPtr list, xsltAttrElemPtr old) {
+    xsltAttrElemPtr cur;
+    int add;
+
+    while (old != NULL) {
+	if ((old->attr == NULL) && (old->set == NULL)) {
+	    old = old->next;
+	    continue;
+	}
+	/*
+	 * Check that the attribute is not yet in the list
+	 */
+	cur = list;
+	add = 1;
+	while (cur != NULL) {
+	    if ((cur->attr == NULL) && (cur->set == NULL)) {
+		if (cur->next == NULL)
+		    break;
+		cur = cur->next;
+		continue;
+	    }
+	    if ((cur->set != NULL) && (cur->set == old->set)) {
+		add = 0;
+		break;
+	    }
+	    if (cur->set != NULL) {
+		if (cur->next == NULL)
+		    break;
+		cur = cur->next;
+		continue;
+	    }
+	    if (old->set != NULL) {
+		if (cur->next == NULL)
+		    break;
+		cur = cur->next;
+		continue;
+	    }
+	    if (cur->attr == old->attr) {
+		xsltGenericError(xsltGenericErrorContext,
+	     "xsl:attribute-set : use-attribute-sets recursion detected\n");
+		return(list);
+	    }
+	    if (cur->next == NULL)
+		break;
+            cur = cur->next;
+	}
+
+	if (add == 1) {
+	    /*
+	    * Changed to use the string-dict, rather than duplicating
+	    * @set and @ns; this fixes bug #340400.
+	    */
+	    if (cur == NULL) {
+		list = xsltNewAttrElem(old->attr);
+		if (old->set != NULL) {
+		    list->set = xmlDictLookup(style->dict, old->set, -1);
+		    if (old->ns != NULL)
+			list->ns = xmlDictLookup(style->dict, old->ns, -1);
+		}
+	    } else if (add) {
+		cur->next = xsltNewAttrElem(old->attr);
+		if (old->set != NULL) {
+		    cur->next->set = xmlDictLookup(style->dict, old->set, -1);
+		    if (old->ns != NULL)
+			cur->next->ns = xmlDictLookup(style->dict, old->ns, -1);
+		}
+	    }
+	}
+
+	old = old->next;
+    }
+    return(list);
+}
+
+/************************************************************************
+ *									*
+ *			Module interfaces				*
+ *									*
+ ************************************************************************/
+
+/**
+ * xsltParseStylesheetAttributeSet:
+ * @style:  the XSLT stylesheet
+ * @cur:  the "attribute-set" element
+ *
+ * parse an XSLT stylesheet attribute-set element
+ */
+
+void
+xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) {
+    const xmlChar *ncname;
+    const xmlChar *prefix;
+    xmlChar *value;
+    xmlNodePtr child;
+    xsltAttrElemPtr attrItems;
+
+    if ((cur == NULL) || (style == NULL))
+	return;
+
+    value = xmlGetNsProp(cur, (const xmlChar *)"name", NULL);
+    if (value == NULL) {
+	xsltGenericError(xsltGenericErrorContext,
+	     "xsl:attribute-set : name is missing\n");
+	return;
+    }
+
+    ncname = xsltSplitQName(style->dict, value, &prefix);
+    xmlFree(value);
+    value = NULL;
+
+    if (style->attributeSets == NULL) {
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+	xsltGenericDebug(xsltGenericDebugContext,
+	    "creating attribute set table\n");
+#endif
+	style->attributeSets = xmlHashCreate(10);
+    }
+    if (style->attributeSets == NULL)
+	return;
+
+    attrItems = xmlHashLookup2(style->attributeSets, ncname, prefix);
+
+    /*
+    * Parse the content. Only xsl:attribute elements are allowed.
+    */
+    child = cur->children;
+    while (child != NULL) {
+	/*
+	* Report invalid nodes.
+	*/
+	if ((child->type != XML_ELEMENT_NODE) ||
+	    (child->ns == NULL) ||
+	    (! IS_XSLT_ELEM(child)))
+	{
+	    if (child->type == XML_ELEMENT_NODE)
+		xsltTransformError(NULL, style, child,
+			"xsl:attribute-set : unexpected child %s\n",
+		                 child->name);
+	    else
+		xsltTransformError(NULL, style, child,
+			"xsl:attribute-set : child of unexpected type\n");
+	} else if (!IS_XSLT_NAME(child, "attribute")) {
+	    xsltTransformError(NULL, style, child,
+		"xsl:attribute-set : unexpected child xsl:%s\n",
+		child->name);
+	} else {
+#ifdef XSLT_REFACTORED
+	    xsltAttrElemPtr nextAttr, curAttr;
+
+	    /*
+	    * Process xsl:attribute
+	    * ---------------------
+	    */
+
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+	    xsltGenericDebug(xsltGenericDebugContext,
+		"add attribute to list %s\n", ncname);
+#endif
+	    /*
+	    * The following was taken over from
+	    * xsltAddAttrElemList().
+	    */
+	    if (attrItems == NULL) {
+		attrItems = xsltNewAttrElem(child);
+	    } else {
+		curAttr = attrItems;
+		while (curAttr != NULL) {
+		    nextAttr = curAttr->next;
+		    if (curAttr->attr == child) {
+			/*
+			* URGENT TODO: Can somebody explain
+			*  why attrItems is set to curAttr
+			*  here? Is this somehow related to
+			*  avoidance of recursions?
+			*/
+			attrItems = curAttr;
+			goto next_child;
+		    }
+		    if (curAttr->next == NULL)			
+			curAttr->next = xsltNewAttrElem(child);
+		    curAttr = nextAttr;
+		}
+	    }
+	    /*
+	    * Parse the xsl:attribute and its content.
+	    */
+	    xsltParseAnyXSLTElem(XSLT_CCTXT(style), child);
+#else
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+	    xsltGenericDebug(xsltGenericDebugContext,
+		"add attribute to list %s\n", ncname);
+#endif
+	    /*
+	    * OLD behaviour:
+	    */
+	    attrItems = xsltAddAttrElemList(attrItems, child);
+#endif
+	}
+
+#ifdef XSLT_REFACTORED
+next_child:
+#endif
+	child = child->next;
+    }
+
+    /*
+    * Process attribue "use-attribute-sets".
+    */
+    /* TODO check recursion */    
+    value = xmlGetNsProp(cur, (const xmlChar *)"use-attribute-sets",
+	NULL);
+    if (value != NULL) {
+	const xmlChar *curval, *endval;
+	curval = value;
+	while (*curval != 0) {
+	    while (IS_BLANK(*curval)) curval++;
+	    if (*curval == 0)
+		break;
+	    endval = curval;
+	    while ((*endval != 0) && (!IS_BLANK(*endval))) endval++;
+	    curval = xmlDictLookup(style->dict, curval, endval - curval);
+	    if (curval) {
+		const xmlChar *ncname2 = NULL;
+		const xmlChar *prefix2 = NULL;
+		xsltAttrElemPtr refAttrItems;
+		
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+		xsltGenericDebug(xsltGenericDebugContext,
+		    "xsl:attribute-set : %s adds use %s\n", ncname, curval);
+#endif
+		ncname2 = xsltSplitQName(style->dict, curval, &prefix2);
+		refAttrItems = xsltNewAttrElem(NULL);
+		if (refAttrItems != NULL) {
+		    refAttrItems->set = ncname2;
+		    refAttrItems->ns = prefix2;
+		    attrItems = xsltMergeAttrElemList(style,
+			attrItems, refAttrItems);
+		    xsltFreeAttrElem(refAttrItems);
+		}
+	    }
+	    curval = endval;
+	}
+	xmlFree(value);
+	value = NULL;
+    }
+
+    /*
+     * Update the value
+     */
+    /*
+    * TODO: Why is this dummy entry needed.?
+    */
+    if (attrItems == NULL)
+	attrItems = xsltNewAttrElem(NULL);
+    xmlHashUpdateEntry2(style->attributeSets, ncname, prefix, attrItems, NULL);
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+    xsltGenericDebug(xsltGenericDebugContext,
+	"updated attribute list %s\n", ncname);
+#endif
+}
+
+/**
+ * xsltGetSAS:
+ * @style:  the XSLT stylesheet
+ * @name:  the attribute list name
+ * @ns:  the attribute list namespace
+ *
+ * lookup an attribute set based on the style cascade
+ *
+ * Returns the attribute set or NULL
+ */
+static xsltAttrElemPtr
+xsltGetSAS(xsltStylesheetPtr style, const xmlChar *name, const xmlChar *ns) {
+    xsltAttrElemPtr values;
+
+    while (style != NULL) {
+	values = xmlHashLookup2(style->attributeSets, name, ns);
+	if (values != NULL)
+	    return(values);
+	style = xsltNextImport(style);
+    }
+    return(NULL);
+}
+
+/**
+ * xsltResolveSASCallback,:
+ * @style:  the XSLT stylesheet
+ *
+ * resolve the references in an attribute set.
+ */
+static void
+xsltResolveSASCallback(xsltAttrElemPtr values, xsltStylesheetPtr style,
+	               const xmlChar *name, const xmlChar *ns,
+		       ATTRIBUTE_UNUSED const xmlChar *ignored) {
+    xsltAttrElemPtr tmp;
+    xsltAttrElemPtr refs;
+
+    tmp = values;
+    while (tmp != NULL) {
+	if (tmp->set != NULL) {
+	    /*
+	     * Check against cycles !
+	     */
+	    if ((xmlStrEqual(name, tmp->set)) && (xmlStrEqual(ns, tmp->ns))) {
+		xsltGenericError(xsltGenericErrorContext,
+     "xsl:attribute-set : use-attribute-sets recursion detected on %s\n",
+                                 name);
+	    } else {
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+		xsltGenericDebug(xsltGenericDebugContext,
+			"Importing attribute list %s\n", tmp->set);
+#endif
+
+		refs = xsltGetSAS(style, tmp->set, tmp->ns);
+		if (refs == NULL) {
+		    xsltGenericError(xsltGenericErrorContext,
+     "xsl:attribute-set : use-attribute-sets %s reference missing %s\n",
+				     name, tmp->set);
+		} else {
+		    /*
+		     * recurse first for cleanup
+		     */
+		    xsltResolveSASCallback(refs, style, name, ns, NULL);
+		    /*
+		     * Then merge
+		     */
+		    xsltMergeAttrElemList(style, values, refs);
+		    /*
+		     * Then suppress the reference
+		     */
+		    tmp->set = NULL;
+		    tmp->ns = NULL;
+		}
+	    }
+	}
+	tmp = tmp->next;
+    }
+}
+
+/**
+ * xsltMergeSASCallback,:
+ * @style:  the XSLT stylesheet
+ *
+ * Merge an attribute set from an imported stylesheet.
+ */
+static void
+xsltMergeSASCallback(xsltAttrElemPtr values, xsltStylesheetPtr style,
+	               const xmlChar *name, const xmlChar *ns,
+		       ATTRIBUTE_UNUSED const xmlChar *ignored) {
+    int ret;
+    xsltAttrElemPtr topSet;
+
+    ret = xmlHashAddEntry2(style->attributeSets, name, ns, values);
+    if (ret < 0) {
+	/*
+	 * Add failed, this attribute set can be removed.
+	 */
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+	xsltGenericDebug(xsltGenericDebugContext,
+		"attribute set %s present already in top stylesheet"
+		" - merging\n", name);
+#endif
+	topSet = xmlHashLookup2(style->attributeSets, name, ns);
+	if (topSet==NULL) {
+	    xsltGenericError(xsltGenericErrorContext,
+	        "xsl:attribute-set : logic error merging from imports for"
+		" attribute-set %s\n", name);
+	} else {
+	    topSet = xsltMergeAttrElemList(style, topSet, values);
+	    xmlHashUpdateEntry2(style->attributeSets, name, ns, topSet, NULL);
+	}
+	xsltFreeAttrElemList(values);
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+    } else {
+	xsltGenericDebug(xsltGenericDebugContext,
+		"attribute set %s moved to top stylesheet\n",
+		         name);
+#endif
+    }
+}
+
+/**
+ * xsltResolveStylesheetAttributeSet:
+ * @style:  the XSLT stylesheet
+ *
+ * resolve the references between attribute sets.
+ */
+void
+xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) {
+    xsltStylesheetPtr cur;
+
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+    xsltGenericDebug(xsltGenericDebugContext,
+	    "Resolving attribute sets references\n");
+#endif
+    /*
+     * First aggregate all the attribute sets definitions from the imports
+     */
+    cur = xsltNextImport(style);
+    while (cur != NULL) {
+	if (cur->attributeSets != NULL) {
+	    if (style->attributeSets == NULL) {
+#ifdef WITH_XSLT_DEBUG_ATTRIBUTES
+		xsltGenericDebug(xsltGenericDebugContext,
+		    "creating attribute set table\n");
+#endif
+		style->attributeSets = xmlHashCreate(10);
+	    }
+	    xmlHashScanFull(cur->attributeSets, 
+		(xmlHashScannerFull) xsltMergeSASCallback, style);
+	    /*
+	     * the attribute lists have either been migrated to style
+	     * or freed directly in xsltMergeSASCallback()
+	     */
+	    xmlHashFree(cur->attributeSets, NULL);
+	    cur->attributeSets = NULL;
+	}
+	cur = xsltNextImport(cur);
+    }
+
+    /*
+     * Then resolve all the references and computes the resulting sets
+     */
+    if (style->attributeSets != NULL) {
+	xmlHashScanFull(style->attributeSets, 
+		(xmlHashScannerFull) xsltResolveSASCallback, style);
+    }
+}
+
+/**
+ * xsltAttributeInternal:
+ * @ctxt:  a XSLT process context
+ * @node:  the current node in the source tree
+ * @inst:  the xsl:attribute element
+ * @comp:  precomputed information
+ * @fromAttributeSet:  the attribute comes from an attribute-set
+ *
+ * Process the xslt attribute node on the source node
+ */
+static void
+xsltAttributeInternal(xsltTransformContextPtr ctxt,
+		      xmlNodePtr contextNode,
+                      xmlNodePtr inst,
+		      xsltStylePreCompPtr castedComp,
+                      int fromAttributeSet)
+{
+#ifdef XSLT_REFACTORED
+    xsltStyleItemAttributePtr comp =
+	(xsltStyleItemAttributePtr) castedComp;   
+#else
+    xsltStylePreCompPtr comp = castedComp;
+#endif
+    xmlNodePtr targetElem;
+    xmlChar *prop = NULL;    
+    const xmlChar *name = NULL, *prefix = NULL, *nsName = NULL;
+    xmlChar *value = NULL;
+    xmlNsPtr ns = NULL;
+    xmlAttrPtr attr;    
+
+    if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL))
+        return;
+
+    /* 
+    * A comp->has_name == 0 indicates that we need to skip this instruction,
+    * since it was evaluated to be invalid already during compilation.
+    */
+    if (!comp->has_name)
+        return;
+    /*
+    * BIG NOTE: This previously used xsltGetSpecialNamespace() and
+    *  xsltGetNamespace(), but since both are not appropriate, we
+    *  will process namespace lookup here to avoid adding yet another
+    *  ns-lookup function to namespaces.c.
+    */
+    /*
+    * SPEC XSLT 1.0: Error cases:
+    * - Creating nodes other than text nodes during the instantiation of
+    *   the content of the xsl:attribute element; implementations may
+    *   either signal the error or ignore the offending nodes."
+    */
+
+    if (comp == NULL) {
+        xsltTransformError(ctxt, NULL, inst,
+	    "Internal error in xsltAttributeInternal(): "
+	    "The XSLT 'attribute' instruction was not compiled.\n");
+        return;
+    }
+    /*
+    * TODO: Shouldn't ctxt->insert == NULL be treated as an internal error?
+    *   So report an internal error?
+    */
+    if (ctxt->insert == NULL)
+        return;    
+    /*
+    * SPEC XSLT 1.0:
+    *  "Adding an attribute to a node that is not an element;
+    *  implementations may either signal the error or ignore the attribute."
+    *
+    * TODO: I think we should signal such errors in the future, and maybe
+    *  provide an option to ignore such errors.
+    */
+    targetElem = ctxt->insert;
+    if (targetElem->type != XML_ELEMENT_NODE)
+	return;
+    
+    /*
+    * SPEC XSLT 1.0:
+    * "Adding an attribute to an element after children have been added
+    *  to it; implementations may either signal the error or ignore the
+    *  attribute."
+    *
+    * TODO: We should decide whether not to report such errors or
+    *  to ignore them; note that we *ignore* if the parent is not an
+    *  element, but here we report an error.
+    */
+    if (targetElem->children != NULL) {
+	/*
+	* NOTE: Ah! This seems to be intended to support streamed
+	*  result generation!.
+	*/
+        xsltTransformError(ctxt, NULL, inst,
+	    "xsl:attribute: Cannot add attributes to an "
+	    "element if children have been already added "
+	    "to the element.\n");
+        return;
+    }
+
+    /*
+    * Process the name
+    * ----------------
+    */    
+
+#ifdef WITH_DEBUGGER
+    if (ctxt->debugStatus != XSLT_DEBUG_NONE)
+        xslHandleDebugger(inst, contextNode, NULL, ctxt);
+#endif
+
+    if (comp->name == NULL) {
+	/* TODO: fix attr acquisition wrt to the XSLT namespace */
+        prop = xsltEvalAttrValueTemplate(ctxt, inst,
+	    (const xmlChar *) "name", XSLT_NAMESPACE);
+        if (prop == NULL) {
+            xsltTransformError(ctxt, NULL, inst,
+		"xsl:attribute: The attribute 'name' is missing.\n");
+            goto error;
+        }
+	if (xmlValidateQName(prop, 0)) {
+	    xsltTransformError(ctxt, NULL, inst,
+		"xsl:attribute: The effective name '%s' is not a "
+		"valid QName.\n", prop);
+	    /* we fall through to catch any further errors, if possible */
+	}
+	name = xsltSplitQName(ctxt->dict, prop, &prefix);
+	xmlFree(prop);
+
+	/*
+	* Reject a prefix of "xmlns".
+	*/
+	if ((prefix != NULL) &&
+	    (!xmlStrncasecmp(prefix, (xmlChar *) "xmlns", 5)))
+	{
+#ifdef WITH_XSLT_DEBUG_PARSING
+	    xsltGenericDebug(xsltGenericDebugContext,
+		"xsltAttribute: xmlns prefix forbidden\n");
+#endif
+	    /*
+	    * SPEC XSLT 1.0:
+	    *  "It is an error if the string that results from instantiating
+	    *  the attribute value template is not a QName or is the string
+	    *  xmlns. An XSLT processor may signal the error; if it does not
+	    *  signal the error, it must recover by not adding the attribute
+	    *  to the result tree."
+	    * TODO: Decide which way to go here.
+	    */
+	    goto error;
+	}
+
+    } else {
+	/*
+	* The "name" value was static.
+	*/
+#ifdef XSLT_REFACTORED
+	prefix = comp->nsPrefix;
+	name = comp->name;
+#else
+	name = xsltSplitQName(ctxt->dict, comp->name, &prefix);
+#endif
+    }
+    
+    /*
+    * Process namespace semantics
+    * ---------------------------
+    *
+    * Evaluate the namespace name.
+    */
+    if (comp->has_ns) {
+	/*
+	* The "namespace" attribute was existent.
+	*/
+	if (comp->ns != NULL) {
+	    /*
+	    * No AVT; just plain text for the namespace name.
+	    */
+	    if (comp->ns[0] != 0)
+		nsName = comp->ns;
+	} else {
+	    xmlChar *tmpNsName;
+	    /*
+	    * Eval the AVT.
+	    */
+	    /* TODO: check attr acquisition wrt to the XSLT namespace */
+	    tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst,
+		(const xmlChar *) "namespace", XSLT_NAMESPACE);	
+	    /*
+	    * This fixes bug #302020: The AVT might also evaluate to the 
+	    * empty string; this means that the empty string also indicates
+	    * "no namespace".
+	    * SPEC XSLT 1.0:
+	    *  "If the string is empty, then the expanded-name of the
+	    *  attribute has a null namespace URI."
+	    */
+	    if ((tmpNsName != NULL) && (tmpNsName[0] != 0))
+		nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1);
+	    xmlFree(tmpNsName);		
+	};	    
+    } else if (prefix != NULL) {
+	/*
+	* SPEC XSLT 1.0:
+	*  "If the namespace attribute is not present, then the QName is
+	*  expanded into an expanded-name using the namespace declarations
+	*  in effect for the xsl:attribute element, *not* including any
+	*  default namespace declaration."
+	*/	
+	ns = xmlSearchNs(inst->doc, inst, prefix);
+	if (ns == NULL) {
+	    /*
+	    * Note that this is treated as an error now (checked with
+	    *  Saxon, Xalan-J and MSXML).
+	    */
+	    xsltTransformError(ctxt, NULL, inst,
+		"xsl:attribute: The QName '%s:%s' has no "
+		"namespace binding in scope in the stylesheet; "
+		"this is an error, since the namespace was not "
+		"specified by the instruction itself.\n", prefix, name);
+	} else
+	    nsName = ns->href;	
+    }
+
+    if (fromAttributeSet) {
+	/*
+	* This tries to ensure that xsl:attribute(s) coming
+	* from an xsl:attribute-set won't override attribute of
+	* literal result elements or of explicit xsl:attribute(s).
+	* URGENT TODO: This might be buggy, since it will miss to
+	*  overwrite two equal attributes both from attribute sets.
+	*/
+	attr = xmlHasNsProp(targetElem, name, nsName);
+	if (attr != NULL)
+	    return;
+    }
+
+    /*
+    * Find/create a matching ns-decl in the result tree.
+    */
+    ns = NULL;
+    
+#if 0
+    if (0) {	
+	/*
+	* OPTIMIZE TODO: How do we know if we are adding to a
+	*  fragment or to the result tree?
+	*
+	* If we are adding to a result tree fragment (i.e., not to the
+	* actual result tree), we'll don't bother searching for the
+	* ns-decl, but just store it in the dummy-doc of the result
+	* tree fragment.
+	*/
+	if (nsName != NULL) {
+	    /*
+	    * TODO: Get the doc of @targetElem.
+	    */
+	    ns = xsltTreeAcquireStoredNs(some doc, nsName, prefix);
+	}
+    }
+#endif
+
+    if (nsName != NULL) {	
+	/*
+	* Something about ns-prefixes:
+	* SPEC XSLT 1.0:
+	*  "XSLT processors may make use of the prefix of the QName specified
+	*  in the name attribute when selecting the prefix used for outputting
+	*  the created attribute as XML; however, they are not required to do
+	*  so and, if the prefix is xmlns, they must not do so"
+	*/
+	/*
+	* xsl:attribute can produce a scenario where the prefix is NULL,
+	* so generate a prefix.
+	*/
+	if (prefix == NULL) {
+	    xmlChar *pref = xmlStrdup(BAD_CAST "ns_1");
+
+	    ns = xsltGetSpecialNamespace(ctxt, inst, nsName, BAD_CAST pref,
+		targetElem);
+
+	    xmlFree(pref);
+	} else {
+	    ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix,
+		targetElem);
+	}
+	if (ns == NULL) {
+	    xsltTransformError(ctxt, NULL, inst,
+		"Namespace fixup error: Failed to acquire an in-scope "
+		"namespace binding for the generated attribute '{%s}%s'.\n",
+		nsName, name);
+	    goto error;
+	}
+    }
+    /*
+    * Construction of the value
+    * -------------------------
+    */
+    if (inst->children == NULL) {
+	/*
+	* No content.
+	* TODO: Do we need to put the empty string in ?
+	*/
+	attr = xmlSetNsProp(ctxt->insert, ns, name, (const xmlChar *) "");
+    } else if ((inst->children->next == NULL) && 
+	    ((inst->children->type == XML_TEXT_NODE) ||
+	     (inst->children->type == XML_CDATA_SECTION_NODE)))
+    {
+	xmlNodePtr copyTxt;
+	
+	/*
+	* xmlSetNsProp() will take care of duplicates.
+	*/
+	attr = xmlSetNsProp(ctxt->insert, ns, name, NULL);
+	if (attr == NULL) /* TODO: report error ? */
+	    goto error;
+	/*
+	* This was taken over from xsltCopyText() (transform.c).
+	*/
+	if (ctxt->internalized &&
+	    (ctxt->insert->doc != NULL) &&
+	    (ctxt->insert->doc->dict == ctxt->dict))
+	{
+	    copyTxt = xmlNewText(NULL);
+	    if (copyTxt == NULL) /* TODO: report error */
+		goto error;
+	    /*
+	    * This is a safe scenario where we don't need to lookup
+	    * the dict.
+	    */
+	    copyTxt->content = inst->children->content;
+	    /*
+	    * Copy "disable-output-escaping" information.
+	    * TODO: Does this have any effect for attribute values
+	    *  anyway?
+	    */
+	    if (inst->children->name == xmlStringTextNoenc)
+		copyTxt->name = xmlStringTextNoenc;
+	} else {
+	    /*
+	    * Copy the value.
+	    */
+	    copyTxt = xmlNewText(inst->children->content);
+	    if (copyTxt == NULL) /* TODO: report error */
+		goto error;	    	    
+	}
+	attr->children = attr->last = copyTxt;
+	copyTxt->parent = (xmlNodePtr) attr;
+	copyTxt->doc = attr->doc;
+	/*
+	* Copy "disable-output-escaping" information.
+	* TODO: Does this have any effect for attribute values
+	*  anyway?
+	*/
+	if (inst->children->name == xmlStringTextNoenc)
+	    copyTxt->name = xmlStringTextNoenc;	
+
+        /*
+         * 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;    
+}
+
+/**
+ * xsltAttribute:
+ * @ctxt:  a XSLT process context
+ * @node:  the node in the source tree.
+ * @inst:  the xslt attribute node
+ * @comp:  precomputed information
+ *
+ * Process the xslt attribute node on the source node
+ */
+void
+xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node,
+	      xmlNodePtr inst, xsltStylePreCompPtr comp) {
+    xsltAttributeInternal(ctxt, node, inst, comp, 0);
+}
+
+/**
+ * xsltApplyAttributeSet:
+ * @ctxt:  the XSLT stylesheet
+ * @node:  the node in the source tree.
+ * @inst:  the attribute node "xsl:use-attribute-sets"
+ * @attrSets:  the list of QNames of the attribute-sets to be applied
+ *
+ * Apply the xsl:use-attribute-sets.
+ * If @attrSets is NULL, then @inst will be used to exctract this
+ * value.
+ * If both, @attrSets and @inst, are NULL, then this will do nothing.
+ */
+void
+xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node,
+                      xmlNodePtr inst,
+                      const xmlChar *attrSets)
+{
+    const xmlChar *ncname = NULL;
+    const xmlChar *prefix = NULL;    
+    const xmlChar *curstr, *endstr;
+    xsltAttrElemPtr attrs;
+    xsltStylesheetPtr style;    
+
+    if (attrSets == NULL) {
+	if (inst == NULL)
+	    return;
+	else {
+	    /*
+	    * Extract the value from @inst.
+	    */
+	    if (inst->type == XML_ATTRIBUTE_NODE) {
+		if ( ((xmlAttrPtr) inst)->children != NULL)
+		    attrSets = ((xmlAttrPtr) inst)->children->content;
+		
+	    }
+	    if (attrSets == NULL) {
+		/*
+		* TODO: Return an error?
+		*/
+		return;
+	    }
+	}
+    }
+    /*
+    * Parse/apply the list of QNames.
+    */
+    curstr = attrSets;
+    while (*curstr != 0) {
+        while (IS_BLANK(*curstr))
+            curstr++;
+        if (*curstr == 0)
+            break;
+        endstr = curstr;
+        while ((*endstr != 0) && (!IS_BLANK(*endstr)))
+            endstr++;
+        curstr = xmlDictLookup(ctxt->dict, curstr, endstr - curstr);
+        if (curstr) {
+	    /*
+	    * TODO: Validate the QName.
+	    */
+
+#ifdef WITH_XSLT_DEBUG_curstrUTES
+            xsltGenericDebug(xsltGenericDebugContext,
+                             "apply curstrute set %s\n", curstr);
+#endif
+            ncname = xsltSplitQName(ctxt->dict, curstr, &prefix);
+
+            style = ctxt->style;
+
+#ifdef WITH_DEBUGGER
+            if ((style != NULL) &&
+		(style->attributeSets != NULL) &&
+		(ctxt->debugStatus != XSLT_DEBUG_NONE))
+	    {
+                attrs =
+                    xmlHashLookup2(style->attributeSets, ncname, prefix);
+                if ((attrs != NULL) && (attrs->attr != NULL))
+                    xslHandleDebugger(attrs->attr->parent, node, NULL,
+			ctxt);
+            }
+#endif
+	    /*
+	    * Lookup the referenced curstrute-set.
+	    */
+            while (style != NULL) {
+                attrs =
+                    xmlHashLookup2(style->attributeSets, ncname, prefix);
+                while (attrs != NULL) {
+                    if (attrs->attr != NULL) {
+                        xsltAttributeInternal(ctxt, node, attrs->attr,
+			    attrs->attr->psvi, 1);
+                    }
+                    attrs = attrs->next;
+                }
+                style = xsltNextImport(style);
+            }
+        }
+        curstr = endstr;
+    }
+}
+
+/**
+ * xsltFreeAttributeSetsHashes:
+ * @style: an XSLT stylesheet
+ *
+ * Free up the memory used by attribute sets
+ */
+void
+xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) {
+    if (style->attributeSets != NULL)
+	xmlHashFree((xmlHashTablePtr) style->attributeSets,
+		    (xmlHashDeallocator) xsltFreeAttrElemList);
+    style->attributeSets = NULL;
+}
diff --git a/libxslt/attributes.h b/libxslt/attributes.h
new file mode 100644
index 0000000..d61ebb0
--- /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..bda0910
--- /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..128cefe
--- /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..2eb1f2a
--- /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..6187b7a
--- /dev/null
+++ b/libxslt/extensions.c
@@ -0,0 +1,2361 @@
+/*
+ * 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++;
+    }
+
+    if (*(i - 1) == '_')
+        *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;
+
+    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 (0);
+}
+
+/**
+ * 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;
+    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..3a0f547
--- /dev/null
+++ b/libxslt/extra.c
@@ -0,0 +1,329 @@
+/*
+ * 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
+     */
+    lmt = gmt - timezone;
+
+    /*
+     * 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..59e932b
--- /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..4720c7a
--- /dev/null
+++ b/libxslt/functions.c
@@ -0,0 +1,975 @@
+/*
+ * 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
+    xmlFree(fragment);	
+
+    if (resObj == NULL)
+	goto out_fragment;
+	
+    switch (resObj->type) {
+	case XPATH_NODESET:
+	    break;
+	case XPATH_UNDEFINED:
+	case XPATH_BOOLEAN:
+	case XPATH_NUMBER:
+	case XPATH_STRING:
+	case XPATH_POINT:
+	case XPATH_USERS:
+	case XPATH_XSLT_TREE:
+	case XPATH_RANGE:
+	case XPATH_LOCATIONSET:
+	    xsltTransformError(tctxt, NULL, NULL,
+		"document() : XPointer does not select a node set: #%s\n", 
+		fragment);
+	goto out_object;
+    }
+    
+    valuePush(ctxt, resObj);
+    return;
+
+out_object:
+    xmlXPathFreeObject(resObj);
+
+out_fragment:
+    valuePush(ctxt, xmlXPathNewNodeSet(NULL));
+}
+
+/**
+ * xsltDocumentFunction:
+ * @ctxt:  the XPath Parser context
+ * @nargs:  the number of arguments
+ *
+ * Implement the document() XSLT function
+ *   node-set document(object, node-set?)
+ */
+void
+xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+    xmlXPathObjectPtr obj, obj2 = NULL;
+    xmlChar *base = NULL, *URI;
+
+
+    if ((nargs < 1) || (nargs > 2)) {
+        xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+                         "document() : invalid number of args %d\n",
+                         nargs);
+        ctxt->error = XPATH_INVALID_ARITY;
+        return;
+    }
+    if (ctxt->value == NULL) {
+        xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+                         "document() : invalid arg value\n");
+        ctxt->error = XPATH_INVALID_TYPE;
+        return;
+    }
+
+    if (nargs == 2) {
+        if (ctxt->value->type != XPATH_NODESET) {
+            xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+                             "document() : invalid arg expecting a nodeset\n");
+            ctxt->error = XPATH_INVALID_TYPE;
+            return;
+        }
+
+        obj2 = valuePop(ctxt);
+    }
+
+    if (ctxt->value->type == XPATH_NODESET) {
+        int i;
+        xmlXPathObjectPtr newobj, ret;
+
+        obj = valuePop(ctxt);
+        ret = xmlXPathNewNodeSet(NULL);
+
+        if (obj->nodesetval) {
+            for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+                valuePush(ctxt,
+                          xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
+                xmlXPathStringFunction(ctxt, 1);
+                if (nargs == 2) {
+                    valuePush(ctxt, xmlXPathObjectCopy(obj2));
+                } else {
+                    valuePush(ctxt,
+                              xmlXPathNewNodeSet(obj->nodesetval->
+                                                 nodeTab[i]));
+                }
+                xsltDocumentFunction(ctxt, 2);
+                newobj = valuePop(ctxt);
+                ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
+                                                       newobj->nodesetval);
+                xmlXPathFreeObject(newobj);
+            }
+        }
+
+        xmlXPathFreeObject(obj);
+        if (obj2 != NULL)
+            xmlXPathFreeObject(obj2);
+        valuePush(ctxt, ret);
+        return;
+    }
+    /*
+     * Make sure it's converted to a string
+     */
+    xmlXPathStringFunction(ctxt, 1);
+    if (ctxt->value->type != XPATH_STRING) {
+        xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+                         "document() : invalid arg expecting a string\n");
+        ctxt->error = XPATH_INVALID_TYPE;
+        if (obj2 != NULL)
+            xmlXPathFreeObject(obj2);
+        return;
+    }
+    obj = valuePop(ctxt);
+    if (obj->stringval == NULL) {
+        valuePush(ctxt, xmlXPathNewNodeSet(NULL));
+    } else {
+        if ((obj2 != NULL) && (obj2->nodesetval != NULL) &&
+            (obj2->nodesetval->nodeNr > 0) &&
+            IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) {
+            xmlNodePtr target;
+
+            target = obj2->nodesetval->nodeTab[0];
+            if ((target->type == XML_ATTRIBUTE_NODE) ||
+	        (target->type == XML_PI_NODE)) {
+                target = ((xmlAttrPtr) target)->parent;
+            }
+            base = xmlNodeGetBase(target->doc, target);
+        } else {
+            xsltTransformContextPtr tctxt;
+
+            tctxt = xsltXPathGetTransformContext(ctxt);
+            if ((tctxt != NULL) && (tctxt->inst != NULL)) {
+                base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst);
+            } else if ((tctxt != NULL) && (tctxt->style != NULL) &&
+                       (tctxt->style->doc != NULL)) {
+                base = xmlNodeGetBase(tctxt->style->doc,
+                                      (xmlNodePtr) tctxt->style->doc);
+            }
+        }
+        URI = xmlBuildURI(obj->stringval, base);
+        if (base != NULL)
+            xmlFree(base);
+        if (URI == NULL) {
+            valuePush(ctxt, xmlXPathNewNodeSet(NULL));
+        } else {
+	    xsltDocumentFunctionLoadDocument( ctxt, URI );
+	    xmlFree(URI);
+	}
+    }
+    xmlXPathFreeObject(obj);
+    if (obj2 != NULL)
+        xmlXPathFreeObject(obj2);
+}
+
+/**
+ * xsltKeyFunction:
+ * @ctxt:  the XPath Parser context
+ * @nargs:  the number of arguments
+ *
+ * Implement the key() XSLT function
+ *   node-set key(string, object)
+ */
+void
+xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){    
+    xmlXPathObjectPtr obj1, obj2;    
+    
+    if (nargs != 2) {
+	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+		"key() : expects two arguments\n");
+	ctxt->error = XPATH_INVALID_ARITY;
+	return;
+    }    
+
+    /*
+    * Get the key's value.
+    */
+    obj2 = valuePop(ctxt);
+    xmlXPathStringFunction(ctxt, 1);
+    if ((obj2 == NULL) ||
+	(ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
+	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+	    "key() : invalid arg expecting a string\n");
+	ctxt->error = XPATH_INVALID_TYPE;
+	xmlXPathFreeObject(obj2);
+
+	return;
+    }
+    /*
+    * Get the key's name.
+    */
+    obj1 = valuePop(ctxt);    
+
+    if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) {
+	int i;
+	xmlXPathObjectPtr newobj, ret;
+
+	ret = xmlXPathNewNodeSet(NULL);
+
+	if (obj2->nodesetval != NULL) {
+	    for (i = 0; i < obj2->nodesetval->nodeNr; i++) {
+		valuePush(ctxt, xmlXPathObjectCopy(obj1));
+		valuePush(ctxt,
+			  xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i]));
+		xmlXPathStringFunction(ctxt, 1);
+		xsltKeyFunction(ctxt, 2);
+		newobj = valuePop(ctxt);
+		ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
+						       newobj->nodesetval);
+		xmlXPathFreeObject(newobj);
+	    }
+	}
+	valuePush(ctxt, ret);
+    } else {
+	xmlNodeSetPtr nodelist = NULL;
+	xmlChar *key = NULL, *value;
+	const xmlChar *keyURI;
+	xsltTransformContextPtr tctxt;   
+	xmlChar *qname, *prefix;
+	xmlXPathContextPtr xpctxt = ctxt->context;
+	xmlNodePtr tmpNode = NULL;
+	xsltDocumentPtr oldDocInfo;
+
+	tctxt = xsltXPathGetTransformContext(ctxt);
+
+	oldDocInfo = tctxt->document;
+
+	if (xpctxt->node == NULL) {
+	    xsltTransformError(tctxt, NULL, tctxt->inst,
+		"Internal error in xsltKeyFunction(): "
+		"The context node is not set on the XPath context.\n");
+	    tctxt->state = XSLT_STATE_STOPPED;
+	    goto error;
+	}	
+	/*
+	 * Get the associated namespace URI if qualified name
+	 */
+	qname = obj1->stringval;
+	key = xmlSplitQName2(qname, &prefix);
+	if (key == NULL) {
+	    key = xmlStrdup(obj1->stringval);
+	    keyURI = NULL;
+	    if (prefix != NULL)
+		xmlFree(prefix);
+	} else {
+	    if (prefix != NULL) {
+		keyURI = xmlXPathNsLookup(xpctxt, prefix);
+		if (keyURI == NULL) {
+		    xsltTransformError(tctxt, NULL, tctxt->inst,
+			"key() : prefix %s is not bound\n", prefix);
+		    /*
+		    * TODO: Shouldn't we stop here?
+		    */
+		}
+		xmlFree(prefix);
+	    } else {
+		keyURI = NULL;
+	    }
+	}
+
+	/*
+	 * Force conversion of first arg to string
+	 */
+	valuePush(ctxt, obj2);
+	xmlXPathStringFunction(ctxt, 1);
+	if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
+	    xsltTransformError(tctxt, NULL, tctxt->inst,
+		"key() : invalid arg expecting a string\n");
+	    ctxt->error = XPATH_INVALID_TYPE;
+	    goto error;
+	}
+	obj2 = valuePop(ctxt);
+	value = obj2->stringval;
+	
+	/*
+	* We need to ensure that ctxt->document is available for
+	* xsltGetKey().
+	* First find the relevant doc, which is the context node's
+	* owner doc; using context->doc is not safe, since
+	* the doc could have been acquired via the document() function,
+	* or the doc might be a Result Tree Fragment.
+	* FUTURE INFO: In XSLT 2.0 the key() function takes an additional
+	* argument indicating the doc to use.
+	*/	
+	if (xpctxt->node->type == XML_NAMESPACE_DECL) {
+	    /*
+	    * REVISIT: This is a libxml hack! Check xpath.c for details.
+	    * The XPath module sets the owner element of a ns-node on
+	    * the ns->next field.
+	    */
+	    if ((((xmlNsPtr) xpctxt->node)->next != NULL) &&
+		(((xmlNsPtr) xpctxt->node)->next->type == XML_ELEMENT_NODE))
+	    {
+		tmpNode = (xmlNodePtr) ((xmlNsPtr) xpctxt->node)->next;
+	    }
+	} else
+	    tmpNode = xpctxt->node;
+
+	if ((tmpNode == NULL) || (tmpNode->doc == NULL)) {
+	    xsltTransformError(tctxt, NULL, tctxt->inst,
+		"Internal error in xsltKeyFunction(): "
+		"Couldn't get the doc of the XPath context node.\n");
+	    goto error;
+	}
+
+	if ((tctxt->document == NULL) ||
+	    (tctxt->document->doc != tmpNode->doc))
+	{	   
+	    if (tmpNode->doc->name && (tmpNode->doc->name[0] == ' ')) {
+		/*
+		* This is a Result Tree Fragment.
+		*/
+		if (tmpNode->doc->_private == NULL) {
+		    tmpNode->doc->_private = xsltNewDocument(tctxt, tmpNode->doc);
+		    if (tmpNode->doc->_private == NULL)
+			goto error;
+		}
+		tctxt->document = (xsltDocumentPtr) tmpNode->doc->_private;		
+	    } else {
+		/*
+		* May be the initial source doc or a doc acquired via the
+		* document() function.
+		*/
+		tctxt->document = xsltFindDocument(tctxt, tmpNode->doc);
+	    }
+	    if (tctxt->document == NULL) {
+		xsltTransformError(tctxt, NULL, tctxt->inst,
+		    "Internal error in xsltKeyFunction(): "
+		    "Could not get the document info of a context doc.\n");
+		tctxt->state = XSLT_STATE_STOPPED;
+		goto error;
+	    }
+	}
+	/*
+	* Get/compute the key value.
+	*/
+	nodelist = xsltGetKey(tctxt, key, keyURI, value);
+
+error:	
+	tctxt->document = oldDocInfo;
+	valuePush(ctxt, xmlXPathWrapNodeSet(
+	    xmlXPathNodeSetMerge(NULL, nodelist)));
+	if (key != NULL)
+	    xmlFree(key);
+    }    
+
+    if (obj1 != NULL)
+	xmlXPathFreeObject(obj1);
+    if (obj2 != NULL)
+	xmlXPathFreeObject(obj2);    
+}
+
+/**
+ * xsltUnparsedEntityURIFunction:
+ * @ctxt:  the XPath Parser context
+ * @nargs:  the number of arguments
+ *
+ * Implement the unparsed-entity-uri() XSLT function
+ *   string unparsed-entity-uri(string)
+ */
+void
+xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){
+    xmlXPathObjectPtr obj;
+    xmlChar *str;
+
+    if ((nargs != 1) || (ctxt->value == NULL)) {
+        xsltGenericError(xsltGenericErrorContext,
+		"unparsed-entity-uri() : expects one string arg\n");
+	ctxt->error = XPATH_INVALID_ARITY;
+	return;
+    }
+    obj = valuePop(ctxt);
+    if (obj->type != XPATH_STRING) {
+	obj = xmlXPathConvertString(obj);
+    }
+
+    str = obj->stringval;
+    if (str == NULL) {
+	valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
+    } else {
+	xmlEntityPtr entity;
+
+	entity = xmlGetDocEntity(ctxt->context->doc, str);
+	if (entity == NULL) {
+	    valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
+	} else {
+	    if (entity->URI != NULL)
+		valuePush(ctxt, xmlXPathNewString(entity->URI));
+	    else
+		valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
+	}
+    }
+    xmlXPathFreeObject(obj);
+}
+
+/**
+ * xsltFormatNumberFunction:
+ * @ctxt:  the XPath Parser context
+ * @nargs:  the number of arguments
+ *
+ * Implement the format-number() XSLT function
+ *   string format-number(number, string, string?)
+ */
+void
+xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+    xmlXPathObjectPtr numberObj = NULL;
+    xmlXPathObjectPtr formatObj = NULL;
+    xmlXPathObjectPtr decimalObj = NULL;
+    xsltStylesheetPtr sheet;
+    xsltDecimalFormatPtr formatValues;
+    xmlChar *result;
+    xsltTransformContextPtr tctxt;
+
+    tctxt = xsltXPathGetTransformContext(ctxt);
+    if (tctxt == NULL)
+	return;
+    sheet = tctxt->style;
+    if (sheet == NULL)
+	return;
+    formatValues = sheet->decimalFormat;
+    
+    switch (nargs) {
+    case 3:
+	CAST_TO_STRING;
+	decimalObj = valuePop(ctxt);
+	formatValues = xsltDecimalFormatGetByName(sheet, decimalObj->stringval);
+	if (formatValues == NULL) {
+	    xsltTransformError(tctxt, NULL, NULL,
+		    "format-number() : undeclared decimal format '%s'\n", 
+		    decimalObj->stringval);
+	}
+	/* Intentional fall-through */
+    case 2:
+	CAST_TO_STRING;
+	formatObj = valuePop(ctxt);
+	CAST_TO_NUMBER;
+	numberObj = valuePop(ctxt);
+	break;
+    default:
+	XP_ERROR(XPATH_INVALID_ARITY);
+    }
+
+    if (formatValues != NULL) {
+	if (xsltFormatNumberConversion(formatValues,
+				       formatObj->stringval,
+				       numberObj->floatval,
+				       &result) == XPATH_EXPRESSION_OK) {
+	    valuePush(ctxt, xmlXPathNewString(result));
+	    xmlFree(result);
+	}
+    }
+
+    xmlXPathFreeObject(numberObj);
+    xmlXPathFreeObject(formatObj);
+    xmlXPathFreeObject(decimalObj);
+}
+
+/**
+ * xsltGenerateIdFunction:
+ * @ctxt:  the XPath Parser context
+ * @nargs:  the number of arguments
+ *
+ * Implement the generate-id() XSLT function
+ *   string generate-id(node-set?)
+ */
+void
+xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
+    xmlNodePtr cur = NULL;
+    unsigned long val;
+    xmlChar str[20];
+
+    if (nargs == 0) {
+	cur = ctxt->context->node;
+    } else if (nargs == 1) {
+	xmlXPathObjectPtr obj;
+	xmlNodeSetPtr nodelist;
+	int i, ret;
+
+	if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) {
+	    ctxt->error = XPATH_INVALID_TYPE;
+	    xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+		"generate-id() : invalid arg expecting a node-set\n");
+	    return;
+	}
+	obj = valuePop(ctxt);
+	nodelist = obj->nodesetval;
+	if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) {
+	    xmlXPathFreeObject(obj);
+	    valuePush(ctxt, xmlXPathNewCString(""));
+	    return;
+	}
+	cur = nodelist->nodeTab[0];
+	for (i = 1;i < nodelist->nodeNr;i++) {
+	    ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]);
+	    if (ret == -1)
+	        cur = nodelist->nodeTab[i];
+	}
+	xmlXPathFreeObject(obj);
+    } else {
+	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+		"generate-id() : invalid number of args %d\n", nargs);
+	ctxt->error = XPATH_INVALID_ARITY;
+	return;
+    }
+    /*
+     * Okay this is ugly but should work, use the NodePtr address
+     * to forge the ID
+     */
+    val = (unsigned long)((char *)cur - (char *)0);
+    val /= sizeof(xmlNode);
+    sprintf((char *)str, "id%ld", val);
+    valuePush(ctxt, xmlXPathNewString(str));
+}
+
+/**
+ * xsltSystemPropertyFunction:
+ * @ctxt:  the XPath Parser context
+ * @nargs:  the number of arguments
+ *
+ * Implement the system-property() XSLT function
+ *   object system-property(string)
+ */
+void
+xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){
+    xmlXPathObjectPtr obj;
+    xmlChar *prefix, *name;
+    const xmlChar *nsURI = NULL;
+
+    if (nargs != 1) {
+	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+		"system-property() : expects one string arg\n");
+	ctxt->error = XPATH_INVALID_ARITY;
+	return;
+    }
+    if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
+	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+	    "system-property() : invalid arg expecting a string\n");
+	ctxt->error = XPATH_INVALID_TYPE;
+	return;
+    }
+    obj = valuePop(ctxt);
+    if (obj->stringval == NULL) {
+	valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
+    } else {
+	name = xmlSplitQName2(obj->stringval, &prefix);
+	if (name == NULL) {
+	    name = xmlStrdup(obj->stringval);
+	} else {
+	    nsURI = xmlXPathNsLookup(ctxt->context, prefix);
+	    if (nsURI == NULL) {
+		xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+		    "system-property() : prefix %s is not bound\n", prefix);
+	    }
+	}
+
+	if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) {
+#ifdef DOCBOOK_XSL_HACK
+	    if (xmlStrEqual(name, (const xmlChar *)"vendor")) {
+		xsltStylesheetPtr sheet;
+		xsltTransformContextPtr tctxt;
+
+		tctxt = xsltXPathGetTransformContext(ctxt);
+		if ((tctxt != NULL) && (tctxt->inst != NULL) &&
+		    (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) &&
+		    (tctxt->inst->parent != NULL) &&
+		    (xmlStrEqual(tctxt->inst->parent->name,
+				 BAD_CAST "template")))
+		    sheet = tctxt->style;
+		else
+		    sheet = NULL;
+		if ((sheet != NULL) && (sheet->doc != NULL) &&
+		    (sheet->doc->URL != NULL) &&
+		    (xmlStrstr(sheet->doc->URL,
+			       (const xmlChar *)"chunk") != NULL)) {
+		    valuePush(ctxt, xmlXPathNewString(
+			(const xmlChar *)"libxslt (SAXON 6.2 compatible)"));
+
+		} else {
+		    valuePush(ctxt, xmlXPathNewString(
+			(const xmlChar *)XSLT_DEFAULT_VENDOR));
+		}
+	    } else
+#else
+	    if (xmlStrEqual(name, (const xmlChar *)"vendor")) {
+		valuePush(ctxt, xmlXPathNewString(
+			  (const xmlChar *)XSLT_DEFAULT_VENDOR));
+	    } else
+#endif
+	    if (xmlStrEqual(name, (const xmlChar *)"version")) {
+		valuePush(ctxt, xmlXPathNewString(
+		    (const xmlChar *)XSLT_DEFAULT_VERSION));
+	    } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) {
+		valuePush(ctxt, xmlXPathNewString(
+		    (const xmlChar *)XSLT_DEFAULT_URL));
+	    } else {
+		valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
+	    }
+	}
+	if (name != NULL)
+	    xmlFree(name);
+	if (prefix != NULL)
+	    xmlFree(prefix);
+    }
+    xmlXPathFreeObject(obj);
+}
+
+/**
+ * xsltElementAvailableFunction:
+ * @ctxt:  the XPath Parser context
+ * @nargs:  the number of arguments
+ *
+ * Implement the element-available() XSLT function
+ *   boolean element-available(string)
+ */
+void
+xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
+    xmlXPathObjectPtr obj;
+    xmlChar *prefix, *name;
+    const xmlChar *nsURI = NULL;
+    xsltTransformContextPtr tctxt;
+
+    if (nargs != 1) {
+	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
+		"element-available() : expects one string arg\n");
+	ctxt->error = XPATH_INVALID_ARITY;
+	return;
+    }
+    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..caa4941
--- /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..23538ab
--- /dev/null
+++ b/libxslt/imports.c
@@ -0,0 +1,419 @@
+/*
+ * 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) {
+	cur = style->templates;
+	while (cur != NULL) {
+	    if (xmlStrEqual(name, cur->name)) {
+		if (((nameURI == NULL) && (cur->nameURI == NULL)) ||
+		    ((nameURI != NULL) && (cur->nameURI != NULL) &&
+		     (xmlStrEqual(nameURI, cur->nameURI)))) {
+		    return(cur);
+		}
+	    }
+	    cur = cur->next;
+	}
+
+	style = xsltNextImport(style);
+    }
+    return(NULL);
+}
+
diff --git a/libxslt/imports.h b/libxslt/imports.h
new file mode 100644
index 0000000..38656f1
--- /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..d28aea6
--- /dev/null
+++ b/libxslt/keys.c
@@ -0,0 +1,919 @@
+/*
+ * 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 "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,
+		                       "key pattern is malformed: %s",
+				       key->match);
+		    if (style != NULL) style->errors++;
+		    goto error;
+		}
+	    } else
+		end++;
+	}
+	if (current == end) {
+	    xsltTransformError(NULL, style, inst,
+			       "key pattern is empty\n");
+	    if (style != NULL) style->errors++;
+	    goto error;
+	}
+	if (match[start] != '/') {
+	    pattern = xmlStrcat(pattern, (xmlChar *)"//");
+	    if (pattern == NULL) {
+		if (style != NULL) style->errors++;
+		goto error;
+	    }
+	}
+	pattern = xmlStrncat(pattern, &match[start], end - start);
+	if (pattern == NULL) {
+	    if (style != NULL) style->errors++;
+	    goto error;
+	}
+
+	if (match[end] == '|') {
+	    pattern = xmlStrcat(pattern, (xmlChar *)"|");
+	    end++;
+	}
+	current = end;
+    }
+#ifdef WITH_XSLT_DEBUG_KEYS
+    xsltGenericDebug(xsltGenericDebugContext,
+	"   resulting pattern %s\n", pattern);
+#endif
+    /*
+    * XSLT-1: "It is an error for the value of either the use
+    *  attribute or the match attribute to contain a
+    *  VariableReference."
+    * TODO: We should report a variable-reference at compile-time.
+    *   Maybe a search for "$", if it occurs outside of quotation
+    *   marks, could be sufficient.
+    */
+    key->comp = xsltXPathCompile(style, pattern);
+    if (key->comp == NULL) {
+	xsltTransformError(NULL, style, inst,
+		"xsl:key : XPath pattern compilation failed '%s'\n",
+		         pattern);
+	if (style != NULL) style->errors++;
+    }
+    key->usecomp = xsltXPathCompile(style, use);
+    if (key->usecomp == NULL) {
+	xsltTransformError(NULL, style, inst,
+		"xsl:key : XPath pattern compilation failed '%s'\n",
+		         use);
+	if (style != NULL) style->errors++;
+    }
+
+    /*
+     * 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;
+	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..9182baa
--- /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.h b/libxslt/libxslt.h
new file mode 100644
index 0000000..2b9f52a
--- /dev/null
+++ b/libxslt/libxslt.h
@@ -0,0 +1,30 @@
+/*
+ * 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
+
+#endif /* ! __XSLT_LIBXSLT_H__ */
diff --git a/libxslt/namespaces.c b/libxslt/namespaces.c
new file mode 100644
index 0000000..3e3891f
--- /dev/null
+++ b/libxslt/namespaces.c
@@ -0,0 +1,851 @@
+/*
+ * 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 = "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)
+	        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..0bda596
--- /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..8683ca8
--- /dev/null
+++ b/libxslt/numbers.c
@@ -0,0 +1,1351 @@
+/*
+ * 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(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);
+
+    /* 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 (fabs(number) < 1.0)
+	    break; /* for */
+    }
+    xmlBufferCCat(buffer, pointer);
+}
+
+static void
+xsltNumberFormatRoman(xmlBufferPtr buffer,
+		      double number,
+		      int is_upper)
+{
+    /*
+     * 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];
+	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(buffer,
+					  number,
+					  TRUE);
+
+		    break;
+		case 'a':
+		    xsltNumberFormatAlpha(buffer,
+					  number,
+					  FALSE);
+
+		    break;
+		case 'I':
+		    xsltNumberFormatRoman(buffer,
+					  number,
+					  TRUE);
+
+		    break;
+		case 'i':
+		    xsltNumberFormatRoman(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
+xsltNumberFormatGetAnyLevel(xsltTransformContextPtr context,
+			    xmlNodePtr node,
+			    const xmlChar *count,
+			    const xmlChar *from,
+			    double *array,
+			    xmlDocPtr doc,
+			    xmlNodePtr elem)
+{
+    int amount = 0;
+    int cnt = 0;
+    xmlNodePtr cur;
+    xsltCompMatchPtr countPat = NULL;
+    xsltCompMatchPtr fromPat = NULL;
+
+    if (count != NULL)
+	countPat = xsltCompilePattern(count, doc, elem, NULL, context);
+    if (from != NULL)
+	fromPat = xsltCompilePattern(from, doc, elem, NULL, context);
+	
+    /* select the starting node */
+    switch (node->type) {
+	case XML_ELEMENT_NODE:
+	    cur = node;
+	    break;
+	case XML_ATTRIBUTE_NODE:
+	    cur = ((xmlAttrPtr) node)->parent;
+	    break;
+	case XML_TEXT_NODE:
+	case XML_PI_NODE:
+	case XML_COMMENT_NODE:
+	    cur = node->parent;
+	    break;
+	default:
+	    cur = NULL;
+	    break;
+    }
+
+    while (cur != NULL) {
+	/* process current node */
+	if (count == NULL) {
+	    if ((node->type == cur->type) &&
+		/* FIXME: must use expanded-name instead of local name */
+		xmlStrEqual(node->name, cur->name)) {
+		    if ((node->ns == cur->ns) ||
+		        ((node->ns != NULL) &&
+			 (cur->ns != NULL) &&
+		         (xmlStrEqual(node->ns->href,
+		             cur->ns->href) )))
+		        cnt++;
+	    }
+	} else {
+	    if (xsltTestCompMatchList(context, cur, countPat))
+		cnt++;
+	}
+	if ((from != 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 */
+
+	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;
+
+    if (countPat != NULL)
+	xsltFreeCompMatchList(countPat);
+    if (fromPat != NULL)
+	xsltFreeCompMatchList(fromPat);
+    return(amount);
+}
+
+static int
+xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context,
+				 xmlNodePtr node,
+				 const xmlChar *count,
+				 const xmlChar *from,
+				 double *array,
+				 int max,
+				 xmlDocPtr doc,
+				 xmlNodePtr elem)
+{
+    int amount = 0;
+    int cnt;
+    xmlNodePtr ancestor;
+    xmlNodePtr preceding;
+    xmlXPathParserContextPtr parser;
+    xsltCompMatchPtr countPat;
+    xsltCompMatchPtr fromPat;
+
+    if (count != NULL)
+	countPat = xsltCompilePattern(count, doc, elem, NULL, context);
+    else
+	countPat = NULL;
+    if (from != NULL)
+	fromPat = xsltCompilePattern(from, doc, elem, NULL, context);
+    else
+	fromPat = NULL;
+    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 ((from != NULL) &&
+		xsltTestCompMatchList(context, ancestor, fromPat))
+		break; /* for */
+	    
+	    if ((count == NULL && node->type == ancestor->type && 
+		xmlStrEqual(node->name, ancestor->name)) ||
+		xsltTestCompMatchList(context, ancestor, countPat)) {
+		/* count(preceding-sibling::*) */
+		cnt = 0;
+		for (preceding = ancestor;
+		     preceding != NULL;
+		     preceding = 
+		        xmlXPathNextPrecedingSibling(parser, preceding)) {
+		    if (count == NULL) {
+			if ((preceding->type == ancestor->type) &&
+			    xmlStrEqual(preceding->name, ancestor->name)){
+			    if ((preceding->ns == ancestor->ns) ||
+			        ((preceding->ns != NULL) &&
+				 (ancestor->ns != NULL) &&
+			         (xmlStrEqual(preceding->ns->href,
+			             ancestor->ns->href) )))
+			        cnt++;
+			}
+		    } else {
+			if (xsltTestCompMatchList(context, preceding,
+				                  countPat))
+			    cnt++;
+		    }
+		}
+		array[amount++] = (double)cnt;
+		if (amount >= max)
+		    break; /* for */
+	    }
+	}
+	xmlXPathFreeParserContext(parser);
+    }
+    xsltFreeCompMatchList(countPat);
+    xsltFreeCompMatchList(fromPat);
+    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;
+    int tempformat = 0;
+
+    if ((data->format == NULL) && (data->has_format != 0)) {
+	data->format = xsltEvalAttrValueTemplate(ctxt, data->node,
+					     (const xmlChar *) "format",
+					     XSLT_NAMESPACE);
+	tempformat = 1;
+    }
+    if (data->format == NULL) {
+	return;
+    }
+
+    output = xmlBufferCreate();
+    if (output == NULL)
+	goto XSLT_NUMBER_FORMAT_END;
+
+    xsltNumberFormatTokenize(data->format, &tokens);
+
+    /*
+     * 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->count,
+						      data->from,
+						      &number,
+						      1,
+						      data->doc,
+						      data->node);
+	    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->count,
+						      data->from,
+						      numarray,
+						      max,
+						      data->doc,
+						      data->node);
+	    if (amount > 0) {
+		xsltNumberFormatInsertNumbers(data,
+					      numarray,
+					      amount,
+					      &tokens,
+					      output);
+	    }
+	} else if (xmlStrEqual(data->level, (const xmlChar *) "any")) {
+	    amount = xsltNumberFormatGetAnyLevel(ctxt,
+						 node,
+						 data->count,
+						 data->from,
+						 &number, 
+						 data->doc,
+						 data->node);
+	    if (amount > 0) {
+		xsltNumberFormatInsertNumbers(data,
+					      &number,
+					      1,
+					      &tokens,
+					      output);
+	    }
+	}
+    }
+    /* Insert number as text node */
+    xsltCopyTextString(ctxt, ctxt->insert, xmlBufferContent(output), 0);
+
+    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);
+    }
+    
+XSLT_NUMBER_FORMAT_END:
+    if (tempformat == 1) {
+	/* The format need to be recomputed each time */
+	data->format = NULL;
+    }
+    if (output != NULL)
+	xmlBufferFree(output);
+}
+
+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 (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..7b3cb17
--- /dev/null
+++ b/libxslt/numbersInternals.h
@@ -0,0 +1,69 @@
+/*
+ * 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
+
+/**
+ * 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;
+
+    /*
+     * 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..8ce74e3
--- /dev/null
+++ b/libxslt/pattern.c
@@ -0,0 +1,2509 @@
+/*
+ * 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 "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++;
+	    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 indicating a direct
+     * matching should be done.
+     */
+    for (i = 0;i < comp->nbStep - 1;i++) {
+        if ((comp->steps[i].op == XSLT_OP_PREDICATE) &&
+	    (comp->steps[i + 1].op == XSLT_OP_PREDICATE)) {
+
+	    comp->direct = 1;
+	    if (comp->pattern[0] != '/') {
+		xmlChar *query;
+
+		query = xmlStrdup((const xmlChar *)"//");
+		query = xmlStrcat(query, comp->pattern);
+
+		xmlFree((xmlChar *) comp->pattern);
+		comp->pattern = query;
+	    }
+	    break;
+	}
+    }
+}
+
+/************************************************************************
+ * 									*
+ * 		The interpreter for the precompiled patterns		*
+ * 									*
+ ************************************************************************/
+
+static int
+xsltPatPushState(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;
+	xmlNsPtr *oldNamespaces;
+
+	oldnode = ctxt->xpathCtxt->node;
+	olddoc = ctxt->xpathCtxt->doc;
+	oldNsNr = ctxt->xpathCtxt->nsNr;
+	oldNamespaces = ctxt->xpathCtxt->namespaces;
+	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;
+	if (newlist == NULL)
+	    return(-1);
+	if (newlist->type != XPATH_NODESET) {
+	    xmlXPathFreeObject(newlist);
+	    return(-1);
+	}
+	ix = 0;
+
+	if ((parent == NULL) || (node->doc == NULL) || isRVT)
+	    nocache = 1;
+	
+	if (nocache == 0) {
+	    if (list != NULL)
+		xmlXPathFreeObject(list);
+	    list = newlist;
+
+	    XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) =
+		(void *) list;
+	    XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
+		(void *) doc;
+	    XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
+		0;
+	    XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) =
+		(xmlFreeFunc) xmlXPathFreeObject;
+	} else
+	    list = newlist;
+    }
+    if ((list->nodesetval == NULL) ||
+	(list->nodesetval->nodeNr <= 0)) {
+	if (nocache == 1)
+	    xmlXPathFreeObject(list);
+	return(0);
+    }
+    /* TODO: store the index and use it for the scan */
+    if (ix == 0) {
+	for (j = 0;j < list->nodesetval->nodeNr;j++) {
+	    if (list->nodesetval->nodeTab[j] == node) {
+		if (nocache == 1)
+		    xmlXPathFreeObject(list);
+		return(1);
+	    }
+	}
+    } else {
+    }
+    if (nocache == 1)
+	xmlXPathFreeObject(list);
+    return(0);
+}
+
+/**
+ * xsltTestCompMatch:
+ * @ctxt:  a XSLT process context
+ * @comp: the precompiled pattern
+ * @node: a node
+ * @mode:  the mode name or NULL
+ * @modeURI:  the mode URI or NULL
+ *
+ * Test whether the node matches the pattern
+ *
+ * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
+ */
+static int
+xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
+	          xmlNodePtr node, const xmlChar *mode,
+		  const xmlChar *modeURI) {
+    int i;
+    xsltStepOpPtr step, sel = NULL;
+    xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */
+
+    if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) {
+	xsltTransformError(ctxt, NULL, node,
+		"xsltTestCompMatch: null arg\n");
+        return(-1);
+    }
+    if (mode != NULL) {
+	if (comp->mode == NULL)
+	    return(0);
+	/*
+	 * both mode strings must be interned on the stylesheet dictionary
+	 */
+	if (comp->mode != mode)
+	    return(0);
+    } else {
+	if (comp->mode != NULL)
+	    return(0);
+    }
+    if (modeURI != NULL) {
+	if (comp->modeURI == NULL)
+	    return(0);
+	/*
+	 * both modeURI strings must be interned on the stylesheet dictionary
+	 */
+	if (comp->modeURI != modeURI)
+	    return(0);
+    } else {
+	if (comp->modeURI != NULL)
+	    return(0);
+    }
+
+    i = 0;
+restart:
+    for (;i < comp->nbStep;i++) {
+	step = &comp->steps[i];
+	if (step->op != XSLT_OP_PREDICATE)
+	    sel = step;
+	switch (step->op) {
+            case XSLT_OP_END:
+		goto found;
+            case XSLT_OP_ROOT:
+		if ((node->type == XML_DOCUMENT_NODE) ||
+#ifdef LIBXML_DOCB_ENABLED
+		    (node->type == XML_DOCB_DOCUMENT_NODE) ||
+#endif
+		    (node->type == XML_HTML_DOCUMENT_NODE))
+		    continue;
+		if ((node->type == XML_ELEMENT_NODE) && (node->name[0] == ' '))
+		    continue;
+		goto rollback;
+            case XSLT_OP_ELEM:
+		if (node->type != XML_ELEMENT_NODE)
+		    goto rollback;
+		if (step->value == NULL)
+		    continue;
+		if (step->value[0] != node->name[0])
+		    goto rollback;
+		if (!xmlStrEqual(step->value, node->name))
+		    goto rollback;
+
+		/* Namespace test */
+		if (node->ns == NULL) {
+		    if (step->value2 != NULL)
+			goto rollback;
+		} else if (node->ns->href != NULL) {
+		    if (step->value2 == NULL)
+			goto rollback;
+		    if (!xmlStrEqual(step->value2, node->ns->href))
+			goto rollback;
+		}
+		continue;
+            case XSLT_OP_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: {
+		xmlNodePtr oldNode;
+		xmlDocPtr doc;
+		int oldCS, oldCP;
+		int pos = 0, len = 0;
+		int isRVT;
+
+		/*
+		 * when there is cascading XSLT_OP_PREDICATE, then use a
+		 * direct computation approach. It's not done directly
+		 * at the beginning of the routine to filter out as much
+		 * as possible this costly computation.
+		 */
+		if (comp->direct) {
+		    if (states.states != NULL) {
+			/* Free the rollback states */
+			xmlFree(states.states);
+		    }
+		    return(xsltTestCompMatchDirect(ctxt, comp, node,
+		    				   comp->nsList, comp->nsNr));
+		}
+
+		doc = node->doc;
+		if (XSLT_IS_RES_TREE_FRAG(doc))
+		    isRVT = 1;
+		else
+		    isRVT = 0;
+
+		/*
+		 * Depending on the last selection, one may need to
+		 * recompute contextSize and proximityPosition.
+		 */
+		oldCS = ctxt->xpathCtxt->contextSize;
+		oldCP = ctxt->xpathCtxt->proximityPosition;
+		if ((sel != NULL) &&
+		    (sel->op == XSLT_OP_ELEM) &&
+		    (sel->value != NULL) &&
+		    (node->type == XML_ELEMENT_NODE) &&
+		    (node->parent != NULL)) {
+		    xmlNodePtr previous;
+		    int ix, nocache = 0;
+
+		    previous = (xmlNodePtr)
+			XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
+		    ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
+		    if ((previous != NULL) &&
+			(previous->parent == node->parent)) {
+			/*
+			 * just walk back to adjust the index
+			 */
+			int indx = 0;
+			xmlNodePtr sibling = node;
+
+			while (sibling != NULL) {
+			    if (sibling == previous)
+				break;
+			    if ((previous->type == XML_ELEMENT_NODE) &&
+				(previous->name != NULL) &&
+				(sibling->name != NULL) &&
+				(previous->name[0] == sibling->name[0]) &&
+				(xmlStrEqual(previous->name, sibling->name)))
+			    {
+				if ((sel->value2 == NULL) ||
+				    ((sibling->ns != NULL) &&
+				     (xmlStrEqual(sel->value2,
+						  sibling->ns->href))))
+				    indx++;
+			    }
+			    sibling = sibling->prev;
+			}
+			if (sibling == NULL) {
+			    /* hum going backward in document order ... */
+			    indx = 0;
+			    sibling = node;
+			    while (sibling != NULL) {
+				if (sibling == previous)
+				    break;
+				if ((previous->type == XML_ELEMENT_NODE) &&
+				    (previous->name != NULL) &&
+				    (sibling->name != NULL) &&
+				    (previous->name[0] == sibling->name[0]) &&
+				    (xmlStrEqual(previous->name, sibling->name)))
+				{
+				    if ((sel->value2 == NULL) ||
+					((sibling->ns != NULL) &&
+					(xmlStrEqual(sel->value2,
+					sibling->ns->href))))
+				    {
+					indx--;
+				    }
+				}
+				sibling = sibling->next;
+			    }
+			}
+			if (sibling != NULL) {
+			    pos = ix + indx;
+			    /*
+			     * If the node is in a Value Tree we need to
+			     * save len, but cannot cache the node!
+			     * (bugs 153137 and 158840)
+			     */
+			    if (node->doc != NULL) {
+				len = XSLT_RUNTIME_EXTRA(ctxt,
+				        sel->lenExtra, ival);
+				if (!isRVT) {
+				    XSLT_RUNTIME_EXTRA(ctxt,
+					sel->previousExtra, ptr) = node;
+				    XSLT_RUNTIME_EXTRA(ctxt,
+				        sel->indexExtra, ival) = pos;
+				}
+			    }
+			    ix = pos;
+			} else
+			    pos = 0;
+		    } else {
+			/*
+			 * recompute the index
+			 */
+			xmlNodePtr 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 ix, nocache = 0;
+
+		    previous = (xmlNodePtr)
+			XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
+		    ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
+		    if ((previous != NULL) &&
+			(previous->parent == node->parent)) {
+			/*
+			 * just walk back to adjust the index
+			 */
+			int indx = 0;
+			xmlNodePtr sibling = node;
+
+			while (sibling != NULL) {
+			    if (sibling == previous)
+				break;
+			    if (sibling->type == XML_ELEMENT_NODE)
+				indx++;
+			    sibling = sibling->prev;
+			}
+			if (sibling == NULL) {
+			    /* hum going backward in document order ... */
+			    indx = 0;
+			    sibling = node;
+			    while (sibling != NULL) {
+				if (sibling == previous)
+				    break;
+				if (sibling->type == XML_ELEMENT_NODE)
+				    indx--;
+				sibling = sibling->next;
+			    }
+			}
+			if (sibling != NULL) {
+			    pos = ix + indx;
+			    /*
+			     * If the node is in a Value Tree we cannot
+			     * cache it !
+			     */
+			    if ((node->doc != NULL) && !isRVT) {
+				len = XSLT_RUNTIME_EXTRA(ctxt,
+				        sel->lenExtra, ival);
+				XSLT_RUNTIME_EXTRA(ctxt,
+					sel->previousExtra, ptr) = node;
+				XSLT_RUNTIME_EXTRA(ctxt,
+					sel->indexExtra, ival) = pos;
+			    }
+			} else
+			    pos = 0;
+		    } else {
+			/*
+			 * recompute the index
+			 */
+			xmlNodePtr 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;
+
+		if (step->value == NULL)
+		    goto wrong_index;
+		if (step->comp == NULL)
+		    goto wrong_index;
+
+		if (!xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList,
+			                    comp->nsNr))
+		    goto wrong_index;
+
+		if (pos != 0) {
+		    ctxt->xpathCtxt->contextSize = oldCS;
+		    ctxt->xpathCtxt->proximityPosition = oldCP;
+		}
+		ctxt->node = oldNode;
+		break;
+wrong_index:
+		if (pos != 0) {
+		    ctxt->xpathCtxt->contextSize = oldCS;
+		    ctxt->xpathCtxt->proximityPosition = oldCP;
+		}
+		ctxt->node = oldNode;
+		goto rollback;
+	    }
+            case XSLT_OP_PI:
+		if (node->type != XML_PI_NODE)
+		    goto rollback;
+		if (step->value != NULL) {
+		    if (!xmlStrEqual(step->value, node->name))
+			goto rollback;
+		}
+		break;
+            case XSLT_OP_COMMENT:
+		if (node->type != XML_COMMENT_NODE)
+		    goto rollback;
+		break;
+            case XSLT_OP_TEXT:
+		if ((node->type != XML_TEXT_NODE) &&
+		    (node->type != XML_CDATA_SECTION_NODE))
+		    goto rollback;
+		break;
+            case XSLT_OP_NODE:
+		switch (node->type) {
+		    case XML_ELEMENT_NODE:
+		    case XML_CDATA_SECTION_NODE:
+		    case XML_PI_NODE:
+		    case XML_COMMENT_NODE:
+		    case XML_TEXT_NODE:
+			break;
+		    default:
+			goto rollback;
+		}
+		break;
+	}
+    }
+found:
+    if (states.states != NULL) {
+        /* Free the rollback states */
+	xmlFree(states.states);
+    }
+    return(1);
+rollback:
+    /* got an error try to rollback */
+    if (states.states == NULL)
+	return(0);
+    if (states.nbstates <= 0) {
+	xmlFree(states.states);
+	return(0);
+    }
+    states.nbstates--;
+    i = states.states[states.nbstates].step;
+    node = states.states[states.nbstates].node;
+#if 0
+    fprintf(stderr, "Pop: %d, %s\n", i, node->name);
+#endif
+    goto restart;
+}
+
+/**
+ * xsltTestCompMatchList:
+ * @ctxt:  a XSLT process context
+ * @node: a node
+ * @comp: the precompiled pattern list
+ *
+ * Test whether the node matches one of the patterns in the list
+ *
+ * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
+ */
+int
+xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node,
+	              xsltCompMatchPtr comp) {
+    int ret;
+
+    if ((ctxt == NULL) || (node == NULL))
+	return(-1);
+    while (comp != NULL) {
+	ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL);
+	if (ret == 1)
+	    return(1);
+	comp = comp->next;
+    }
+    return(0);
+}
+
+/************************************************************************
+ *									*
+ *			Dedicated parser for templates			*
+ *									*
+ ************************************************************************/
+
+#define CUR (*ctxt->cur)
+#define SKIP(val) ctxt->cur += (val)
+#define NXT(val) ctxt->cur[(val)]
+#define CUR_PTR ctxt->cur
+
+#define SKIP_BLANKS 							\
+    while (IS_BLANK_CH(CUR)) NEXT
+
+#define CURRENT (*ctxt->cur)
+#define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
+
+
+#define PUSH(op, val, val2, novar) 						\
+    if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto error;
+
+#define SWAP() 						\
+    xsltSwapTopCompMatch(ctxt->comp);
+
+#define XSLT_ERROR(X)							\
+    { xsltError(ctxt, __FILE__, __LINE__, X);			\
+      ctxt->error = (X); return; }
+
+#define XSLT_ERROR0(X)							\
+    { xsltError(ctxt, __FILE__, __LINE__, X);			\
+      ctxt->error = (X); return(0); }
+
+/**
+ * xsltScanLiteral:
+ * @ctxt:  the XPath Parser context
+ *
+ * Parse an XPath Litteral:
+ *
+ * [29] Literal ::= '"' [^"]* '"'
+ *                | "'" [^']* "'"
+ *
+ * Returns the Literal parsed or NULL
+ */
+
+static xmlChar *
+xsltScanLiteral(xsltParserContextPtr ctxt) {
+    const xmlChar *q, *cur;
+    xmlChar *ret = NULL;
+    int val, len;
+
+    SKIP_BLANKS;
+    if (CUR == '"') {
+        NEXT;
+	cur = q = CUR_PTR;
+	val = xmlStringCurrentChar(NULL, cur, &len);
+	while ((IS_CHAR(val)) && (val != '"')) {
+	    cur += len;
+	    val = xmlStringCurrentChar(NULL, cur, &len);
+	}
+	if (!IS_CHAR(val)) {
+	    ctxt->error = 1;
+	    return(NULL);
+	} else {
+	    ret = xmlStrndup(q, cur - q);
+        }
+	cur += len;
+	CUR_PTR = cur;
+    } else if (CUR == '\'') {
+        NEXT;
+	cur = q = CUR_PTR;
+	val = xmlStringCurrentChar(NULL, cur, &len);
+	while ((IS_CHAR(val)) && (val != '\'')) {
+	    cur += len;
+	    val = xmlStringCurrentChar(NULL, cur, &len);
+	}
+	if (!IS_CHAR(val)) {
+	    ctxt->error = 1;
+	    return(NULL);
+	} else {
+	    ret = xmlStrndup(q, cur - q);
+        }
+	cur += len;
+	CUR_PTR = cur;
+    } else {
+	/* XP_ERROR(XPATH_START_LITERAL_ERROR); */
+	ctxt->error = 1;
+	return(NULL);
+    }
+    return(ret);
+}
+
+/**
+ * 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)
+	    return;
+	SKIP_BLANKS;
+	if (CUR != ')') {
+	    xsltTransformError(NULL, NULL, NULL,
+		    "xsltCompileIdKeyPattern : ) expected\n");
+	    ctxt->error = 1;
+	    return;
+	}
+	NEXT;
+	PUSH(XSLT_OP_ID, lit, NULL, novar);
+    } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
+	if (axis != 0) {
+	    xsltTransformError(NULL, NULL, NULL,
+		    "xsltCompileIdKeyPattern : NodeTest expected\n");
+	    ctxt->error = 1;
+	    return;
+	}
+	NEXT;
+	SKIP_BLANKS;
+        lit = xsltScanLiteral(ctxt);
+	if (ctxt->error)
+	    return;
+	SKIP_BLANKS;
+	if (CUR != ',') {
+	    xsltTransformError(NULL, NULL, NULL,
+		    "xsltCompileIdKeyPattern : , expected\n");
+	    ctxt->error = 1;
+	    return;
+	}
+	NEXT;
+	SKIP_BLANKS;
+        lit2 = xsltScanLiteral(ctxt);
+	if (ctxt->error)
+	    return;
+	SKIP_BLANKS;
+	if (CUR != ')') {
+	    xsltTransformError(NULL, NULL, NULL,
+		    "xsltCompileIdKeyPattern : ) expected\n");
+	    ctxt->error = 1;
+	    return;
+	}
+	NEXT;
+	/* URGENT TODO: support namespace in keys */
+	PUSH(XSLT_OP_KEY, lit, lit2, novar);
+    } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
+	NEXT;
+	SKIP_BLANKS;
+	if (CUR != ')') {
+	    lit = xsltScanLiteral(ctxt);
+	    if (ctxt->error)
+		return;
+	    SKIP_BLANKS;
+	    if (CUR != ')') {
+		xsltTransformError(NULL, NULL, NULL,
+			"xsltCompileIdKeyPattern : ) expected\n");
+		ctxt->error = 1;
+		return;
+	    }
+	}
+	NEXT;
+	PUSH(XSLT_OP_PI, lit, NULL, novar);
+    } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
+	NEXT;
+	SKIP_BLANKS;
+	if (CUR != ')') {
+	    xsltTransformError(NULL, NULL, NULL,
+		    "xsltCompileIdKeyPattern : ) expected\n");
+	    ctxt->error = 1;
+	    return;
+	}
+	NEXT;
+	PUSH(XSLT_OP_TEXT, NULL, NULL, novar);
+    } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
+	NEXT;
+	SKIP_BLANKS;
+	if (CUR != ')') {
+	    xsltTransformError(NULL, NULL, NULL,
+		    "xsltCompileIdKeyPattern : ) expected\n");
+	    ctxt->error = 1;
+	    return;
+	}
+	NEXT;
+	PUSH(XSLT_OP_COMMENT, NULL, NULL, novar);
+    } else if (xmlStrEqual(name, (const xmlChar *)"node")) {
+	NEXT;
+	SKIP_BLANKS;
+	if (CUR != ')') {
+	    xsltTransformError(NULL, NULL, NULL,
+		    "xsltCompileIdKeyPattern : ) expected\n");
+	    ctxt->error = 1;
+	    return;
+	}
+	NEXT;
+	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:
+    if (name != NULL)
+	xmlFree(name);
+}
+
+/**
+ * xsltCompileStepPattern:
+ * @ctxt:  the compilation context
+ * @token:  a posible precompiled name
+ * @novar: flag to prohibit xslt variables from pattern
+ *
+ * Compile the XSLT StepPattern and generates a precompiled
+ * form suitable for fast matching.
+ *
+ * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate* 
+ * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
+ *                                     | ('child' | 'attribute') '::'
+ * from XPath
+ * [7]  NodeTest ::= NameTest
+ *                 | NodeType '(' ')'
+ *                 | 'processing-instruction' '(' Literal ')'
+ * [8] Predicate ::= '[' PredicateExpr ']'
+ * [9] PredicateExpr ::= Expr
+ * [13] AbbreviatedAxisSpecifier ::= '@'?
+ * [37] NameTest ::= '*' | NCName ':' '*' | QName
+ */
+
+static void
+xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
+    xmlChar *name = NULL;
+    const xmlChar *URI = NULL;
+    xmlChar *URL = NULL;
+    int level;
+    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);
+	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);
+		ctxt->error = 1;
+		goto error;
+	    } else {
+		URL = xmlStrdup(ns->href);
+	    }
+	    xmlFree(prefix);
+	    if (token == NULL) {
+		if (CUR == '*') {
+		    NEXT;
+                    if (axis == AXIS_ATTRIBUTE) {
+                        PUSH(XSLT_OP_ATTR, NULL, URL, novar);
+                    }
+                    else {
+                        PUSH(XSLT_OP_NS, URL, NULL, novar);
+                    }
+		} 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);
+                }
+                else {
+                    PUSH(XSLT_OP_ELEM, token, URL, novar);
+                }
+	    }
+	} 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);
+            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);
+        }
+        else {
+            PUSH(XSLT_OP_ELEM, token, URL, novar);
+        }
+    }
+parse_predicate:
+    SKIP_BLANKS;
+    level = 0;
+    while (CUR == '[') {
+	const xmlChar *q;
+	xmlChar *ret = NULL;
+
+	level++;
+	NEXT;
+	q = CUR_PTR;
+	while (CUR != 0) {
+	    /* Skip over nested predicates */
+	    if (CUR == '[')
+		level++;
+	    else if (CUR == ']') {
+		level--;
+		if (level == 0)
+		    break;
+	    } else if (CUR == '"') {
+		NEXT;
+		while ((CUR != 0) && (CUR != '"'))
+		    NEXT;
+	    } else if (CUR == '\'') {
+		NEXT;
+		while ((CUR != 0) && (CUR != '\''))
+		    NEXT;
+	    }
+	    NEXT;
+	}
+	if (CUR == 0) {
+	    xsltTransformError(NULL, NULL, NULL,
+		    "xsltCompileStepPattern : ']' expected\n");
+	    ctxt->error = 1;
+	    return;
+        }
+	ret = xmlStrndup(q, CUR_PTR - q);
+	PUSH(XSLT_OP_PREDICATE, ret, NULL, novar);
+	/* push the predicate lower than local test */
+	SWAP();
+	NEXT;
+	SKIP_BLANKS;
+    }
+    return;
+error:
+    if (token != NULL)
+	xmlFree(token);
+    if (name != NULL)
+	xmlFree(name);
+}
+
+/**
+ * xsltCompileRelativePathPattern:
+ * @comp:  the compilation context
+ * @token:  a posible precompiled name
+ * @novar:  flag to prohibit xslt variables
+ *
+ * Compile the XSLT RelativePathPattern and generates a precompiled
+ * form suitable for fast matching.
+ *
+ * [4] RelativePathPattern ::= StepPattern
+ *                           | RelativePathPattern '/' StepPattern
+ *                           | RelativePathPattern '//' StepPattern
+ */
+static void
+xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
+    xsltCompileStepPattern(ctxt, token, novar);
+    if (ctxt->error)
+	goto error;
+    SKIP_BLANKS;
+    while ((CUR != 0) && (CUR != '|')) {
+	if ((CUR == '/') && (NXT(1) == '/')) {
+	    PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
+	    NEXT;
+	    NEXT;
+	    SKIP_BLANKS;
+	    xsltCompileStepPattern(ctxt, NULL, novar);
+	} else if (CUR == '/') {
+	    PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
+	    NEXT;
+	    SKIP_BLANKS;
+	    if ((CUR != 0) && (CUR != '|')) {
+		xsltCompileRelativePathPattern(ctxt, NULL, novar);
+	    }
+	} else {
+	    ctxt->error = 1;
+	}
+	if (ctxt->error)
+	    goto error;
+	SKIP_BLANKS;
+    }
+error:
+    return;
+}
+
+/**
+ * xsltCompileLocationPathPattern:
+ * @ctxt:  the compilation context
+ * @novar:  flag to prohibit xslt variables
+ *
+ * Compile the XSLT LocationPathPattern and generates a precompiled
+ * form suitable for fast matching.
+ *
+ * [2] LocationPathPattern ::= '/' RelativePathPattern?
+ *                           | IdKeyPattern (('/' | '//') RelativePathPattern)?
+ *                           | '//'? RelativePathPattern
+ */
+static void
+xsltCompileLocationPathPattern(xsltParserContextPtr ctxt, int novar) {
+    SKIP_BLANKS;
+    if ((CUR == '/') && (NXT(1) == '/')) {
+	/*
+	 * since we reverse the query
+	 * a leading // can be safely ignored
+	 */
+	NEXT;
+	NEXT;
+	ctxt->comp->priority = 0.5;	/* '//' means not 0 priority */
+	xsltCompileRelativePathPattern(ctxt, NULL, novar);
+    } else if (CUR == '/') {
+	/*
+	 * We need to find root as the parent
+	 */
+	NEXT;
+	SKIP_BLANKS;
+	PUSH(XSLT_OP_ROOT, NULL, NULL, novar);
+	if ((CUR != 0) && (CUR != '|')) {
+	    PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
+	    xsltCompileRelativePathPattern(ctxt, NULL, novar);
+	}
+    } else if (CUR == '*') {
+	xsltCompileRelativePathPattern(ctxt, NULL, novar);
+    } else if (CUR == '@') {
+	xsltCompileRelativePathPattern(ctxt, NULL, novar);
+    } else {
+	xmlChar *name;
+	name = 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);
+	    if ((CUR == '/') && (NXT(1) == '/')) {
+		PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
+		NEXT;
+		NEXT;
+		SKIP_BLANKS;
+		xsltCompileRelativePathPattern(ctxt, NULL, novar);
+	    } else if (CUR == '/') {
+		PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
+		NEXT;
+		SKIP_BLANKS;
+		xsltCompileRelativePathPattern(ctxt, NULL, novar);
+	    }
+	    return;
+	}
+	xsltCompileRelativePathPattern(ctxt, name, novar);
+    }
+error:
+    return;
+}
+
+/**
+ * xsltCompilePatternInternal:
+ * @pattern: an XSLT pattern
+ * @doc:  the containing document
+ * @node:  the containing element
+ * @style:  the stylesheet
+ * @runtime:  the transformation context, if done at run-time
+ * @novar:  flag to prohibit xslt variables
+ *
+ * Compile the XSLT pattern and generates a list of precompiled form suitable
+ * for fast matching.
+ *
+ * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
+ *
+ * Returns the generated pattern list or NULL in case of failure
+ */
+
+static xsltCompMatchPtr
+xsltCompilePatternInternal(const xmlChar *pattern, xmlDocPtr doc,
+	           xmlNodePtr node, xsltStylesheetPtr style,
+		   xsltTransformContextPtr runtime, int novar) {
+    xsltParserContextPtr ctxt = NULL;
+    xsltCompMatchPtr element, first = NULL, previous = NULL;
+    int current, start, end, level, j;
+
+    if (pattern == NULL) {
+	xsltTransformError(NULL, NULL, node,
+			 "xsltCompilePattern : NULL pattern\n");
+	return(NULL);
+    }
+
+    ctxt = xsltNewParserContext(style, runtime);
+    if (ctxt == NULL)
+	return(NULL);
+    ctxt->doc = doc;
+    ctxt->elem = node;
+    current = end = 0;
+    while (pattern[current] != 0) {
+	start = current;
+	while (IS_BLANK_CH(pattern[current]))
+	    current++;
+	end = current;
+	level = 0;
+	while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) {
+	    if (pattern[end] == '[')
+		level++;
+	    else if (pattern[end] == ']')
+		level--;
+	    else if (pattern[end] == '\'') {
+		end++;
+		while ((pattern[end] != 0) && (pattern[end] != '\''))
+		    end++;
+	    } else if (pattern[end] == '"') {
+		end++;
+		while ((pattern[end] != 0) && (pattern[end] != '"'))
+		    end++;
+	    }
+	    end++;
+	}
+	if (current == end) {
+	    xsltTransformError(NULL, NULL, node,
+			     "xsltCompilePattern : NULL pattern\n");
+	    goto error;
+	}
+	element = xsltNewCompMatch();
+	if (element == NULL) {
+	    goto error;
+	}
+	if (first == NULL)
+	    first = element;
+	else if (previous != NULL)
+	    previous->next = element;
+	previous = element;
+
+	ctxt->comp = element;
+	ctxt->base = xmlStrndup(&pattern[start], end - start);
+	if (ctxt->base == NULL)
+	    goto error;
+	ctxt->cur = &(ctxt->base)[current - start];
+	element->pattern = ctxt->base;
+	element->nsList = xmlGetNsList(doc, node);
+	j = 0;
+	if (element->nsList != NULL) {
+	    while (element->nsList[j] != NULL)
+		j++;
+	}
+	element->nsNr = j;
+
+
+#ifdef WITH_XSLT_DEBUG_PATTERN
+	xsltGenericDebug(xsltGenericDebugContext,
+			 "xsltCompilePattern : parsing '%s'\n",
+			 element->pattern);
+#endif
+	/*
+	 Preset default priority to be zero.
+	 This may be changed by xsltCompileLocationPathPattern.
+	 */
+	element->priority = 0;
+	xsltCompileLocationPathPattern(ctxt, novar);
+	if (ctxt->error) {
+	    xsltTransformError(NULL, style, node,
+			     "xsltCompilePattern : failed to compile '%s'\n",
+			     element->pattern);
+	    if (style != NULL) style->errors++;
+	    goto error;
+	}
+
+	/*
+	 * Reverse for faster interpretation.
+	 */
+	xsltReverseCompMatch(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) || (cur->match == NULL))
+	return(-1);
+
+    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);
+}
+
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..b47d809
--- /dev/null
+++ b/libxslt/preproc.c
@@ -0,0 +1,2350 @@
+/*
+ * 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"
+
+#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:
+            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->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))
+	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))
+	return;
+#ifdef XSLT_REFACTORED
+    comp = (xsltStyleItemCopyPtr) xsltNewStylePreComp(style, XSLT_FUNC_COPY);
+#else
+    comp = xsltNewStylePreComp(style, XSLT_FUNC_COPY);
+#endif
+    
+    if (comp == NULL)
+	return;
+    inst->psvi = comp;
+    comp->inst = inst;
+
+
+    comp->use = xsltGetCNsProp(style, inst, (const xmlChar *)"use-attribute-sets",
+				    XSLT_NAMESPACE);
+    if (comp->use == NULL)
+	comp->has_use = 0;
+    else
+	comp->has_use = 1;
+}
+
+#ifdef XSLT_REFACTORED
+    /* Enable if ever needed for xsl:text. */
+#else
+/**
+ * xsltTextComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst:  the xslt text node
+ *
+ * TODO: This function is obsolete, since xsl:text won't
+ *  be compiled, but removed from the tree.
+ *
+ * Process the xslt text node on the source node
+ */
+static void
+xsltTextComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+    xsltStyleItemTextPtr comp;
+#else
+    xsltStylePreCompPtr comp;
+#endif
+    const xmlChar *prop;
+
+    if ((style == NULL) || (inst == NULL))
+	return;
+
+#ifdef XSLT_REFACTORED
+    comp = (xsltStyleItemTextPtr) xsltNewStylePreComp(style, XSLT_FUNC_TEXT);
+#else
+    comp = xsltNewStylePreComp(style, XSLT_FUNC_TEXT);
+#endif    
+    if (comp == NULL)
+	return;
+    inst->psvi = comp;
+    comp->inst = inst;
+    comp->noescape = 0;
+
+    prop = xsltGetCNsProp(style, inst,
+	    (const xmlChar *)"disable-output-escaping",
+			XSLT_NAMESPACE);
+    if (prop != NULL) {
+	if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
+	    comp->noescape = 1;
+	} else if (!xmlStrEqual(prop,
+	    (const xmlChar *)"no")){
+	    xsltTransformError(NULL, style, inst,
+		"xsl:text: disable-output-escaping allows only yes or no\n");
+	    if (style != NULL) style->warnings++;
+	}
+    }
+}
+#endif /* else of XSLT_REFACTORED */
+
+/**
+ * xsltElementComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst:  the xslt element node
+ *
+ * Process the xslt element node on the source node
+ */
+static void
+xsltElementComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+    xsltStyleItemElementPtr comp;
+#else
+    xsltStylePreCompPtr comp;
+#endif
+
+    /*
+    * <xsl:element
+    *   name = { qname }
+    *   namespace = { uri-reference }
+    *   use-attribute-sets = qnames>
+    *   <!-- Content: template -->
+    * </xsl:element>
+    */
+    if ((style == NULL) || (inst == NULL))
+	return;
+
+#ifdef XSLT_REFACTORED
+    comp = (xsltStyleItemElementPtr) xsltNewStylePreComp(style, XSLT_FUNC_ELEMENT);
+#else
+    comp = xsltNewStylePreComp(style, XSLT_FUNC_ELEMENT);
+#endif
+
+    if (comp == NULL)
+	return;
+    inst->psvi = comp;
+    comp->inst = inst;
+
+    /*
+    * Attribute "name".
+    */
+    /*
+    * TODO: Precompile the AVT. See bug #344894.
+    */
+    comp->name = xsltEvalStaticAttrValueTemplate(style, inst,
+	(const xmlChar *)"name", NULL, &comp->has_name);
+    if (! comp->has_name) {
+	xsltTransformError(NULL, style, inst,
+	    "xsl:element: The attribute 'name' is missing.\n");
+	style->errors++;
+	goto error;
+    }
+    /*
+    * Attribute "namespace".
+    */
+    /*
+    * TODO: Precompile the AVT. See bug #344894.
+    */
+    comp->ns = xsltEvalStaticAttrValueTemplate(style, inst,
+	(const xmlChar *)"namespace", NULL, &comp->has_ns);
+    
+    if (comp->name != NULL) {	
+	if (xmlValidateQName(comp->name, 0)) {
+	    xsltTransformError(NULL, style, inst,
+		"xsl:element: The value '%s' of the attribute 'name' is "
+		"not a valid QName.\n", comp->name);
+	    style->errors++;
+	} else {
+	    const xmlChar *prefix = NULL, *name;
+
+	    name = xsltSplitQName(style->dict, comp->name, &prefix);
+	    if (comp->has_ns == 0) {	    
+		xmlNsPtr ns;
+
+		/*
+		* SPEC XSLT 1.0:
+		*  "If the namespace attribute is not present, then the QName is
+		*  expanded into an expanded-name using the namespace declarations
+		*  in effect for the xsl:element element, including any default
+		*  namespace declaration.
+		*/		
+		ns = xmlSearchNs(inst->doc, inst, prefix);
+		if (ns != NULL) {
+		    comp->ns = xmlDictLookup(style->dict, ns->href, -1);
+		    comp->has_ns = 1;
+#ifdef XSLT_REFACTORED
+		    comp->nsPrefix = prefix;
+		    comp->name = name;
+#endif
+		} else if (prefix != NULL) {
+		    xsltTransformError(NULL, style, inst,
+			"xsl:element: The prefixed QName '%s' "
+			"has no namespace binding in scope in the "
+			"stylesheet; this is an error, since the namespace was "
+			"not specified by the instruction itself.\n", comp->name);
+		    style->errors++;
+		}
+	    }	    
+	    if ((prefix != NULL) &&
+		(!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3)))
+	    {
+		/*
+		* Mark is to be skipped.
+		*/
+		comp->has_name = 0;		
+	    }
+	}
+    }    
+    /*
+    * Attribute "use-attribute-sets",
+    */
+    comp->use = xsltEvalStaticAttrValueTemplate(style, inst,
+		       (const xmlChar *)"use-attribute-sets",
+		       NULL, &comp->has_use);
+
+error:    
+    return;
+}
+
+/**
+ * xsltAttributeComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst:  the xslt attribute node
+ *
+ * Process the xslt attribute node on the source node
+ */
+static void
+xsltAttributeComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+    xsltStyleItemAttributePtr comp;
+#else
+    xsltStylePreCompPtr comp;
+#endif
+
+    /*
+    * <xsl:attribute
+    *   name = { qname }
+    *   namespace = { uri-reference }>
+    *   <!-- Content: template -->
+    * </xsl:attribute>
+    */
+    if ((style == NULL) || (inst == NULL))
+	return;
+
+#ifdef XSLT_REFACTORED
+    comp = (xsltStyleItemAttributePtr) xsltNewStylePreComp(style,
+	XSLT_FUNC_ATTRIBUTE);
+#else
+    comp = xsltNewStylePreComp(style, XSLT_FUNC_ATTRIBUTE);
+#endif
+    
+    if (comp == NULL)
+	return;
+    inst->psvi = comp;
+    comp->inst = inst;
+
+    /*
+    * Attribute "name".
+    */
+    /*
+    * TODO: Precompile the AVT. See bug #344894.
+    */
+    comp->name = xsltEvalStaticAttrValueTemplate(style, inst,
+				 (const xmlChar *)"name",
+				 NULL, &comp->has_name);
+    if (! comp->has_name) {
+	xsltTransformError(NULL, style, inst,
+	    "XSLT-attribute: The attribute 'name' is missing.\n");
+	style->errors++;
+	return;
+    }    
+    /*
+    * Attribute "namespace".
+    */
+    /*
+    * TODO: Precompile the AVT. See bug #344894.
+    */
+    comp->ns = xsltEvalStaticAttrValueTemplate(style, inst,
+	(const xmlChar *)"namespace",
+	NULL, &comp->has_ns);
+
+    if (comp->name != NULL) {
+	if (xmlValidateQName(comp->name, 0)) {
+	    xsltTransformError(NULL, style, inst,
+		"xsl:attribute: The value '%s' of the attribute 'name' is "
+		"not a valid QName.\n", comp->name);
+	    style->errors++;
+	} else {
+	    const xmlChar *prefix = NULL, *name;
+
+	    name = xsltSplitQName(style->dict, comp->name, &prefix);
+	    if (prefix != NULL) {
+		if (comp->has_ns == 0) {
+		    xmlNsPtr ns;
+
+		    /*
+		    * SPEC XSLT 1.0:
+		    *  "If the namespace attribute is not present, then the
+		    *  QName is expanded into an expanded-name using the
+		    *  namespace declarations in effect for the xsl:element
+		    *  element, including any default namespace declaration.
+		    */	    	    
+		    ns = xmlSearchNs(inst->doc, inst, prefix);
+		    if (ns != NULL) {
+			comp->ns = xmlDictLookup(style->dict, ns->href, -1);
+			comp->has_ns = 1;
+#ifdef XSLT_REFACTORED
+			comp->nsPrefix = prefix;
+			comp->name = name;
+#endif
+		    } else {
+			xsltTransformError(NULL, style, inst,
+			    "xsl:attribute: The prefixed QName '%s' "
+			    "has no namespace binding in scope in the "
+			    "stylesheet; this is an error, since the "
+			    "namespace was not specified by the instruction "
+			    "itself.\n", comp->name);
+			style->errors++;
+		    }
+		}
+		if (!xmlStrncasecmp(prefix, (xmlChar *) "xmlns", 5)) {
+		    /*
+		    * SPEC XSLT 1.0:
+		    *  "It is an error if the string that results from
+		    *  instantiating the attribute value template is not a
+		    *  QName or is the string xmlns. An XSLT processor may
+		    *  signal the error; if it does not signal the error,
+		    *  it must recover by not adding the attribute to the
+		    *  result tree."
+		    *
+		    * Reject a prefix of "xmlns". Mark to be skipped.
+		    */
+		    comp->has_name = 0;
+		    
+#ifdef WITH_XSLT_DEBUG_PARSING
+		    xsltGenericDebug(xsltGenericDebugContext,
+			"xsltAttribute: xmlns prefix forbidden\n");
+#endif		    
+		    return;
+		}
+		
+	    }
+	}	
+    }
+}
+
+/**
+ * xsltCommentComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst:  the xslt comment node
+ *
+ * Process the xslt comment node on the source node
+ */
+static void
+xsltCommentComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+    xsltStyleItemCommentPtr comp;
+#else
+    xsltStylePreCompPtr comp;
+#endif
+
+    if ((style == NULL) || (inst == NULL))
+	return;
+
+#ifdef XSLT_REFACTORED
+    comp = (xsltStyleItemCommentPtr) xsltNewStylePreComp(style, XSLT_FUNC_COMMENT);
+#else
+    comp = xsltNewStylePreComp(style, XSLT_FUNC_COMMENT);
+#endif
+
+    if (comp == NULL)
+	return;
+    inst->psvi = comp;
+    comp->inst = inst;
+}
+
+/**
+ * xsltProcessingInstructionComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst:  the xslt processing-instruction node
+ *
+ * Process the xslt processing-instruction node on the source node
+ */
+static void
+xsltProcessingInstructionComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+    xsltStyleItemPIPtr comp;
+#else
+    xsltStylePreCompPtr comp;
+#endif
+
+    if ((style == NULL) || (inst == NULL))
+	return;
+
+#ifdef XSLT_REFACTORED
+    comp = (xsltStyleItemPIPtr) xsltNewStylePreComp(style, XSLT_FUNC_PI);
+#else
+    comp = xsltNewStylePreComp(style, XSLT_FUNC_PI);
+#endif
+
+    if (comp == NULL)
+	return;
+    inst->psvi = comp;
+    comp->inst = inst;
+
+    comp->name = xsltEvalStaticAttrValueTemplate(style, inst,
+				 (const xmlChar *)"name",
+				 XSLT_NAMESPACE, &comp->has_name);
+}
+
+/**
+ * xsltCopyOfComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst:  the xslt copy-of node
+ *
+ * Process the xslt copy-of node on the source node
+ */
+static void
+xsltCopyOfComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+    xsltStyleItemCopyOfPtr comp;
+#else
+    xsltStylePreCompPtr comp;
+#endif
+
+    if ((style == NULL) || (inst == NULL))
+	return;
+
+#ifdef XSLT_REFACTORED
+    comp = (xsltStyleItemCopyOfPtr) xsltNewStylePreComp(style, XSLT_FUNC_COPYOF);
+#else
+    comp = xsltNewStylePreComp(style, XSLT_FUNC_COPYOF);
+#endif
+
+    if (comp == NULL)
+	return;
+    inst->psvi = comp;
+    comp->inst = inst;
+
+    comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select",
+	                        XSLT_NAMESPACE);
+    if (comp->select == NULL) {
+	xsltTransformError(NULL, style, inst,
+	     "xsl:copy-of : select is missing\n");
+	if (style != NULL) style->errors++;
+	return;
+    }
+    comp->comp = xsltXPathCompile(style, comp->select);
+    if (comp->comp == NULL) {
+	xsltTransformError(NULL, style, inst,
+	     "xsl:copy-of : could not compile select expression '%s'\n",
+	                 comp->select);
+	if (style != NULL) style->errors++;
+    }
+}
+
+/**
+ * xsltValueOfComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst:  the xslt value-of node
+ *
+ * Process the xslt value-of node on the source node
+ */
+static void
+xsltValueOfComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+    xsltStyleItemValueOfPtr comp;
+#else
+    xsltStylePreCompPtr comp;
+#endif
+    const xmlChar *prop;
+
+    if ((style == NULL) || (inst == NULL))
+	return;
+
+#ifdef XSLT_REFACTORED
+    comp = (xsltStyleItemValueOfPtr) xsltNewStylePreComp(style, XSLT_FUNC_VALUEOF);
+#else
+    comp = xsltNewStylePreComp(style, XSLT_FUNC_VALUEOF);
+#endif
+
+    if (comp == NULL)
+	return;
+    inst->psvi = comp;
+    comp->inst = inst;
+
+    prop = xsltGetCNsProp(style, inst,
+	    (const xmlChar *)"disable-output-escaping",
+			XSLT_NAMESPACE);
+    if (prop != NULL) {
+	if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
+	    comp->noescape = 1;
+	} else if (!xmlStrEqual(prop,
+				(const xmlChar *)"no")){
+	    xsltTransformError(NULL, style, inst,
+"xsl:value-of : disable-output-escaping allows only yes or no\n");
+	    if (style != NULL) style->warnings++;
+	}
+    }
+    comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select",
+	                        XSLT_NAMESPACE);
+    if (comp->select == NULL) {
+	xsltTransformError(NULL, style, inst,
+	     "xsl:value-of : select is missing\n");
+	if (style != NULL) style->errors++;
+	return;
+    }
+    comp->comp = xsltXPathCompile(style, comp->select);
+    if (comp->comp == NULL) {
+	xsltTransformError(NULL, style, inst,
+	     "xsl:value-of : could not compile select expression '%s'\n",
+	                 comp->select);
+	if (style != NULL) style->errors++;
+    }
+}
+
+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 {
+		*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))
+	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))
+	return;
+
+#ifdef XSLT_REFACTORED
+    comp = (xsltStyleItemNumberPtr) xsltNewStylePreComp(style, XSLT_FUNC_NUMBER);
+#else
+    comp = xsltNewStylePreComp(style, XSLT_FUNC_NUMBER);
+#endif
+
+    if (comp == NULL)
+	return;
+    cur->psvi = comp;
+
+    if ((style == NULL) || (cur == NULL))
+	return;
+
+    comp->numdata.doc = cur->doc;
+    comp->numdata.node = cur;
+    comp->numdata.value = xsltGetCNsProp(style, cur, (const xmlChar *)"value",
+	                                XSLT_NAMESPACE);
+    
+    prop = xsltEvalStaticAttrValueTemplate(style, cur,
+			 (const xmlChar *)"format",
+			 XSLT_NAMESPACE, &comp->numdata.has_format);
+    if (comp->numdata.has_format == 0) {
+	comp->numdata.format = xmlDictLookup(style->dict, BAD_CAST "" , 0);
+    } else {
+	comp->numdata.format = prop;
+    }
+
+    comp->numdata.count = xsltGetCNsProp(style, cur, (const xmlChar *)"count",
+					XSLT_NAMESPACE);
+    comp->numdata.from = xsltGetCNsProp(style, cur, (const xmlChar *)"from",
+					XSLT_NAMESPACE);
+    
+    prop = xsltGetCNsProp(style, cur, (const xmlChar *)"level", XSLT_NAMESPACE);
+    if (prop != NULL) {
+	if (xmlStrEqual(prop, BAD_CAST("single")) ||
+	    xmlStrEqual(prop, BAD_CAST("multiple")) ||
+	    xmlStrEqual(prop, BAD_CAST("any"))) {
+	    comp->numdata.level = prop;
+	} else {
+	    xsltTransformError(NULL, style, cur,
+			 "xsl:number : invalid value %s for level\n", prop);
+	    if (style != NULL) style->warnings++;
+	}
+    }
+    
+    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))
+	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))
+	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))
+	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))
+	return;
+
+#ifdef XSLT_REFACTORED
+    comp = (xsltStyleItemChoosePtr)
+	xsltNewStylePreComp(style, XSLT_FUNC_CHOOSE);
+#else
+    comp = xsltNewStylePreComp(style, XSLT_FUNC_CHOOSE);
+#endif
+
+    if (comp == NULL)
+	return;
+    inst->psvi = comp;
+    comp->inst = inst;
+}
+
+/**
+ * xsltIfComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst:  the xslt if node
+ *
+ * Process the xslt if node on the source node
+ */
+static void
+xsltIfComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+    xsltStyleItemIfPtr comp;
+#else
+    xsltStylePreCompPtr comp;
+#endif
+
+    if ((style == NULL) || (inst == NULL))
+	return;
+
+#ifdef XSLT_REFACTORED
+    comp = (xsltStyleItemIfPtr)
+	xsltNewStylePreComp(style, XSLT_FUNC_IF);
+#else
+    comp = xsltNewStylePreComp(style, XSLT_FUNC_IF);
+#endif
+
+    if (comp == NULL)
+	return;
+    inst->psvi = comp;
+    comp->inst = inst;
+
+    comp->test = xsltGetCNsProp(style, inst, (const xmlChar *)"test", XSLT_NAMESPACE);
+    if (comp->test == NULL) {
+	xsltTransformError(NULL, style, inst,
+	     "xsl:if : test is not defined\n");
+	if (style != NULL) style->errors++;
+	return;
+    }
+    comp->comp = xsltXPathCompile(style, comp->test);
+    if (comp->comp == NULL) {
+	xsltTransformError(NULL, style, inst,
+	     "xsl:if : could not compile test expression '%s'\n",
+	                 comp->test);
+	if (style != NULL) style->errors++;
+    }
+}
+
+/**
+ * xsltWhenComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst:  the xslt if node
+ *
+ * Process the xslt if node on the source node
+ */
+static void
+xsltWhenComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+    xsltStyleItemWhenPtr comp;
+#else
+    xsltStylePreCompPtr comp;
+#endif
+
+    if ((style == NULL) || (inst == NULL))
+	return;
+
+#ifdef XSLT_REFACTORED
+    comp = (xsltStyleItemWhenPtr)
+	xsltNewStylePreComp(style, XSLT_FUNC_WHEN);
+#else
+    comp = xsltNewStylePreComp(style, XSLT_FUNC_WHEN);
+#endif
+
+    if (comp == NULL)
+	return;
+    inst->psvi = comp;
+    comp->inst = inst;
+
+    comp->test = xsltGetCNsProp(style, inst, (const xmlChar *)"test", XSLT_NAMESPACE);
+    if (comp->test == NULL) {
+	xsltTransformError(NULL, style, inst,
+	     "xsl:when : test is not defined\n");
+	if (style != NULL) style->errors++;
+	return;
+    }
+    comp->comp = xsltXPathCompile(style, comp->test);
+    if (comp->comp == NULL) {
+	xsltTransformError(NULL, style, inst,
+	     "xsl:when : could not compile test expression '%s'\n",
+	                 comp->test);
+	if (style != NULL) style->errors++;
+    }
+}
+
+/**
+ * xsltForEachComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst:  the xslt for-each node
+ *
+ * Process the xslt for-each node on the source node
+ */
+static void
+xsltForEachComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+    xsltStyleItemForEachPtr comp;
+#else
+    xsltStylePreCompPtr comp;
+#endif
+
+    if ((style == NULL) || (inst == NULL))
+	return;
+
+#ifdef XSLT_REFACTORED
+    comp = (xsltStyleItemForEachPtr)
+	xsltNewStylePreComp(style, XSLT_FUNC_FOREACH);
+#else
+    comp = xsltNewStylePreComp(style, XSLT_FUNC_FOREACH);
+#endif
+
+    if (comp == NULL)
+	return;
+    inst->psvi = comp;
+    comp->inst = inst;
+
+    comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select",
+	                        XSLT_NAMESPACE);
+    if (comp->select == NULL) {
+	xsltTransformError(NULL, style, inst,
+		"xsl:for-each : select is missing\n");
+	if (style != NULL) style->errors++;
+    } else {
+	comp->comp = xsltXPathCompile(style, comp->select);
+	if (comp->comp == NULL) {
+	    xsltTransformError(NULL, style, inst,
+     "xsl:for-each : could not compile select expression '%s'\n",
+			     comp->select);
+	    if (style != NULL) style->errors++;
+	}
+    }
+    /* TODO: handle and skip the xsl:sort */
+}
+
+/**
+ * xsltVariableComp:
+ * @style: an XSLT compiled stylesheet
+ * @inst:  the xslt variable node
+ *
+ * Process the xslt variable node on the source node
+ */
+static void
+xsltVariableComp(xsltStylesheetPtr style, xmlNodePtr inst) {
+#ifdef XSLT_REFACTORED
+    xsltStyleItemVariablePtr comp;
+#else
+    xsltStylePreCompPtr comp;
+#endif
+
+    if ((style == NULL) || (inst == NULL))
+	return;
+
+#ifdef XSLT_REFACTORED
+    comp = (xsltStyleItemVariablePtr)
+	xsltNewStylePreComp(style, XSLT_FUNC_VARIABLE);
+#else
+    comp = xsltNewStylePreComp(style, XSLT_FUNC_VARIABLE);
+#endif
+
+    if (comp == NULL)
+	return;
+
+    inst->psvi = comp;
+    comp->inst = inst;
+    /*
+     * The full template resolution can be done statically
+     */
+
+    /*
+    * Attribute "name".
+    */
+    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-variable: Failed to compile the XPath expression '%s'.\n",
+		comp->select);
+	    style->errors++;
+	}
+	if (inst->children != NULL) {
+	    xsltTransformError(NULL, style, inst,
+		"XSLT-variable: The must be no child nodes, since the "
+		"attribute 'select' was specified.\n");
+	    style->errors++;
+	}
+    }
+}
+
+/**
+ * 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))
+	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->psvi = NULL;
+	return;
+    }
+    if (node == NULL)
+	return;
+    if (! IS_XSLT_ELEM_FAST(node))
+	return;
+
+    node->psvi = NULL;
+    if (XSLT_CCTXT(style)->inode->type != 0) {
+	switch (XSLT_CCTXT(style)->inode->type) {
+	    case XSLT_FUNC_APPLYTEMPLATES:
+		xsltApplyTemplatesComp(style, node);
+		break;
+	    case XSLT_FUNC_WITHPARAM:			   
+		xsltWithParamComp(style, node);
+		break;
+	    case XSLT_FUNC_VALUEOF:	    
+		xsltValueOfComp(style, node);
+		break;
+	    case XSLT_FUNC_COPY:	    
+		xsltCopyComp(style, node);
+		break;
+	    case XSLT_FUNC_COPYOF:
+		xsltCopyOfComp(style, node);
+		break;
+	    case XSLT_FUNC_IF:	    
+		xsltIfComp(style, node);
+		break;
+	    case XSLT_FUNC_CHOOSE:	    
+		xsltChooseComp(style, node);
+		break;
+	    case XSLT_FUNC_WHEN:	    
+		xsltWhenComp(style, node);
+		break;
+	    case XSLT_FUNC_OTHERWISE:	    
+		/* NOP yet */
+		return;
+	    case XSLT_FUNC_FOREACH:	    
+		xsltForEachComp(style, node);
+		break;
+	    case XSLT_FUNC_APPLYIMPORTS:	    
+		xsltApplyImportsComp(style, node);
+		break;
+	    case XSLT_FUNC_ATTRIBUTE:	    
+		xsltAttributeComp(style, node);
+		break;
+	    case XSLT_FUNC_ELEMENT:	    
+		xsltElementComp(style, node);
+		break;
+	    case XSLT_FUNC_SORT:	    
+		xsltSortComp(style, node);
+		break;
+	    case XSLT_FUNC_COMMENT:	    
+		xsltCommentComp(style, node);
+		break;
+	    case XSLT_FUNC_NUMBER:	    
+		xsltNumberComp(style, node);
+		break;
+	    case XSLT_FUNC_PI:	    
+		xsltProcessingInstructionComp(style, node);
+		break;
+	    case XSLT_FUNC_CALLTEMPLATE:	    
+		xsltCallTemplateComp(style, node);
+		break;
+	    case XSLT_FUNC_PARAM:	    
+		xsltParamComp(style, node);
+		break;
+	    case XSLT_FUNC_VARIABLE:	    
+		xsltVariableComp(style, node);
+		break;
+	    case XSLT_FUNC_FALLBACK:	    
+		/* NOP yet */
+		return;
+	    case XSLT_FUNC_DOCUMENT:	    
+		/* The extra one */
+		node->psvi = (void *) xsltDocumentComp(style, node,
+		    (xsltTransformFunction) xsltDocumentElem);
+		break;
+	    case XSLT_FUNC_MESSAGE:
+		/* NOP yet */
+		return;
+	    default:
+		/*
+		* NOTE that xsl:text, xsl:template, xsl:stylesheet,
+		*  xsl:transform, xsl:import, xsl:include are not expected
+		*  to be handed over to this function.
+		*/
+		xsltTransformError(NULL, style, node,
+		    "Internal error: (xsltStylePreCompute) cannot handle "
+		    "the XSLT element '%s'.\n", node->name);
+		style->errors++;
+		return;
+	}
+    } else {
+	/*
+	* Fallback to string comparison.
+	*/	
+	if (IS_XSLT_NAME(node, "apply-templates")) {
+	    xsltApplyTemplatesComp(style, node);
+	} else if (IS_XSLT_NAME(node, "with-param")) {
+	    xsltWithParamComp(style, node);
+	} else if (IS_XSLT_NAME(node, "value-of")) {
+	    xsltValueOfComp(style, node);
+	} else if (IS_XSLT_NAME(node, "copy")) {
+	    xsltCopyComp(style, node);
+	} else if (IS_XSLT_NAME(node, "copy-of")) {
+	    xsltCopyOfComp(style, node);
+	} else if (IS_XSLT_NAME(node, "if")) {
+	    xsltIfComp(style, node);
+	} else if (IS_XSLT_NAME(node, "choose")) {
+	    xsltChooseComp(style, node);
+	} else if (IS_XSLT_NAME(node, "when")) {
+	    xsltWhenComp(style, node);	
+	} else if (IS_XSLT_NAME(node, "otherwise")) {
+	    /* NOP yet */
+	    return;
+	} else if (IS_XSLT_NAME(node, "for-each")) {
+	    xsltForEachComp(style, node);
+	} else if (IS_XSLT_NAME(node, "apply-imports")) {
+	    xsltApplyImportsComp(style, node);
+	} else if (IS_XSLT_NAME(node, "attribute")) {
+	    xsltAttributeComp(style, node);
+	} else if (IS_XSLT_NAME(node, "element")) {
+	    xsltElementComp(style, node);
+	} else if (IS_XSLT_NAME(node, "sort")) {
+	    xsltSortComp(style, node);
+	} else if (IS_XSLT_NAME(node, "comment")) {
+	    xsltCommentComp(style, node);
+	} else if (IS_XSLT_NAME(node, "number")) {
+	    xsltNumberComp(style, node);
+	} else if (IS_XSLT_NAME(node, "processing-instruction")) {
+	    xsltProcessingInstructionComp(style, node);
+	} else if (IS_XSLT_NAME(node, "call-template")) {
+	    xsltCallTemplateComp(style, node);
+	} else if (IS_XSLT_NAME(node, "param")) {
+	    xsltParamComp(style, node);
+	} else if (IS_XSLT_NAME(node, "variable")) {
+	    xsltVariableComp(style, node);
+	} else if (IS_XSLT_NAME(node, "fallback")) {
+	    /* NOP yet */
+	    return;
+	} else if (IS_XSLT_NAME(node, "document")) {
+	    /* The extra one */
+	    node->psvi = (void *) xsltDocumentComp(style, node,
+		(xsltTransformFunction) xsltDocumentElem);	
+	} else if (IS_XSLT_NAME(node, "output")) {
+	    /* Top-level */
+	    return;
+	} else if (IS_XSLT_NAME(node, "preserve-space")) {
+	    /* Top-level */
+	    return;
+	} else if (IS_XSLT_NAME(node, "strip-space")) {
+	    /* Top-level */
+	    return;	
+	} else if (IS_XSLT_NAME(node, "key")) {
+	    /* Top-level */
+	    return;
+	} else if (IS_XSLT_NAME(node, "message")) {
+	    return;
+	} else if (IS_XSLT_NAME(node, "attribute-set")) {
+	    /* Top-level */
+	    return;
+	} else if (IS_XSLT_NAME(node, "namespace-alias")) {
+	    /* Top-level */
+	    return;
+	} else if (IS_XSLT_NAME(node, "decimal-format")) {
+	    /* Top-level */
+	    return;
+	} else if (IS_XSLT_NAME(node, "include")) {
+	    /* Top-level */	    	    
+	} else {
+	    /*
+	    * NOTE that xsl:text, xsl:template, xsl:stylesheet,
+	    *  xsl:transform, xsl:import, xsl:include are not expected
+	    *  to be handed over to this function.
+	    */
+	    xsltTransformError(NULL, style, node,
+		"Internal error: (xsltStylePreCompute) cannot handle "
+		"the XSLT element '%s'.\n", node->name);
+		style->errors++;
+	    return;
+	}	
+    }
+    /*
+    * Assign the current list of in-scope namespaces to the
+    * item. This is needed for XPath expressions.
+    */
+    if (node->psvi != NULL) {
+	((xsltStylePreCompPtr) node->psvi)->inScopeNs =
+	    XSLT_CCTXT(style)->inode->inScopeNs;
+    }
+}
+
+#else
+
+/**
+ * xsltStylePreCompute:
+ * @style:  the XSLT stylesheet
+ * @inst:  the instruction in the stylesheet
+ *
+ * Precompute an XSLT stylesheet element
+ */
+void
+xsltStylePreCompute(xsltStylesheetPtr style, xmlNodePtr inst) {
+    /*
+    * URGENT TODO: Normally inst->psvi Should never be reserved here,
+    *   BUT: since if we include the same stylesheet from
+    *   multiple imports, then the stylesheet will be parsed
+    *   again. We simply must not try to compute the stylesheet again.
+    * TODO: Get to the point where we don't need to query the
+    *   namespace- and local-name of the node, but can evaluate this
+    *   using cctxt->style->inode->category;
+    */
+    if (inst->psvi != NULL)
+	return;
+
+    if (IS_XSLT_ELEM(inst)) {
+	xsltStylePreCompPtr cur;
+
+	if (IS_XSLT_NAME(inst, "apply-templates")) {
+	    xsltCheckInstructionElement(style, inst);
+	    xsltApplyTemplatesComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "with-param")) {
+	    xsltCheckParentElement(style, inst, BAD_CAST "apply-templates",
+	                           BAD_CAST "call-template");
+	    xsltWithParamComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "value-of")) {
+	    xsltCheckInstructionElement(style, inst);
+	    xsltValueOfComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "copy")) {
+	    xsltCheckInstructionElement(style, inst);
+	    xsltCopyComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "copy-of")) {
+	    xsltCheckInstructionElement(style, inst);
+	    xsltCopyOfComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "if")) {
+	    xsltCheckInstructionElement(style, inst);
+	    xsltIfComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "when")) {
+	    xsltCheckParentElement(style, inst, BAD_CAST "choose", NULL);
+	    xsltWhenComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "choose")) {
+	    xsltCheckInstructionElement(style, inst);
+	    xsltChooseComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "for-each")) {
+	    xsltCheckInstructionElement(style, inst);
+	    xsltForEachComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "apply-imports")) {
+	    xsltCheckInstructionElement(style, inst);
+	    xsltApplyImportsComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "attribute")) {
+	    xmlNodePtr parent = inst->parent;
+
+	    if ((parent == NULL) || (parent->ns == NULL) ||
+		((parent->ns != inst->ns) &&
+		 (!xmlStrEqual(parent->ns->href, inst->ns->href))) ||
+		(!xmlStrEqual(parent->name, BAD_CAST "attribute-set"))) {
+		xsltCheckInstructionElement(style, inst);
+	    }
+	    xsltAttributeComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "element")) {
+	    xsltCheckInstructionElement(style, inst);
+	    xsltElementComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "text")) {
+	    xsltCheckInstructionElement(style, inst);
+	    xsltTextComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "sort")) {
+	    xsltCheckParentElement(style, inst, BAD_CAST "apply-templates",
+	                           BAD_CAST "for-each");
+	    xsltSortComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "comment")) {
+	    xsltCheckInstructionElement(style, inst);
+	    xsltCommentComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "number")) {
+	    xsltCheckInstructionElement(style, inst);
+	    xsltNumberComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "processing-instruction")) {
+	    xsltCheckInstructionElement(style, inst);
+	    xsltProcessingInstructionComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "call-template")) {
+	    xsltCheckInstructionElement(style, inst);
+	    xsltCallTemplateComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "param")) {	   
+	    if (xsltCheckTopLevelElement(style, inst, 0) == 0)
+	        xsltCheckInstructionElement(style, inst);
+	    xsltParamComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "variable")) {
+	    if (xsltCheckTopLevelElement(style, inst, 0) == 0)
+	        xsltCheckInstructionElement(style, inst);
+	    xsltVariableComp(style, inst);
+	} else if (IS_XSLT_NAME(inst, "otherwise")) {
+	    xsltCheckParentElement(style, inst, BAD_CAST "choose", NULL);
+	    xsltCheckInstructionElement(style, inst);
+	    return;
+	} else if (IS_XSLT_NAME(inst, "template")) {
+	    xsltCheckTopLevelElement(style, inst, 1);
+	    return;
+	} else if (IS_XSLT_NAME(inst, "output")) {
+	    xsltCheckTopLevelElement(style, inst, 1);
+	    return;
+	} else if (IS_XSLT_NAME(inst, "preserve-space")) {
+	    xsltCheckTopLevelElement(style, inst, 1);
+	    return;
+	} else if (IS_XSLT_NAME(inst, "strip-space")) {
+	    xsltCheckTopLevelElement(style, inst, 1);
+	    return;
+	} else if ((IS_XSLT_NAME(inst, "stylesheet")) ||
+	           (IS_XSLT_NAME(inst, "transform"))) {
+	    xmlNodePtr parent = inst->parent;
+
+	    if ((parent == NULL) || (parent->type != XML_DOCUMENT_NODE)) {
+		xsltTransformError(NULL, style, inst,
+		    "element %s only allowed only as root element\n",
+				   inst->name);
+		style->errors++;
+	    }
+	    return;
+	} else if (IS_XSLT_NAME(inst, "key")) {
+	    xsltCheckTopLevelElement(style, inst, 1);
+	    return;
+	} else if (IS_XSLT_NAME(inst, "message")) {
+	    xsltCheckInstructionElement(style, inst);
+	    return;
+	} else if (IS_XSLT_NAME(inst, "attribute-set")) {
+	    xsltCheckTopLevelElement(style, inst, 1);
+	    return;
+	} else if (IS_XSLT_NAME(inst, "namespace-alias")) {
+	    xsltCheckTopLevelElement(style, inst, 1);
+	    return;
+	} else if (IS_XSLT_NAME(inst, "include")) {
+	    xsltCheckTopLevelElement(style, inst, 1);
+	    return;
+	} else if (IS_XSLT_NAME(inst, "import")) {
+	    xsltCheckTopLevelElement(style, inst, 1);
+	    return;
+	} else if (IS_XSLT_NAME(inst, "decimal-format")) {
+	    xsltCheckTopLevelElement(style, inst, 1);
+	    return;
+	} else if (IS_XSLT_NAME(inst, "fallback")) {
+	    xsltCheckInstructionElement(style, inst);
+	    return;
+	} else if (IS_XSLT_NAME(inst, "document")) {
+	    xsltCheckInstructionElement(style, inst);
+	    inst->psvi = (void *) xsltDocumentComp(style, inst,
+				(xsltTransformFunction) xsltDocumentElem);
+	} else {
+	    xsltTransformError(NULL, style, inst,
+		 "xsltStylePreCompute: unknown xsl:%s\n", inst->name);
+	    if (style != NULL) style->warnings++;
+	}
+	
+	cur = (xsltStylePreCompPtr) inst->psvi;
+	/*
+	* A ns-list is build for every XSLT item in the
+	* node-tree. This is needed for XPath expressions.
+	*/
+	if (cur != NULL) {
+	    int i = 0;
+
+	    cur->nsList = xmlGetNsList(inst->doc, inst);
+            if (cur->nsList != NULL) {
+		while (cur->nsList[i] != NULL)
+		    i++;
+	    }
+	    cur->nsNr = i;
+	}
+    } else {
+	inst->psvi =
+	    (void *) xsltPreComputeExtModuleElement(style, inst);
+
+	/*
+	 * Unknown element, maybe registered at the context
+	 * level. Mark it for later recognition.
+	 */
+	if (inst->psvi == NULL)
+	    inst->psvi = (void *) xsltExtMarker;
+    }
+}
+#endif /* XSLT_REFACTORED */
diff --git a/libxslt/preproc.h b/libxslt/preproc.h
new file mode 100644
index 0000000..f67b389
--- /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..b766cf7
--- /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..d52c0ae
--- /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..c6250dc
--- /dev/null
+++ b/libxslt/templates.c
@@ -0,0 +1,825 @@
+/*
+ * 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/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))
+	return(NULL);
+
+    if (inst->children == NULL)
+	return(NULL);
+
+    /*
+    * This creates a temporary element-node to add the resulting
+    * text content to.
+    * OPTIMIZE TODO: Keep such an element-node in the transformation
+    *  context to avoid creating it every time.
+    */
+    insert = xmlNewDocNode(ctxt->output, NULL,
+	                   (const xmlChar *)"fake", NULL);
+    if (insert == NULL) {
+	xsltTransformError(ctxt, NULL, contextNode,
+		"Failed to create temporary node\n");
+	return(NULL);
+    }
+    oldInsert = ctxt->insert;
+    ctxt->insert = insert;
+    /*
+    * OPTIMIZE TODO: if inst->children consists only of text-nodes.
+    */
+    xsltApplyOneTemplate(ctxt, contextNode, inst->children, NULL, NULL);
+
+    ctxt->insert = oldInsert;
+
+    ret = xmlNodeGetContent(insert);
+    if (insert != NULL)
+	xmlFreeNode(insert);
+    return(ret);
+}
+
+/**
+ * xsltAttrTemplateValueProcessNode:
+ * @ctxt:  the XSLT transformation context
+ * @str:  the attribute template node value
+ * @inst:  the instruction (or LRE) in the stylesheet holding the
+ *         attribute with an AVT
+ *
+ * Process the given string, allowing to pass a namespace mapping
+ * context and return the new string value.
+ *
+ * Called by:
+ *  - xsltAttrTemplateValueProcess() (templates.c)
+ *  - xsltEvalAttrValueTemplate() (templates.c)
+ *
+ * QUESTION: Why is this function public? It is not used outside
+ *  of templates.c.
+ *
+ * Returns the computed string value or NULL, must be deallocated by the
+ *    caller.
+ */
+xmlChar *
+xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt,
+	  const xmlChar *str, xmlNodePtr inst)
+{
+    xmlChar *ret = NULL;
+    const xmlChar *cur;
+    xmlChar *expr, *val;
+    xmlNsPtr *nsList = NULL;
+    int nsNr = 0;
+
+    if (str == NULL) return(NULL);
+    if (*str == 0)
+	return(xmlStrndup((xmlChar *)"", 0));
+
+    cur = str;
+    while (*cur != 0) {
+	if (*cur == '{') {
+	    if (*(cur+1) == '{') {	/* escaped '{' */
+	        cur++;
+		ret = xmlStrncat(ret, str, cur - str);
+		cur++;
+		str = cur;
+		continue;
+	    }
+	    ret = xmlStrncat(ret, str, cur - str);
+	    str = cur;
+	    cur++;
+	    while ((*cur != 0) && (*cur != '}')) cur++;
+	    if (*cur == 0) {
+	        xsltTransformError(ctxt, NULL, inst,
+			"xsltAttrTemplateValueProcessNode: unmatched '{'\n");
+		ret = xmlStrncat(ret, str, cur - str);
+		return(ret);
+	    }
+	    str++;
+	    expr = xmlStrndup(str, cur - str);
+	    if (expr == NULL)
+		return(ret);
+	    else if (*expr == '{') {
+		ret = xmlStrcat(ret, expr);
+		xmlFree(expr);
+	    } else {
+		xmlXPathCompExprPtr comp;
+		/*
+		 * TODO: keep precompiled form around
+		 */
+		if ((nsList == NULL) && (inst != NULL)) {
+		    int i = 0;
+
+		    nsList = xmlGetNsList(inst->doc, inst);
+		    if (nsList != NULL) {
+			while (nsList[i] != NULL)
+			    i++;
+			nsNr = i;
+		    }
+		}
+		comp = xmlXPathCompile(expr);
+                val = xsltEvalXPathStringNs(ctxt, comp, nsNr, nsList);
+		xmlXPathFreeCompExpr(comp);
+		xmlFree(expr);
+		if (val != NULL) {
+		    ret = xmlStrcat(ret, val);
+		    xmlFree(val);
+		}
+	    }
+	    cur++;
+	    str = cur;
+	} else if (*cur == '}') {
+	    cur++;
+	    if (*cur == '}') {	/* escaped '}' */
+		ret = xmlStrncat(ret, str, cur - str);
+		cur++;
+		str = cur;
+		continue;
+	    } else {
+	        xsltTransformError(ctxt, NULL, inst,
+		     "xsltAttrTemplateValueProcessNode: unmatched '}'\n");
+	    }
+	} else
+	    cur++;
+    }
+    if (cur != str) {
+	ret = xmlStrncat(ret, str, cur - str);
+    }
+
+    if (nsList != NULL)
+	xmlFree(nsList);
+
+    return(ret);
+}
+
+/**
+ * xsltAttrTemplateValueProcess:
+ * @ctxt:  the XSLT transformation context
+ * @str:  the attribute template node value
+ *
+ * Process the given node and return the new string value.
+ *
+ * Returns the computed string value or NULL, must be deallocated by the
+ *    caller.
+ */
+xmlChar *
+xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt, const xmlChar *str) {
+    return(xsltAttrTemplateValueProcessNode(ctxt, str, NULL));
+}
+
+/**
+ * xsltEvalAttrValueTemplate:
+ * @ctxt:  the XSLT transformation context
+ * @inst:  the instruction (or LRE) in the stylesheet holding the
+ *         attribute with an AVT
+ * @name:  the attribute QName
+ * @ns:  the attribute namespace URI
+ *
+ * Evaluate a attribute value template, i.e. the attribute value can
+ * contain expressions contained in curly braces ({}) and those are
+ * substituted by they computed value.
+ *
+ * Returns the computed string value or NULL, must be deallocated by the
+ *    caller.
+ */
+xmlChar *
+xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst,
+	                  const xmlChar *name, const xmlChar *ns)
+{
+    xmlChar *ret;
+    xmlChar *expr;
+
+    if ((ctxt == NULL) || (inst == NULL) || (name == NULL))
+	return(NULL);
+
+    expr = xsltGetNsProp(inst, name, ns);
+    if (expr == NULL)
+	return(NULL);
+
+    /*
+     * TODO: though now {} is detected ahead, it would still be good to
+     *       optimize both functions to keep the splitted value if the
+     *       attribute content and the XPath precompiled expressions around
+     */
+
+    ret = xsltAttrTemplateValueProcessNode(ctxt, expr, inst);
+#ifdef WITH_XSLT_DEBUG_TEMPLATES
+    XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
+	 "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret));
+#endif
+    if (expr != NULL)
+	xmlFree(expr);
+    return(ret);
+}
+
+/**
+ * xsltEvalStaticAttrValueTemplate:
+ * @style:  the XSLT stylesheet
+ * @inst:  the instruction (or LRE) in the stylesheet holding the
+ *         attribute with an AVT
+ * @name:  the attribute Name
+ * @ns:  the attribute namespace URI
+ * @found:  indicator whether the attribute is present
+ *
+ * Check if an attribute value template has a static value, i.e. the
+ * attribute value does not contain expressions contained in curly braces ({})
+ *
+ * Returns the static string value or NULL, must be deallocated by the
+ *    caller.
+ */
+const xmlChar *
+xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr inst,
+			const xmlChar *name, const xmlChar *ns, int *found) {
+    const xmlChar *ret;
+    xmlChar *expr;
+
+    if ((style == NULL) || (inst == NULL) || (name == NULL))
+	return(NULL);
+
+    expr = xsltGetNsProp(inst, name, ns);
+    if (expr == NULL) {
+	*found = 0;
+	return(NULL);
+    }
+    *found = 1;
+
+    ret = xmlStrchr(expr, '{');
+    if (ret != NULL) {
+	xmlFree(expr);
+	return(NULL);
+    }
+    ret = xmlDictLookup(style->dict, expr, -1);
+    xmlFree(expr);
+    return(ret);
+}
+
+/**
+ * xsltAttrTemplateProcess:
+ * @ctxt:  the XSLT transformation context
+ * @target:  the element where the attribute will be grafted
+ * @attr:  the attribute node of a literal result element
+ *
+ * Process one attribute of a Literal Result Element (in the stylesheet).
+ * Evaluates Attribute Value Templates and copies the attribute over to
+ * the result element.
+ * This does *not* process attribute sets (xsl:use-attribute-set).
+ * 
+ *
+ * Returns the generated attribute node.
+ */
+xmlAttrPtr
+xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target,
+	                xmlAttrPtr attr)
+{
+    const xmlChar *value;
+    xmlAttrPtr ret;
+
+    if ((ctxt == NULL) || (attr == NULL) || (target == NULL))
+	return(NULL);
+    
+    if (attr->type != XML_ATTRIBUTE_NODE)
+	return(NULL);
+
+    /*
+    * Skip all XSLT attributes.
+    */
+#ifdef XSLT_REFACTORED    
+    if (attr->psvi == xsltXSLTAttrMarker)
+	return(NULL);
+#else
+    if ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, XSLT_NAMESPACE))
+	return(NULL);
+#endif
+    /*
+    * Get the value.
+    */
+    if (attr->children != NULL) {
+	if ((attr->children->type != XML_TEXT_NODE) ||
+	    (attr->children->next != NULL))
+	{
+	    xsltTransformError(ctxt, NULL, attr->parent,
+		"Internal error: The children of an attribute node of a "
+		"literal result element are not in the expected form.\n");
+	    return(NULL);
+	}
+	value = attr->children->content;
+	if (value == NULL)
+	    value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
+    } else
+	value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
+    /*
+    * Overwrite duplicates.
+    */
+    ret = target->properties;
+    while (ret != NULL) {
+        if (((attr->ns != NULL) == (ret->ns != NULL)) &&
+	    xmlStrEqual(ret->name, attr->name) &&
+	    ((attr->ns == NULL) || xmlStrEqual(ret->ns->href, attr->ns->href)))
+	{
+	    break;
+	}
+        ret = ret->next;
+    }
+    if (ret != NULL) {	
+        /* free the existing value */
+	xmlFreeNodeList(ret->children);
+	ret->children = ret->last = NULL;
+	/*
+	* Adjust ns-prefix if needed.
+	*/
+	if ((ret->ns != NULL) &&
+	    (! xmlStrEqual(ret->ns->prefix, attr->ns->prefix)))
+	{
+	    ret->ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target);
+	}
+    } else {
+        /* create a new attribute */
+	if (attr->ns != NULL)
+	    ret = xmlNewNsProp(target,
+		xsltGetNamespace(ctxt, attr->parent, attr->ns, target),
+		    attr->name, NULL);
+	else
+	    ret = xmlNewNsProp(target, NULL, attr->name, NULL);	
+    }
+    /*
+    * Set the value.
+    */
+    if (ret != NULL) {
+        xmlNodePtr text;
+
+        text = xmlNewText(NULL);
+	if (text != NULL) {
+	    ret->last = ret->children = text;
+	    text->parent = (xmlNodePtr) ret;
+	    text->doc = ret->doc;
+
+	    if (attr->psvi != NULL) {
+		/*
+		* Evaluate the Attribute Value Template.
+		*/
+		xmlChar *val;
+		val = xsltEvalAVT(ctxt, attr->psvi, attr->parent);
+		if (val == NULL) {
+		    /*
+		    * TODO: Damn, we need an easy mechanism to report
+		    * qualified names!
+		    */
+		    if (attr->ns) {
+			xsltTransformError(ctxt, NULL, attr->parent,
+			    "Internal error: Failed to evaluate the AVT "
+			    "of attribute '{%s}%s'.\n",
+			    attr->ns->href, attr->name);
+		    } else {
+			xsltTransformError(ctxt, NULL, attr->parent,
+			    "Internal error: Failed to evaluate the AVT "
+			    "of attribute '%s'.\n",
+			    attr->name);
+		    }
+		    text->content = xmlStrdup(BAD_CAST "");
+		} else {
+		    text->content = val;
+		}
+	    } else if ((ctxt->internalized) && (target != NULL) &&
+	               (target->doc != NULL) &&
+		       (target->doc->dict == ctxt->dict)) {
+		text->content = (xmlChar *) value;
+	    } else {
+		text->content = xmlStrdup(value);
+	    }
+	}
+    } else {
+	if (attr->ns) {
+	    xsltTransformError(ctxt, NULL, attr->parent,
+	    	"Internal error: Failed to create attribute '{%s}%s'.\n",
+		attr->ns->href, attr->name);
+	} else {
+	    xsltTransformError(ctxt, NULL, attr->parent,
+	    	"Internal error: Failed to create attribute '%s'.\n",
+		attr->name);
+	}
+    }
+    return(ret);
+}
+
+
+/**
+ * xsltAttrListTemplateProcess:
+ * @ctxt:  the XSLT transformation context
+ * @target:  the element where the attributes will be grafted
+ * @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))
+	return(NULL);
+
+    oldInsert = ctxt->insert;
+    ctxt->insert = target;        
+
+    /*
+    * Instantiate LRE-attributes.
+    */
+    if (target->properties) {
+	last = target->properties;
+	while (last->next != NULL)
+	    last = last->next;
+    } else {
+	last = NULL;
+    }
+    attr = attrs;
+    do {
+	/*
+	* Skip XSLT attributes.
+	*/
+#ifdef XSLT_REFACTORED
+	if (attr->psvi == xsltXSLTAttrMarker) {
+	    goto next_attribute;
+	}
+#else
+	if ((attr->ns != NULL) &&
+	    xmlStrEqual(attr->ns->href, XSLT_NAMESPACE))
+	{
+	    goto next_attribute;
+	}
+#endif
+	/*
+	* Get the value.
+	*/
+	if (attr->children != NULL) {
+	    if ((attr->children->type != XML_TEXT_NODE) ||
+		(attr->children->next != NULL))
+	    {
+		xsltTransformError(ctxt, NULL, attr->parent,
+		    "Internal error: The children of an attribute node of a "
+		    "literal result element are not in the expected form.\n");
+		goto error;
+	    }
+	    value = attr->children->content;
+	    if (value == NULL)
+		value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
+	} else
+	    value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0);
+
+	/*
+	* Create a new attribute.
+	*/
+	copy = xmlNewDocProp(target->doc, attr->name, NULL);
+	if (copy == NULL) {
+	    if (attr->ns) {
+		xsltTransformError(ctxt, NULL, attr->parent,
+		    "Internal error: Failed to create attribute '{%s}%s'.\n",
+		    attr->ns->href, attr->name);
+	    } else {
+		xsltTransformError(ctxt, NULL, attr->parent,
+		    "Internal error: Failed to create attribute '%s'.\n",
+		    attr->name);
+	    }
+	    goto error;
+	}
+	/*
+	* Attach it to the target element.
+	*/
+	copy->parent = target;
+	if (last == NULL) {
+	    target->properties = copy;
+	    last = copy;
+	} else {
+	    last->next = copy;
+	    copy->prev = last;
+	    last = copy;
+	}
+	/*
+	* Set the namespace. Avoid lookups of same namespaces.
+	*/
+	if (attr->ns != origNs) {
+	    origNs = attr->ns;
+	    if (attr->ns != NULL) {
+#ifdef XSLT_REFACTORED
+		copyNs = xsltGetSpecialNamespace(ctxt, attr->parent,
+		    attr->ns->href, attr->ns->prefix, target);
+#else
+		copyNs = xsltGetNamespace(ctxt, attr->parent,
+		    attr->ns, target);
+#endif
+		if (copyNs == NULL)
+		    goto error;
+	    } else
+		copyNs = NULL;
+	}
+	copy->ns = copyNs;
+
+	/*
+	* Set the value.
+	*/
+	text = xmlNewText(NULL);
+	if (text != NULL) {
+	    copy->last = copy->children = text;
+	    text->parent = (xmlNodePtr) copy;
+	    text->doc = copy->doc;
+
+	    if (attr->psvi != NULL) {
+		/*
+		* Evaluate the Attribute Value Template.
+		*/
+		valueAVT = xsltEvalAVT(ctxt, attr->psvi, attr->parent);
+		if (valueAVT == NULL) {
+		    /*
+		    * TODO: Damn, we need an easy mechanism to report
+		    * qualified names!
+		    */
+		    if (attr->ns) {
+			xsltTransformError(ctxt, NULL, attr->parent,
+			    "Internal error: Failed to evaluate the AVT "
+			    "of attribute '{%s}%s'.\n",
+			    attr->ns->href, attr->name);
+		    } else {
+			xsltTransformError(ctxt, NULL, attr->parent,
+			    "Internal error: Failed to evaluate the AVT "
+			    "of attribute '%s'.\n",
+			    attr->name);
+		    }
+		    text->content = xmlStrdup(BAD_CAST "");
+		    goto error;
+		} else {
+		    text->content = valueAVT;
+		}
+	    } else if ((ctxt->internalized) &&
+		(target->doc != NULL) &&
+		(target->doc->dict == ctxt->dict))
+	    {
+		text->content = (xmlChar *) value;
+	    } else {
+		text->content = xmlStrdup(value);
+	    }
+            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..18adfdb
--- /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..a4ca41d
--- /dev/null
+++ b/libxslt/transform.c
@@ -0,0 +1,6475 @@
+/*
+ * 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 <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
+#endif
+
+#define XSLT_GENERATE_HTML_DOCTYPE
+#ifdef XSLT_GENERATE_HTML_DOCTYPE
+static int xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID,
+			  const xmlChar **systemID);
+#endif
+
+int xsltMaxDepth = 3000;
+
+/*
+ * Useful macros
+ */
+
+#ifndef FALSE
+# define FALSE (0 == 1)
+# define TRUE (!FALSE)
+#endif
+
+#define IS_BLANK_NODE(n)						\
+    (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
+
+
+/*
+* Forward declarations
+*/
+
+static xmlNsPtr
+xsltCopyNamespaceListInternal(xmlNodePtr node, xmlNsPtr cur);
+