diff options
Diffstat (limited to 'libxslt')
49 files changed, 40048 insertions, 0 deletions
diff --git a/libxslt/Makefile.am b/libxslt/Makefile.am new file mode 100644 index 0000000..d9fed68 --- /dev/null +++ b/libxslt/Makefile.am @@ -0,0 +1,79 @@ +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/libxslt + +AM_CFLAGS = $(LIBXML_CFLAGS) + +lib_LTLIBRARIES = libxslt.la + +xsltincdir = $(includedir)/libxslt + +xsltinc_HEADERS = \ + xslt.h \ + xsltutils.h \ + pattern.h \ + templates.h \ + variables.h \ + keys.h \ + numbersInternals.h \ + extensions.h \ + extra.h \ + functions.h \ + namespaces.h \ + imports.h \ + attributes.h \ + documents.h \ + preproc.h \ + transform.h \ + security.h \ + xsltInternals.h \ + xsltexports.h \ + xsltlocale.h +nodist_xsltinc_HEADERS = \ + xsltconfig.h + +libxslt_la_SOURCES = \ + attrvt.c \ + xslt.c \ + xsltlocale.c \ + xsltutils.c \ + pattern.c \ + templates.c \ + variables.c \ + keys.c \ + numbers.c \ + extensions.c \ + extra.c \ + functions.c \ + namespaces.c \ + imports.c \ + attributes.c \ + documents.c \ + preproc.c \ + transform.c \ + security.c \ + win32config.h \ + xsltwin32config.h.in \ + libxslt.h +nodist_libxslt_la_SOURCES = \ + xsltwin32config.h + +if USE_VERSION_SCRIPT +LIBXSLT_VERSION_SCRIPT = $(VERSION_SCRIPT_FLAGS)$(srcdir)/libxslt.syms +else +LIBXSLT_VERSION_SCRIPT = +endif + +libxslt_la_LIBADD = $(LIBXML_LIBS) $(EXTRA_LIBS) +libxslt_la_LDFLAGS = \ + $(WIN32_EXTRA_LDFLAGS) \ + $(LIBXSLT_VERSION_SCRIPT) \ + -version-info $(LIBXSLT_VERSION_INFO) + +man_MANS = libxslt.3 + +EXTRA_DIST = $(man_MANS) trio.h triodef.h libxslt.syms + +xsltproc: all + @(cd ../xsltproc ; $(MAKE)) + +install-exec-hook: + $(MKDIR_P) "$(DESTDIR)$(libdir)/libxslt-plugins" diff --git a/libxslt/attributes.c b/libxslt/attributes.c new file mode 100644 index 0000000..9165ab1 --- /dev/null +++ b/libxslt/attributes.c @@ -0,0 +1,1240 @@ +/* + * attributes.c: Implementation of the XSLT attributes handling + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_MATH_H +#include <math.h> +#endif +#ifdef HAVE_FLOAT_H +#include <float.h> +#endif +#ifdef HAVE_IEEEFP_H +#include <ieeefp.h> +#endif +#ifdef HAVE_NAN_H +#include <nan.h> +#endif +#ifdef HAVE_CTYPE_H +#include <ctype.h> +#endif + +#include <libxml/xmlmemory.h> +#include <libxml/tree.h> +#include <libxml/hash.h> +#include <libxml/xmlerror.h> +#include <libxml/uri.h> +#include <libxml/parserInternals.h> +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "attributes.h" +#include "namespaces.h" +#include "templates.h" +#include "imports.h" +#include "transform.h" +#include "preproc.h" + +#define WITH_XSLT_DEBUG_ATTRIBUTES +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_ATTRIBUTES +#endif + +/* + * Useful macros + */ +#ifdef IS_BLANK +#undef IS_BLANK +#endif + +#define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \ + ((c) == 0x0D)) + +#define IS_BLANK_NODE(n) \ + (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content))) + +#define ATTRSET_UNRESOLVED 0 +#define ATTRSET_RESOLVING 1 +#define ATTRSET_RESOLVED 2 + + +/* + * The in-memory structure corresponding to an XSLT Attribute in + * an attribute set + */ + + +typedef struct _xsltAttrElem xsltAttrElem; +typedef xsltAttrElem *xsltAttrElemPtr; +struct _xsltAttrElem { + struct _xsltAttrElem *next;/* chained list */ + xmlNodePtr attr; /* the xsl:attribute definition */ +}; + +typedef struct _xsltUseAttrSet xsltUseAttrSet; +typedef xsltUseAttrSet *xsltUseAttrSetPtr; +struct _xsltUseAttrSet { + struct _xsltUseAttrSet *next; /* chained list */ + const xmlChar *ncname; + const xmlChar *ns; +}; + +typedef struct _xsltAttrSet xsltAttrSet; +typedef xsltAttrSet *xsltAttrSetPtr; +struct _xsltAttrSet { + int state; + xsltAttrElemPtr attrs; /* list head */ + xsltUseAttrSetPtr useAttrSets; /* list head */ +}; + +typedef struct _xsltAttrSetContext xsltAttrSetContext; +typedef xsltAttrSetContext *xsltAttrSetContextPtr; +struct _xsltAttrSetContext { + xsltStylesheetPtr topStyle; + xsltStylesheetPtr style; +}; + +static void +xsltResolveAttrSet(xsltAttrSetPtr set, xsltStylesheetPtr topStyle, + xsltStylesheetPtr style, const xmlChar *name, + const xmlChar *ns, int depth); + +/************************************************************************ + * * + * XSLT Attribute handling * + * * + ************************************************************************/ + +/** + * xsltNewAttrElem: + * @attr: the new xsl:attribute node + * + * Create a new XSLT AttrElem + * + * Returns the newly allocated xsltAttrElemPtr or NULL in case of error + */ +static xsltAttrElemPtr +xsltNewAttrElem(xmlNodePtr attr) { + xsltAttrElemPtr cur; + + cur = (xsltAttrElemPtr) xmlMalloc(sizeof(xsltAttrElem)); + if (cur == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltNewAttrElem : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltAttrElem)); + cur->attr = attr; + return(cur); +} + +/** + * xsltFreeAttrElem: + * @attr: an XSLT AttrElem + * + * Free up the memory allocated by @attr + */ +static void +xsltFreeAttrElem(xsltAttrElemPtr attr) { + xmlFree(attr); +} + +/** + * xsltFreeAttrElemList: + * @list: an XSLT AttrElem list + * + * Free up the memory allocated by @list + */ +static void +xsltFreeAttrElemList(xsltAttrElemPtr list) { + xsltAttrElemPtr next; + + while (list != NULL) { + next = list->next; + xsltFreeAttrElem(list); + list = next; + } +} + +/** + * xsltAddAttrElemList: + * @list: an XSLT AttrElem list + * @attr: the new xsl:attribute node + * + * Add the new attribute to the list. + * + * Returns the new list pointer + */ +static xsltAttrElemPtr +xsltAddAttrElemList(xsltAttrElemPtr list, xmlNodePtr attr) { + xsltAttrElemPtr next, cur; + + if (attr == NULL) + return(list); + if (list == NULL) + return(xsltNewAttrElem(attr)); + cur = list; + while (cur != NULL) { + next = cur->next; + if (next == NULL) { + cur->next = xsltNewAttrElem(attr); + return(list); + } + cur = next; + } + return(list); +} + +/** + * xsltNewUseAttrSet: + * @ncname: local name + * @ns: namespace URI + * + * Create a new XSLT UseAttrSet + * + * Returns the newly allocated xsltUseAttrSetPtr or NULL in case of error. + */ +static xsltUseAttrSetPtr +xsltNewUseAttrSet(const xmlChar *ncname, const xmlChar *ns) { + xsltUseAttrSetPtr cur; + + cur = (xsltUseAttrSetPtr) xmlMalloc(sizeof(xsltUseAttrSet)); + if (cur == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltNewUseAttrSet : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltUseAttrSet)); + cur->ncname = ncname; + cur->ns = ns; + return(cur); +} + +/** + * xsltFreeUseAttrSet: + * @use: an XSLT UseAttrSet + * + * Free up the memory allocated by @use + */ +static void +xsltFreeUseAttrSet(xsltUseAttrSetPtr use) { + xmlFree(use); +} + +/** + * xsltFreeUseAttrSetList: + * @list: an XSLT UseAttrSet list + * + * Free up the memory allocated by @list + */ +static void +xsltFreeUseAttrSetList(xsltUseAttrSetPtr list) { + xsltUseAttrSetPtr next; + + while (list != NULL) { + next = list->next; + xsltFreeUseAttrSet(list); + list = next; + } +} + +/** + * xsltAddUseAttrSetList: + * @list: a xsltUseAttrSet list + * @ncname: local name + * @ns: namespace URI + * + * Add the use-attribute-set name to the list. + * + * Returns the new list pointer. + */ +static xsltUseAttrSetPtr +xsltAddUseAttrSetList(xsltUseAttrSetPtr list, const xmlChar *ncname, + const xmlChar *ns) { + xsltUseAttrSetPtr next, cur; + + if (ncname == NULL) + return(list); + if (list == NULL) + return(xsltNewUseAttrSet(ncname, ns)); + cur = list; + while (cur != NULL) { + if ((cur->ncname == ncname) && (cur->ns == ns)) + return(list); + next = cur->next; + if (next == NULL) { + cur->next = xsltNewUseAttrSet(ncname, ns); + return(list); + } + cur = next; + } + return(list); +} + +/** + * xsltNewAttrSet: + * + * Create a new attribute set. + * + * Returns the newly allocated xsltAttrSetPtr or NULL in case of error. + */ +static xsltAttrSetPtr +xsltNewAttrSet() { + xsltAttrSetPtr cur; + + cur = (xsltAttrSetPtr) xmlMalloc(sizeof(xsltAttrSet)); + if (cur == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltNewAttrSet : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltAttrSet)); + return(cur); +} + +/** + * xsltFreeAttrSet: + * @set: an attribute set + * + * Free memory allocated by @set + */ +static void +xsltFreeAttrSet(xsltAttrSetPtr set) { + if (set == NULL) + return; + + xsltFreeAttrElemList(set->attrs); + xsltFreeUseAttrSetList(set->useAttrSets); + xmlFree(set); +} + +/** + * xsltMergeAttrSets: + * @set: an attribute set + * @other: another attribute set + * + * Add all the attributes from @other to @set, + * but drop redefinition of existing values. + */ +static void +xsltMergeAttrSets(xsltAttrSetPtr set, xsltAttrSetPtr other) { + xsltAttrElemPtr cur; + xsltAttrElemPtr old = other->attrs; + int add; + + while (old != NULL) { + /* + * Check that the attribute is not yet in the list + */ + cur = set->attrs; + add = 1; + while (cur != NULL) { + xsltStylePreCompPtr curComp = cur->attr->psvi; + xsltStylePreCompPtr oldComp = old->attr->psvi; + + if ((curComp->name == oldComp->name) && + (curComp->ns == oldComp->ns)) { + add = 0; + break; + } + if (cur->next == NULL) + break; + cur = cur->next; + } + + if (add == 1) { + if (cur == NULL) { + set->attrs = xsltNewAttrElem(old->attr); + } else if (add) { + cur->next = xsltNewAttrElem(old->attr); + } + } + + old = old->next; + } +} + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +/** + * xsltParseStylesheetAttributeSet: + * @style: the XSLT stylesheet + * @cur: the "attribute-set" element + * + * parse an XSLT stylesheet attribute-set element + */ + +void +xsltParseStylesheetAttributeSet(xsltStylesheetPtr style, xmlNodePtr cur) { + const xmlChar *ncname; + const xmlChar *prefix; + const xmlChar *nsUri = NULL; + xmlChar *value; + xmlNodePtr child; + xsltAttrSetPtr set; + + if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) + return; + + value = xmlGetNsProp(cur, (const xmlChar *)"name", NULL); + if ((value == NULL) || (*value == 0)) { + xsltGenericError(xsltGenericErrorContext, + "xsl:attribute-set : name is missing\n"); + if (value) + xmlFree(value); + return; + } + + if (xmlValidateQName(value, 0)) { + xsltTransformError(NULL, style, cur, + "xsl:attribute-set : The name '%s' is not a valid QName.\n", + value); + style->errors++; + xmlFree(value); + return; + } + + ncname = xsltSplitQName(style->dict, value, &prefix); + xmlFree(value); + value = NULL; + if (prefix != NULL) { + xmlNsPtr ns = xmlSearchNs(style->doc, cur, prefix); + if (ns == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:attribute-set : No namespace found for QName '%s:%s'\n", + prefix, ncname); + style->errors++; + return; + } + nsUri = ns->href; + } + + if (style->attributeSets == NULL) { +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "creating attribute set table\n"); +#endif + style->attributeSets = xmlHashCreate(10); + } + if (style->attributeSets == NULL) + return; + + set = xmlHashLookup2(style->attributeSets, ncname, nsUri); + if (set == NULL) { + set = xsltNewAttrSet(); + if (set == NULL) + return; + xmlHashAddEntry2(style->attributeSets, ncname, nsUri, set); + } + + /* + * Parse the content. Only xsl:attribute elements are allowed. + */ + child = cur->children; + while (child != NULL) { + /* + * Report invalid nodes. + */ + if ((child->type != XML_ELEMENT_NODE) || + (child->ns == NULL) || + (! IS_XSLT_ELEM(child))) + { + if (child->type == XML_ELEMENT_NODE) + xsltTransformError(NULL, style, child, + "xsl:attribute-set : unexpected child %s\n", + child->name); + else + xsltTransformError(NULL, style, child, + "xsl:attribute-set : child of unexpected type\n"); + } else if (!IS_XSLT_NAME(child, "attribute")) { + xsltTransformError(NULL, style, child, + "xsl:attribute-set : unexpected child xsl:%s\n", + child->name); + } else { +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "add attribute to list %s\n", ncname); +#endif + xsltStylePreCompute(style, child); + if (child->children != NULL) { +#ifdef XSLT_REFACTORED + xsltParseSequenceConstructor(XSLT_CCTXT(style), + child->children); +#else + xsltParseTemplateContent(style, child); +#endif + } + if (child->psvi == NULL) { + xsltTransformError(NULL, style, child, + "xsl:attribute-set : internal error, attribute %s not " + "compiled\n", child->name); + } + else { + set->attrs = xsltAddAttrElemList(set->attrs, child); + } + } + + child = child->next; + } + + /* + * Process attribute "use-attribute-sets". + */ + value = xmlGetNsProp(cur, BAD_CAST "use-attribute-sets", NULL); + if (value != NULL) { + const xmlChar *curval, *endval; + curval = value; + while (*curval != 0) { + while (IS_BLANK(*curval)) curval++; + if (*curval == 0) + break; + endval = curval; + while ((*endval != 0) && (!IS_BLANK(*endval))) endval++; + curval = xmlDictLookup(style->dict, curval, endval - curval); + if (curval) { + const xmlChar *ncname2 = NULL; + const xmlChar *prefix2 = NULL; + const xmlChar *nsUri2 = NULL; + +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "xsl:attribute-set : %s adds use %s\n", ncname, curval); +#endif + + if (xmlValidateQName(curval, 0)) { + xsltTransformError(NULL, style, cur, + "xsl:attribute-set : The name '%s' in " + "use-attribute-sets is not a valid QName.\n", curval); + style->errors++; + xmlFree(value); + return; + } + + ncname2 = xsltSplitQName(style->dict, curval, &prefix2); + if (prefix2 != NULL) { + xmlNsPtr ns2 = xmlSearchNs(style->doc, cur, prefix2); + if (ns2 == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:attribute-set : No namespace found for QName " + "'%s:%s' in use-attribute-sets\n", + prefix2, ncname2); + style->errors++; + xmlFree(value); + return; + } + nsUri2 = ns2->href; + } + set->useAttrSets = xsltAddUseAttrSetList(set->useAttrSets, + ncname2, nsUri2); + } + curval = endval; + } + xmlFree(value); + value = NULL; + } + +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "updated attribute list %s\n", ncname); +#endif +} + +/** + * xsltResolveUseAttrSets: + * @set: the attribute set + * @asctx: the context for attribute set resolution + * @depth: recursion depth + * + * Process "use-attribute-sets". + */ +static void +xsltResolveUseAttrSets(xsltAttrSetPtr set, xsltStylesheetPtr topStyle, + int depth) { + xsltStylesheetPtr cur; + xsltAttrSetPtr other; + xsltUseAttrSetPtr use = set->useAttrSets; + xsltUseAttrSetPtr next; + + while (use != NULL) { + /* + * Iterate top stylesheet and all imports. + */ + cur = topStyle; + while (cur != NULL) { + if (cur->attributeSets) { + other = xmlHashLookup2(cur->attributeSets, use->ncname, + use->ns); + if (other != NULL) { + xsltResolveAttrSet(other, topStyle, cur, use->ncname, + use->ns, depth + 1); + xsltMergeAttrSets(set, other); + break; + } + } + cur = xsltNextImport(cur); + } + + next = use->next; + /* Free useAttrSets early. */ + xsltFreeUseAttrSet(use); + use = next; + } + + set->useAttrSets = NULL; +} + +/** + * xsltResolveAttrSet: + * @set: the attribute set + * @asctx: the context for attribute set resolution + * @name: the local name of the attirbute set + * @ns: the namespace of the attribute set + * @depth: recursion depth + * + * resolve the references in an attribute set. + */ +static void +xsltResolveAttrSet(xsltAttrSetPtr set, xsltStylesheetPtr topStyle, + xsltStylesheetPtr style, const xmlChar *name, + const xmlChar *ns, int depth) { + xsltStylesheetPtr cur; + xsltAttrSetPtr other; + + if (set->state == ATTRSET_RESOLVED) + return; + if (set->state == ATTRSET_RESOLVING) { + xsltTransformError(NULL, topStyle, NULL, + "xsl:attribute-set : use-attribute-sets recursion detected" + " on %s\n", name); + topStyle->errors++; + set->state = ATTRSET_RESOLVED; + return; + } + if (depth > 100) { + xsltTransformError(NULL, topStyle, NULL, + "xsl:attribute-set : use-attribute-sets maximum recursion " + "depth exceeded on %s\n", name); + topStyle->errors++; + return; + } + + set->state = ATTRSET_RESOLVING; + + xsltResolveUseAttrSets(set, topStyle, depth); + + /* Merge imported sets. */ + cur = xsltNextImport(style); + while (cur != NULL) { + if (cur->attributeSets != NULL) { + other = xmlHashLookup2(cur->attributeSets, name, ns); + + if (other != NULL) { +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "xsl:attribute-set : merging import for %s\n", name); +#endif + xsltResolveUseAttrSets(other, topStyle, depth); + xsltMergeAttrSets(set, other); + xmlHashRemoveEntry2(cur->attributeSets, name, ns, NULL); + xsltFreeAttrSet(other); + } + } + + cur = xsltNextImport(cur); + } + + set->state = ATTRSET_RESOLVED; +} + +/** + * xsltResolveSASCallback: + * @set: the attribute set + * @asctx: the context for attribute set resolution + * @name: the local name of the attirbute set + * @ns: the namespace of the attribute set + * + * resolve the references in an attribute set. + */ +static void +xsltResolveSASCallback(xsltAttrSetPtr set, xsltAttrSetContextPtr asctx, + const xmlChar *name, const xmlChar *ns, + ATTRIBUTE_UNUSED const xmlChar *ignored) { + xsltStylesheetPtr topStyle = asctx->topStyle; + xsltStylesheetPtr style = asctx->style; + + xsltResolveAttrSet(set, topStyle, style, name, ns, 1); + + /* Move attribute sets to top stylesheet. */ + if (style != topStyle) { + /* + * This imported stylesheet won't be visited anymore. Don't bother + * removing the hash entry. + */ + if (xmlHashAddEntry2(topStyle->attributeSets, name, ns, set) < 0) { + xsltGenericError(xsltGenericErrorContext, + "xsl:attribute-set : internal error, can't move imported " + " attribute set %s\n", name); + } + } +} + +/** + * xsltResolveStylesheetAttributeSet: + * @style: the XSLT stylesheet + * + * resolve the references between attribute sets. + */ +void +xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style) { + xsltStylesheetPtr cur; + xsltAttrSetContext asctx; + +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "Resolving attribute sets references\n"); +#endif + asctx.topStyle = style; + cur = style; + while (cur != NULL) { + if (cur->attributeSets != NULL) { + if (style->attributeSets == NULL) { +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "creating attribute set table\n"); +#endif + style->attributeSets = xmlHashCreate(10); + } + asctx.style = cur; + xmlHashScanFull(cur->attributeSets, + (xmlHashScannerFull) xsltResolveSASCallback, &asctx); + + if (cur != style) { + /* + * the attribute lists have either been migrated to style + * or freed directly in xsltResolveSASCallback() + */ + xmlHashFree(cur->attributeSets, NULL); + cur->attributeSets = NULL; + } + } + cur = xsltNextImport(cur); + } +} + +/** + * xsltAttribute: + * @ctxt: a XSLT process context + * @node: the current node in the source tree + * @inst: the xsl:attribute element + * @comp: precomputed information + * + * Process the xslt attribute node on the source node + */ +void +xsltAttribute(xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, + xmlNodePtr inst, + xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemAttributePtr comp = + (xsltStyleItemAttributePtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xmlNodePtr targetElem; + xmlChar *prop = NULL; + const xmlChar *name = NULL, *prefix = NULL, *nsName = NULL; + xmlChar *value = NULL; + xmlNsPtr ns = NULL; + xmlAttrPtr attr; + + if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL) || + (inst->type != XML_ELEMENT_NODE) ) + return; + + /* + * A comp->has_name == 0 indicates that we need to skip this instruction, + * since it was evaluated to be invalid already during compilation. + */ + if (!comp->has_name) + return; + /* + * BIG NOTE: This previously used xsltGetSpecialNamespace() and + * xsltGetNamespace(), but since both are not appropriate, we + * will process namespace lookup here to avoid adding yet another + * ns-lookup function to namespaces.c. + */ + /* + * SPEC XSLT 1.0: Error cases: + * - Creating nodes other than text nodes during the instantiation of + * the content of the xsl:attribute element; implementations may + * either signal the error or ignore the offending nodes." + */ + + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltAttribute(): " + "The XSLT 'attribute' instruction was not compiled.\n"); + return; + } + /* + * TODO: Shouldn't ctxt->insert == NULL be treated as an internal error? + * So report an internal error? + */ + if (ctxt->insert == NULL) + return; + /* + * SPEC XSLT 1.0: + * "Adding an attribute to a node that is not an element; + * implementations may either signal the error or ignore the attribute." + * + * TODO: I think we should signal such errors in the future, and maybe + * provide an option to ignore such errors. + */ + targetElem = ctxt->insert; + if (targetElem->type != XML_ELEMENT_NODE) + return; + + /* + * SPEC XSLT 1.0: + * "Adding an attribute to an element after children have been added + * to it; implementations may either signal the error or ignore the + * attribute." + * + * TODO: We should decide whether not to report such errors or + * to ignore them; note that we *ignore* if the parent is not an + * element, but here we report an error. + */ + if (targetElem->children != NULL) { + /* + * NOTE: Ah! This seems to be intended to support streamed + * result generation!. + */ + xsltTransformError(ctxt, NULL, inst, + "xsl:attribute: Cannot add attributes to an " + "element if children have been already added " + "to the element.\n"); + return; + } + + /* + * Process the name + * ---------------- + */ + +#ifdef WITH_DEBUGGER + if (ctxt->debugStatus != XSLT_DEBUG_NONE) + xslHandleDebugger(inst, contextNode, NULL, ctxt); +#endif + + if (comp->name == NULL) { + /* TODO: fix attr acquisition wrt to the XSLT namespace */ + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "name", XSLT_NAMESPACE); + if (prop == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:attribute: The attribute 'name' is missing.\n"); + goto error; + } + if (xmlValidateQName(prop, 0)) { + xsltTransformError(ctxt, NULL, inst, + "xsl:attribute: The effective name '%s' is not a " + "valid QName.\n", prop); + /* we fall through to catch any further errors, if possible */ + } + + /* + * Reject a name of "xmlns". + */ + if (xmlStrEqual(prop, BAD_CAST "xmlns")) { + xsltTransformError(ctxt, NULL, inst, + "xsl:attribute: The effective name 'xmlns' is not allowed.\n"); + xmlFree(prop); + goto error; + } + + name = xsltSplitQName(ctxt->dict, prop, &prefix); + xmlFree(prop); + } else { + /* + * The "name" value was static. + */ +#ifdef XSLT_REFACTORED + prefix = comp->nsPrefix; + name = comp->name; +#else + name = xsltSplitQName(ctxt->dict, comp->name, &prefix); +#endif + } + + /* + * Process namespace semantics + * --------------------------- + * + * Evaluate the namespace name. + */ + if (comp->has_ns) { + /* + * The "namespace" attribute was existent. + */ + if (comp->ns != NULL) { + /* + * No AVT; just plain text for the namespace name. + */ + if (comp->ns[0] != 0) + nsName = comp->ns; + } else { + xmlChar *tmpNsName; + /* + * Eval the AVT. + */ + /* TODO: check attr acquisition wrt to the XSLT namespace */ + tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "namespace", XSLT_NAMESPACE); + /* + * This fixes bug #302020: The AVT might also evaluate to the + * empty string; this means that the empty string also indicates + * "no namespace". + * SPEC XSLT 1.0: + * "If the string is empty, then the expanded-name of the + * attribute has a null namespace URI." + */ + if ((tmpNsName != NULL) && (tmpNsName[0] != 0)) + nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1); + xmlFree(tmpNsName); + } + + if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) { + xsltTransformError(ctxt, NULL, inst, + "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ " + "forbidden.\n"); + goto error; + } + if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) { + prefix = BAD_CAST "xml"; + } else if (xmlStrEqual(prefix, BAD_CAST "xml")) { + prefix = NULL; + } + } else if (prefix != NULL) { + /* + * SPEC XSLT 1.0: + * "If the namespace attribute is not present, then the QName is + * expanded into an expanded-name using the namespace declarations + * in effect for the xsl:attribute element, *not* including any + * default namespace declaration." + */ + ns = xmlSearchNs(inst->doc, inst, prefix); + if (ns == NULL) { + /* + * Note that this is treated as an error now (checked with + * Saxon, Xalan-J and MSXML). + */ + xsltTransformError(ctxt, NULL, inst, + "xsl:attribute: The QName '%s:%s' has no " + "namespace binding in scope in the stylesheet; " + "this is an error, since the namespace was not " + "specified by the instruction itself.\n", prefix, name); + } else + nsName = ns->href; + } + + /* + * Find/create a matching ns-decl in the result tree. + */ + ns = NULL; + +#if 0 + if (0) { + /* + * OPTIMIZE TODO: How do we know if we are adding to a + * fragment or to the result tree? + * + * If we are adding to a result tree fragment (i.e., not to the + * actual result tree), we'll don't bother searching for the + * ns-decl, but just store it in the dummy-doc of the result + * tree fragment. + */ + if (nsName != NULL) { + /* + * TODO: Get the doc of @targetElem. + */ + ns = xsltTreeAcquireStoredNs(some doc, nsName, prefix); + } + } +#endif + + if (nsName != NULL) { + /* + * Something about ns-prefixes: + * SPEC XSLT 1.0: + * "XSLT processors may make use of the prefix of the QName specified + * in the name attribute when selecting the prefix used for outputting + * the created attribute as XML; however, they are not required to do + * so and, if the prefix is xmlns, they must not do so" + */ + /* + * xsl:attribute can produce a scenario where the prefix is NULL, + * so generate a prefix. + */ + if ((prefix == NULL) || xmlStrEqual(prefix, BAD_CAST "xmlns")) { + xmlChar *pref = xmlStrdup(BAD_CAST "ns_1"); + + ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, targetElem); + + xmlFree(pref); + } else { + ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix, + targetElem); + } + if (ns == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Namespace fixup error: Failed to acquire an in-scope " + "namespace binding for the generated attribute '{%s}%s'.\n", + nsName, name); + goto error; + } + } + /* + * Construction of the value + * ------------------------- + */ + if (inst->children == NULL) { + /* + * No content. + * TODO: Do we need to put the empty string in ? + */ + attr = xmlSetNsProp(ctxt->insert, ns, name, (const xmlChar *) ""); + } else if ((inst->children->next == NULL) && + ((inst->children->type == XML_TEXT_NODE) || + (inst->children->type == XML_CDATA_SECTION_NODE))) + { + xmlNodePtr copyTxt; + + /* + * xmlSetNsProp() will take care of duplicates. + */ + attr = xmlSetNsProp(ctxt->insert, ns, name, NULL); + if (attr == NULL) /* TODO: report error ? */ + goto error; + /* + * This was taken over from xsltCopyText() (transform.c). + */ + if (ctxt->internalized && + (ctxt->insert->doc != NULL) && + (ctxt->insert->doc->dict == ctxt->dict)) + { + copyTxt = xmlNewText(NULL); + if (copyTxt == NULL) /* TODO: report error */ + goto error; + /* + * This is a safe scenario where we don't need to lookup + * the dict. + */ + copyTxt->content = inst->children->content; + /* + * Copy "disable-output-escaping" information. + * TODO: Does this have any effect for attribute values + * anyway? + */ + if (inst->children->name == xmlStringTextNoenc) + copyTxt->name = xmlStringTextNoenc; + } else { + /* + * Copy the value. + */ + copyTxt = xmlNewText(inst->children->content); + if (copyTxt == NULL) /* TODO: report error */ + goto error; + } + attr->children = attr->last = copyTxt; + copyTxt->parent = (xmlNodePtr) attr; + copyTxt->doc = attr->doc; + /* + * Copy "disable-output-escaping" information. + * TODO: Does this have any effect for attribute values + * anyway? + */ + if (inst->children->name == xmlStringTextNoenc) + copyTxt->name = xmlStringTextNoenc; + + /* + * since we create the attribute without content IDness must be + * asserted as a second step + */ + if ((copyTxt->content != NULL) && + (xmlIsID(attr->doc, attr->parent, attr))) + xmlAddID(NULL, attr->doc, copyTxt->content, attr); + } else { + /* + * The sequence constructor might be complex, so instantiate it. + */ + value = xsltEvalTemplateString(ctxt, contextNode, inst); + if (value != NULL) { + attr = xmlSetNsProp(ctxt->insert, ns, name, value); + xmlFree(value); + } else { + /* + * TODO: Do we have to add the empty string to the attr? + * TODO: Does a value of NULL indicate an + * error in xsltEvalTemplateString() ? + */ + attr = xmlSetNsProp(ctxt->insert, ns, name, + (const xmlChar *) ""); + } + } + +error: + return; +} + +/** + * xsltApplyAttributeSet: + * @ctxt: the XSLT stylesheet + * @node: the node in the source tree. + * @inst: the attribute node "xsl:use-attribute-sets" + * @attrSets: the list of QNames of the attribute-sets to be applied + * + * Apply the xsl:use-attribute-sets. + * If @attrSets is NULL, then @inst will be used to exctract this + * value. + * If both, @attrSets and @inst, are NULL, then this will do nothing. + */ +void +xsltApplyAttributeSet(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, + const xmlChar *attrSets) +{ + const xmlChar *ncname = NULL; + const xmlChar *prefix = NULL; + const xmlChar *curstr, *endstr; + xsltAttrSetPtr set; + xsltStylesheetPtr style; + + if (attrSets == NULL) { + if (inst == NULL) + return; + else { + /* + * Extract the value from @inst. + */ + if (inst->type == XML_ATTRIBUTE_NODE) { + if ( ((xmlAttrPtr) inst)->children != NULL) + attrSets = ((xmlAttrPtr) inst)->children->content; + + } + if (attrSets == NULL) { + /* + * TODO: Return an error? + */ + return; + } + } + } + /* + * Parse/apply the list of QNames. + */ + curstr = attrSets; + while (*curstr != 0) { + while (IS_BLANK(*curstr)) + curstr++; + if (*curstr == 0) + break; + endstr = curstr; + while ((*endstr != 0) && (!IS_BLANK(*endstr))) + endstr++; + curstr = xmlDictLookup(ctxt->dict, curstr, endstr - curstr); + if (curstr) { + xmlNsPtr ns; + const xmlChar *nsUri = NULL; + +#ifdef WITH_XSLT_DEBUG_ATTRIBUTES + xsltGenericDebug(xsltGenericDebugContext, + "apply attribute set %s\n", curstr); +#endif + + if (xmlValidateQName(curstr, 0)) { + xsltTransformError(ctxt, NULL, inst, + "The name '%s' in use-attribute-sets is not a valid " + "QName.\n", curstr); + return; + } + + ncname = xsltSplitQName(ctxt->dict, curstr, &prefix); + if (prefix != NULL) { + ns = xmlSearchNs(inst->doc, inst, prefix); + if (ns == NULL) { + xsltTransformError(ctxt, NULL, inst, + "use-attribute-set : No namespace found for QName " + "'%s:%s'\n", prefix, ncname); + return; + } + nsUri = ns->href; + } + + style = ctxt->style; + +#ifdef WITH_DEBUGGER + if ((style != NULL) && + (style->attributeSets != NULL) && + (ctxt->debugStatus != XSLT_DEBUG_NONE)) + { + set = xmlHashLookup2(style->attributeSets, ncname, nsUri); + if ((set != NULL) && (set->attrs != NULL) && + (set->attrs->attr != NULL)) + xslHandleDebugger(set->attrs->attr->parent, node, NULL, + ctxt); + } +#endif + /* + * Lookup the referenced attribute-set. All attribute sets were + * moved to the top stylesheet so there's no need to iterate + * imported stylesheets + */ + set = xmlHashLookup2(style->attributeSets, ncname, nsUri); + if (set != NULL) { + xsltAttrElemPtr cur = set->attrs; + while (cur != NULL) { + if (cur->attr != NULL) { + xsltAttribute(ctxt, node, cur->attr, + cur->attr->psvi); + } + cur = cur->next; + } + } + } + curstr = endstr; + } +} + +/** + * xsltFreeAttributeSetsHashes: + * @style: an XSLT stylesheet + * + * Free up the memory used by attribute sets + */ +void +xsltFreeAttributeSetsHashes(xsltStylesheetPtr style) { + if (style->attributeSets != NULL) + xmlHashFree((xmlHashTablePtr) style->attributeSets, + (xmlHashDeallocator) xsltFreeAttrSet); + style->attributeSets = NULL; +} diff --git a/libxslt/attributes.h b/libxslt/attributes.h new file mode 100644 index 0000000..05b8a6e --- /dev/null +++ b/libxslt/attributes.h @@ -0,0 +1,38 @@ +/* + * Summary: interface for the XSLT attribute handling + * Description: this module handles the specificities of attribute + * and attribute groups processing. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_ATTRIBUTES_H__ +#define __XML_XSLT_ATTRIBUTES_H__ + +#include <libxml/tree.h> +#include "xsltexports.h" + +#ifdef __cplusplus +extern "C" { +#endif + +XSLTPUBFUN void XSLTCALL + xsltParseStylesheetAttributeSet (xsltStylesheetPtr style, + xmlNodePtr cur); +XSLTPUBFUN void XSLTCALL + xsltFreeAttributeSetsHashes (xsltStylesheetPtr style); +XSLTPUBFUN void XSLTCALL + xsltApplyAttributeSet (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + const xmlChar *attributes); +XSLTPUBFUN void XSLTCALL + xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style); +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_ATTRIBUTES_H__ */ + diff --git a/libxslt/attrvt.c b/libxslt/attrvt.c new file mode 100644 index 0000000..125159c --- /dev/null +++ b/libxslt/attrvt.c @@ -0,0 +1,387 @@ +/* + * attrvt.c: Implementation of the XSL Transformation 1.0 engine + * attribute value template handling part. + * + * References: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * Michael Kay "XSLT Programmer's Reference" pp 637-643 + * Writing Multiple Output Files + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> + +#include <libxml/xmlmemory.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> +#include "xslt.h" +#include "xsltutils.h" +#include "xsltInternals.h" +#include "templates.h" + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_AVT +#endif + +#define MAX_AVT_SEG 10 + +typedef struct _xsltAttrVT xsltAttrVT; +typedef xsltAttrVT *xsltAttrVTPtr; +struct _xsltAttrVT { + struct _xsltAttrVT *next; /* next xsltAttrVT */ + int nb_seg; /* Number of segments */ + int max_seg; /* max capacity before re-alloc needed */ + int strstart; /* is the start a string */ + /* + * the namespaces in scope + */ + xmlNsPtr *nsList; + int nsNr; + /* + * the content is an alternate of string and xmlXPathCompExprPtr + */ + void *segments[MAX_AVT_SEG]; +}; + +/** + * xsltNewAttrVT: + * @style: a XSLT process context + * + * Build a new xsltAttrVT structure + * + * Returns the structure or NULL in case of error + */ +static xsltAttrVTPtr +xsltNewAttrVT(xsltStylesheetPtr style) { + xsltAttrVTPtr cur; + + cur = (xsltAttrVTPtr) xmlMalloc(sizeof(xsltAttrVT)); + if (cur == NULL) { + xsltTransformError(NULL, style, NULL, + "xsltNewAttrVTPtr : malloc failed\n"); + if (style != NULL) style->errors++; + return(NULL); + } + memset(cur, 0, sizeof(xsltAttrVT)); + + cur->nb_seg = 0; + cur->max_seg = MAX_AVT_SEG; + cur->strstart = 0; + cur->next = style->attVTs; + /* + * Note: this pointer may be changed by a re-alloc within xsltCompileAttr, + * so that code may change the stylesheet pointer also! + */ + style->attVTs = (xsltAttrVTPtr) cur; + + return(cur); +} + +/** + * xsltFreeAttrVT: + * @avt: pointer to an xsltAttrVT structure + * + * Free up the memory associated to the attribute value template + */ +static void +xsltFreeAttrVT(xsltAttrVTPtr avt) { + int i; + + if (avt == NULL) return; + + if (avt->strstart == 1) { + for (i = 0;i < avt->nb_seg; i += 2) + if (avt->segments[i] != NULL) + xmlFree((xmlChar *) avt->segments[i]); + for (i = 1;i < avt->nb_seg; i += 2) + xmlXPathFreeCompExpr((xmlXPathCompExprPtr) avt->segments[i]); + } else { + for (i = 0;i < avt->nb_seg; i += 2) + xmlXPathFreeCompExpr((xmlXPathCompExprPtr) avt->segments[i]); + for (i = 1;i < avt->nb_seg; i += 2) + if (avt->segments[i] != NULL) + xmlFree((xmlChar *) avt->segments[i]); + } + if (avt->nsList != NULL) + xmlFree(avt->nsList); + xmlFree(avt); +} + +/** + * xsltFreeAVTList: + * @avt: pointer to an list of AVT structures + * + * Free up the memory associated to the attribute value templates + */ +void +xsltFreeAVTList(void *avt) { + xsltAttrVTPtr cur = (xsltAttrVTPtr) avt, next; + + while (cur != NULL) { + next = cur->next; + xsltFreeAttrVT(cur); + cur = next; + } +} +/** + * xsltSetAttrVTsegment: + * @ avt: pointer to an xsltAttrVT structure + * @ val: the value to be set to the next available segment + * + * Within xsltCompileAttr there are several places where a value + * needs to be added to the 'segments' array within the xsltAttrVT + * structure, and at each place the allocated size may have to be + * re-allocated. This routine takes care of that situation. + * + * Returns the avt pointer, which may have been changed by a re-alloc + */ +static xsltAttrVTPtr +xsltSetAttrVTsegment(xsltAttrVTPtr avt, void *val) { + if (avt->nb_seg >= avt->max_seg) { + avt = (xsltAttrVTPtr) xmlRealloc(avt, sizeof(xsltAttrVT) + + avt->max_seg * sizeof(void *)); + if (avt == NULL) { + return NULL; + } + memset(&avt->segments[avt->nb_seg], 0, MAX_AVT_SEG*sizeof(void *)); + avt->max_seg += MAX_AVT_SEG; + } + avt->segments[avt->nb_seg++] = val; + return avt; +} + +/** + * xsltCompileAttr: + * @style: a XSLT process context + * @attr: the attribute coming from the stylesheet. + * + * Precompile an attribute in a stylesheet, basically it checks if it is + * an attrubute value template, and if yes establish some structures needed + * to process it at transformation time. + */ +void +xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) { + const xmlChar *str; + const xmlChar *cur; + xmlChar *ret = NULL; + xmlChar *expr = NULL; + xsltAttrVTPtr avt; + int i = 0, lastavt = 0; + + if ((style == NULL) || (attr == NULL) || (attr->children == NULL)) + return; + if ((attr->children->type != XML_TEXT_NODE) || + (attr->children->next != NULL)) { + xsltTransformError(NULL, style, attr->parent, + "Attribute '%s': The content is expected to be a single text " + "node when compiling an AVT.\n", attr->name); + style->errors++; + return; + } + str = attr->children->content; + if ((xmlStrchr(str, '{') == NULL) && + (xmlStrchr(str, '}') == NULL)) return; + +#ifdef WITH_XSLT_DEBUG_AVT + xsltGenericDebug(xsltGenericDebugContext, + "Found AVT %s: %s\n", attr->name, str); +#endif + if (attr->psvi != NULL) { +#ifdef WITH_XSLT_DEBUG_AVT + xsltGenericDebug(xsltGenericDebugContext, + "AVT %s: already compiled\n", attr->name); +#endif + return; + } + /* + * Create a new AVT object. + */ + avt = xsltNewAttrVT(style); + if (avt == NULL) + return; + attr->psvi = avt; + + avt->nsList = xmlGetNsList(attr->doc, attr->parent); + if (avt->nsList != NULL) { + while (avt->nsList[i] != NULL) + i++; + } + avt->nsNr = i; + + cur = str; + while (*cur != 0) { + if (*cur == '{') { + if (*(cur+1) == '{') { /* escaped '{' */ + cur++; + ret = xmlStrncat(ret, str, cur - str); + cur++; + str = cur; + continue; + } + if (*(cur+1) == '}') { /* skip empty AVT */ + ret = xmlStrncat(ret, str, cur - str); + cur += 2; + str = cur; + continue; + } + if ((ret != NULL) || (cur - str > 0)) { + ret = xmlStrncat(ret, str, cur - str); + str = cur; + if (avt->nb_seg == 0) + avt->strstart = 1; + if ((avt = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL) + goto error; + ret = NULL; + lastavt = 0; + } + + cur++; + while ((*cur != 0) && (*cur != '}')) { + /* Need to check for literal (bug539741) */ + if ((*cur == '\'') || (*cur == '"')) { + char delim = *(cur++); + while ((*cur != 0) && (*cur != delim)) + cur++; + if (*cur != 0) + cur++; /* skip the ending delimiter */ + } else + cur++; + } + if (*cur == 0) { + xsltTransformError(NULL, style, attr->parent, + "Attribute '%s': The AVT has an unmatched '{'.\n", + attr->name); + style->errors++; + goto error; + } + str++; + expr = xmlStrndup(str, cur - str); + if (expr == NULL) { + /* + * TODO: What needs to be done here? + */ + XSLT_TODO + goto error; + } else { + xmlXPathCompExprPtr comp; + + comp = xsltXPathCompile(style, expr); + if (comp == NULL) { + xsltTransformError(NULL, style, attr->parent, + "Attribute '%s': Failed to compile the expression " + "'%s' in the AVT.\n", attr->name, expr); + style->errors++; + goto error; + } + if (avt->nb_seg == 0) + avt->strstart = 0; + if (lastavt == 1) { + if ((avt = xsltSetAttrVTsegment(avt, NULL)) == NULL) + goto error; + } + if ((avt = xsltSetAttrVTsegment(avt, (void *) comp)) == NULL) + goto error; + lastavt = 1; + xmlFree(expr); + expr = NULL; + } + cur++; + str = cur; + } else if (*cur == '}') { + cur++; + if (*cur == '}') { /* escaped '}' */ + ret = xmlStrncat(ret, str, cur - str); + cur++; + str = cur; + continue; + } else { + xsltTransformError(NULL, style, attr->parent, + "Attribute '%s': The AVT has an unmatched '}'.\n", + attr->name); + goto error; + } + } else + cur++; + } + if ((ret != NULL) || (cur - str > 0)) { + ret = xmlStrncat(ret, str, cur - str); + str = cur; + if (avt->nb_seg == 0) + avt->strstart = 1; + if ((avt = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL) + goto error; + ret = NULL; + } + +error: + if (avt == NULL) { + xsltTransformError(NULL, style, attr->parent, + "xsltCompileAttr: malloc problem\n"); + } else { + if (attr->psvi != avt) { /* may have changed from realloc */ + attr->psvi = avt; + /* + * This is a "hack", but I can't see any clean method of + * doing it. If a re-alloc has taken place, then the pointer + * for this AVT may have changed. style->attVTs was set by + * xsltNewAttrVT, so it needs to be re-set to the new value! + */ + style->attVTs = avt; + } + } + if (ret != NULL) + xmlFree(ret); + if (expr != NULL) + xmlFree(expr); +} + + +/** + * xsltEvalAVT: + * @ctxt: the XSLT transformation context + * @avt: the prevompiled attribute value template info + * @node: the node hosting the attribute + * + * Process the given AVT, and return the new string value. + * + * Returns the computed string value or NULL, must be deallocated by the + * caller. + */ +xmlChar * +xsltEvalAVT(xsltTransformContextPtr ctxt, void *avt, xmlNodePtr node) { + xmlChar *ret = NULL, *tmp; + xmlXPathCompExprPtr comp; + xsltAttrVTPtr cur = (xsltAttrVTPtr) avt; + int i; + int str; + + if ((ctxt == NULL) || (avt == NULL) || (node == NULL)) + return(NULL); + str = cur->strstart; + for (i = 0;i < cur->nb_seg;i++) { + if (str) { + ret = xmlStrcat(ret, (const xmlChar *) cur->segments[i]); + } else { + comp = (xmlXPathCompExprPtr) cur->segments[i]; + tmp = xsltEvalXPathStringNs(ctxt, comp, cur->nsNr, cur->nsList); + if (tmp != NULL) { + if (ret != NULL) { + ret = xmlStrcat(ret, tmp); + xmlFree(tmp); + } else { + ret = tmp; + } + } + } + str = !str; + } + return(ret); +} diff --git a/libxslt/documents.c b/libxslt/documents.c new file mode 100644 index 0000000..3f3a731 --- /dev/null +++ b/libxslt/documents.c @@ -0,0 +1,434 @@ +/* + * documents.c: Implementation of the documents handling + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> + +#include <libxml/xmlmemory.h> +#include <libxml/tree.h> +#include <libxml/hash.h> +#include <libxml/parser.h> +#include <libxml/parserInternals.h> +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "documents.h" +#include "transform.h" +#include "imports.h" +#include "keys.h" +#include "security.h" + +#ifdef LIBXML_XINCLUDE_ENABLED +#include <libxml/xinclude.h> +#endif + +#define WITH_XSLT_DEBUG_DOCUMENTS + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_DOCUMENTS +#endif + +/************************************************************************ + * * + * Hooks for the document loader * + * * + ************************************************************************/ + +/** + * xsltDocDefaultLoaderFunc: + * @URI: the URI of the document to load + * @dict: the dictionary to use when parsing that document + * @options: parsing options, a set of xmlParserOption + * @ctxt: the context, either a stylesheet or a transformation context + * @type: the xsltLoadType indicating the kind of loading required + * + * Default function to load document not provided by the compilation or + * transformation API themselve, for example when an xsl:import, + * xsl:include is found at compilation time or when a document() + * call is made at runtime. + * + * Returns the pointer to the document (which will be modified and + * freed by the engine later), or NULL in case of error. + */ +static xmlDocPtr +xsltDocDefaultLoaderFunc(const xmlChar * URI, xmlDictPtr dict, int options, + void *ctxt ATTRIBUTE_UNUSED, + xsltLoadType type ATTRIBUTE_UNUSED) +{ + xmlParserCtxtPtr pctxt; + xmlParserInputPtr inputStream; + xmlDocPtr doc; + + pctxt = xmlNewParserCtxt(); + if (pctxt == NULL) + return(NULL); + if ((dict != NULL) && (pctxt->dict != NULL)) { + xmlDictFree(pctxt->dict); + pctxt->dict = NULL; + } + if (dict != NULL) { + pctxt->dict = dict; + xmlDictReference(pctxt->dict); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "Reusing dictionary for document\n"); +#endif + } + xmlCtxtUseOptions(pctxt, options); + inputStream = xmlLoadExternalEntity((const char *) URI, NULL, pctxt); + if (inputStream == NULL) { + xmlFreeParserCtxt(pctxt); + return(NULL); + } + inputPush(pctxt, inputStream); + if (pctxt->directory == NULL) + pctxt->directory = xmlParserGetDirectory((const char *) URI); + + xmlParseDocument(pctxt); + + if (pctxt->wellFormed) { + doc = pctxt->myDoc; + } + else { + doc = NULL; + xmlFreeDoc(pctxt->myDoc); + pctxt->myDoc = NULL; + } + xmlFreeParserCtxt(pctxt); + + return(doc); +} + + +xsltDocLoaderFunc xsltDocDefaultLoader = xsltDocDefaultLoaderFunc; + +/** + * xsltSetLoaderFunc: + * @f: the new function to handle document loading. + * + * Set the new function to load document, if NULL it resets it to the + * default function. + */ + +void +xsltSetLoaderFunc(xsltDocLoaderFunc f) { + if (f == NULL) + xsltDocDefaultLoader = xsltDocDefaultLoaderFunc; + else + xsltDocDefaultLoader = f; +} + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +/** + * xsltNewDocument: + * @ctxt: an XSLT transformation context (or NULL) + * @doc: a parsed XML document + * + * Register a new document, apply key computations + * + * Returns a handler to the document + */ +xsltDocumentPtr +xsltNewDocument(xsltTransformContextPtr ctxt, xmlDocPtr doc) { + xsltDocumentPtr cur; + + cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument)); + if (cur == NULL) { + xsltTransformError(ctxt, NULL, (xmlNodePtr) doc, + "xsltNewDocument : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltDocument)); + cur->doc = doc; + if (ctxt != NULL) { + if (! XSLT_IS_RES_TREE_FRAG(doc)) { + cur->next = ctxt->docList; + ctxt->docList = cur; + } + /* + * A key with a specific name for a specific document + * will only be computed if there's a call to the key() + * function using that specific name for that specific + * document. I.e. computation of keys will be done in + * xsltGetKey() (keys.c) on an on-demand basis. + * + * xsltInitCtxtKeys(ctxt, cur); not called here anymore + */ + } + return(cur); +} + +/** + * xsltNewStyleDocument: + * @style: an XSLT style sheet + * @doc: a parsed XML document + * + * Register a new document, apply key computations + * + * Returns a handler to the document + */ +xsltDocumentPtr +xsltNewStyleDocument(xsltStylesheetPtr style, xmlDocPtr doc) { + xsltDocumentPtr cur; + + cur = (xsltDocumentPtr) xmlMalloc(sizeof(xsltDocument)); + if (cur == NULL) { + xsltTransformError(NULL, style, (xmlNodePtr) doc, + "xsltNewStyleDocument : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltDocument)); + cur->doc = doc; + if (style != NULL) { + cur->next = style->docList; + style->docList = cur; + } + return(cur); +} + +/** + * xsltFreeStyleDocuments: + * @style: an XSLT stylesheet (representing a stylesheet-level) + * + * Frees the node-trees (and xsltDocument structures) of all + * stylesheet-modules of the stylesheet-level represented by + * the given @style. + */ +void +xsltFreeStyleDocuments(xsltStylesheetPtr style) { + xsltDocumentPtr doc, cur; +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + xsltNsMapPtr nsMap; +#endif + + if (style == NULL) + return; + +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + if (XSLT_HAS_INTERNAL_NSMAP(style)) + nsMap = XSLT_GET_INTERNAL_NSMAP(style); + else + nsMap = NULL; +#endif + + cur = style->docList; + while (cur != NULL) { + doc = cur; + cur = cur->next; +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + /* + * Restore all changed namespace URIs of ns-decls. + */ + if (nsMap) + xsltRestoreDocumentNamespaces(nsMap, doc->doc); +#endif + xsltFreeDocumentKeys(doc); + if (!doc->main) + xmlFreeDoc(doc->doc); + xmlFree(doc); + } +} + +/** + * xsltFreeDocuments: + * @ctxt: an XSLT transformation context + * + * Free up all the space used by the loaded documents + */ +void +xsltFreeDocuments(xsltTransformContextPtr ctxt) { + xsltDocumentPtr doc, cur; + + cur = ctxt->docList; + while (cur != NULL) { + doc = cur; + cur = cur->next; + xsltFreeDocumentKeys(doc); + if (!doc->main) + xmlFreeDoc(doc->doc); + xmlFree(doc); + } + cur = ctxt->styleList; + while (cur != NULL) { + doc = cur; + cur = cur->next; + xsltFreeDocumentKeys(doc); + if (!doc->main) + xmlFreeDoc(doc->doc); + xmlFree(doc); + } +} + +/** + * xsltLoadDocument: + * @ctxt: an XSLT transformation context + * @URI: the computed URI of the document + * + * Try to load a document (not a stylesheet) + * within the XSLT transformation context + * + * Returns the new xsltDocumentPtr or NULL in case of error + */ +xsltDocumentPtr +xsltLoadDocument(xsltTransformContextPtr ctxt, const xmlChar *URI) { + xsltDocumentPtr ret; + xmlDocPtr doc; + + if ((ctxt == NULL) || (URI == NULL)) + return(NULL); + + /* + * Security framework check + */ + if (ctxt->sec != NULL) { + int res; + + res = xsltCheckRead(ctxt->sec, ctxt, URI); + if (res == 0) { + xsltTransformError(ctxt, NULL, NULL, + "xsltLoadDocument: read rights for %s denied\n", + URI); + return(NULL); + } + } + + /* + * Walk the context list to find the document if preparsed + */ + ret = ctxt->docList; + while (ret != NULL) { + if ((ret->doc != NULL) && (ret->doc->URL != NULL) && + (xmlStrEqual(ret->doc->URL, URI))) + return(ret); + ret = ret->next; + } + + doc = xsltDocDefaultLoader(URI, ctxt->dict, ctxt->parserOptions, + (void *) ctxt, XSLT_LOAD_DOCUMENT); + + if (doc == NULL) + return(NULL); + + if (ctxt->xinclude != 0) { +#ifdef LIBXML_XINCLUDE_ENABLED +#if LIBXML_VERSION >= 20603 + xmlXIncludeProcessFlags(doc, ctxt->parserOptions); +#else + xmlXIncludeProcess(doc); +#endif +#else + xsltTransformError(ctxt, NULL, NULL, + "xsltLoadDocument(%s) : XInclude processing not compiled in\n", + URI); +#endif + } + /* + * Apply white-space stripping if asked for + */ + if (xsltNeedElemSpaceHandling(ctxt)) + xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc)); + if (ctxt->debugStatus == XSLT_DEBUG_NONE) + xmlXPathOrderDocElems(doc); + + ret = xsltNewDocument(ctxt, doc); + return(ret); +} + +/** + * xsltLoadStyleDocument: + * @style: an XSLT style sheet + * @URI: the computed URI of the document + * + * Try to load a stylesheet document within the XSLT transformation context + * + * Returns the new xsltDocumentPtr or NULL in case of error + */ +xsltDocumentPtr +xsltLoadStyleDocument(xsltStylesheetPtr style, const xmlChar *URI) { + xsltDocumentPtr ret; + xmlDocPtr doc; + xsltSecurityPrefsPtr sec; + + if ((style == NULL) || (URI == NULL)) + return(NULL); + + /* + * Security framework check + */ + sec = xsltGetDefaultSecurityPrefs(); + if (sec != NULL) { + int res; + + res = xsltCheckRead(sec, NULL, URI); + if (res == 0) { + xsltTransformError(NULL, NULL, NULL, + "xsltLoadStyleDocument: read rights for %s denied\n", + URI); + return(NULL); + } + } + + /* + * Walk the context list to find the document if preparsed + */ + ret = style->docList; + while (ret != NULL) { + if ((ret->doc != NULL) && (ret->doc->URL != NULL) && + (xmlStrEqual(ret->doc->URL, URI))) + return(ret); + ret = ret->next; + } + + doc = xsltDocDefaultLoader(URI, style->dict, XSLT_PARSE_OPTIONS, + (void *) style, XSLT_LOAD_STYLESHEET); + if (doc == NULL) + return(NULL); + + ret = xsltNewStyleDocument(style, doc); + return(ret); +} + +/** + * xsltFindDocument: + * @ctxt: an XSLT transformation context + * @doc: a parsed XML document + * + * Try to find a document within the XSLT transformation context. + * This will not find document infos for temporary + * Result Tree Fragments. + * + * Returns the desired xsltDocumentPtr or NULL in case of error + */ +xsltDocumentPtr +xsltFindDocument (xsltTransformContextPtr ctxt, xmlDocPtr doc) { + xsltDocumentPtr ret; + + if ((ctxt == NULL) || (doc == NULL)) + return(NULL); + + /* + * Walk the context list to find the document + */ + ret = ctxt->docList; + while (ret != NULL) { + if (ret->doc == doc) + return(ret); + ret = ret->next; + } + if (doc == ctxt->style->doc) + return(ctxt->document); + return(NULL); +} + diff --git a/libxslt/documents.h b/libxslt/documents.h new file mode 100644 index 0000000..ae7c0ca --- /dev/null +++ b/libxslt/documents.h @@ -0,0 +1,93 @@ +/* + * Summary: interface for the document handling + * Description: implements document loading and cache (multiple + * document() reference for the same resources must + * be equal. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_DOCUMENTS_H__ +#define __XML_XSLT_DOCUMENTS_H__ + +#include <libxml/tree.h> +#include "xsltexports.h" +#include "xsltInternals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +XSLTPUBFUN xsltDocumentPtr XSLTCALL + xsltNewDocument (xsltTransformContextPtr ctxt, + xmlDocPtr doc); +XSLTPUBFUN xsltDocumentPtr XSLTCALL + xsltLoadDocument (xsltTransformContextPtr ctxt, + const xmlChar *URI); +XSLTPUBFUN xsltDocumentPtr XSLTCALL + xsltFindDocument (xsltTransformContextPtr ctxt, + xmlDocPtr doc); +XSLTPUBFUN void XSLTCALL + xsltFreeDocuments (xsltTransformContextPtr ctxt); + +XSLTPUBFUN xsltDocumentPtr XSLTCALL + xsltLoadStyleDocument (xsltStylesheetPtr style, + const xmlChar *URI); +XSLTPUBFUN xsltDocumentPtr XSLTCALL + xsltNewStyleDocument (xsltStylesheetPtr style, + xmlDocPtr doc); +XSLTPUBFUN void XSLTCALL + xsltFreeStyleDocuments (xsltStylesheetPtr style); + +/* + * Hooks for document loading + */ + +/** + * xsltLoadType: + * + * Enum defining the kind of loader requirement. + */ +typedef enum { + XSLT_LOAD_START = 0, /* loading for a top stylesheet */ + XSLT_LOAD_STYLESHEET = 1, /* loading for a stylesheet include/import */ + XSLT_LOAD_DOCUMENT = 2 /* loading document at transformation time */ +} xsltLoadType; + +/** + * xsltDocLoaderFunc: + * @URI: the URI of the document to load + * @dict: the dictionary to use when parsing that document + * @options: parsing options, a set of xmlParserOption + * @ctxt: the context, either a stylesheet or a transformation context + * @type: the xsltLoadType indicating the kind of loading required + * + * An xsltDocLoaderFunc is a signature for a function which can be + * registered to load document not provided by the compilation or + * transformation API themselve, for example when an xsl:import, + * xsl:include is found at compilation time or when a document() + * call is made at runtime. + * + * Returns the pointer to the document (which will be modified and + * freed by the engine later), or NULL in case of error. + */ +typedef xmlDocPtr (*xsltDocLoaderFunc) (const xmlChar *URI, + xmlDictPtr dict, + int options, + void *ctxt, + xsltLoadType type); + +XSLTPUBFUN void XSLTCALL + xsltSetLoaderFunc (xsltDocLoaderFunc f); + +/* the loader may be needed by extension libraries so it is exported */ +XSLTPUBVAR xsltDocLoaderFunc xsltDocDefaultLoader; + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_DOCUMENTS_H__ */ + diff --git a/libxslt/extensions.c b/libxslt/extensions.c new file mode 100644 index 0000000..ae6eef0 --- /dev/null +++ b/libxslt/extensions.c @@ -0,0 +1,2365 @@ +/* + * extensions.c: Implemetation of the extensions support + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> +#include <limits.h> + +#include <libxml/xmlmemory.h> +#include <libxml/tree.h> +#include <libxml/hash.h> +#include <libxml/xmlerror.h> +#include <libxml/parserInternals.h> +#include <libxml/xpathInternals.h> +#ifdef WITH_MODULES +#include <libxml/xmlmodule.h> +#endif +#include <libxml/list.h> +#include <libxml/xmlIO.h> +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "imports.h" +#include "extensions.h" + +#ifdef _WIN32 +#include <stdlib.h> /* for _MAX_PATH */ +#ifndef PATH_MAX +#define PATH_MAX _MAX_PATH +#endif +#endif + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_EXTENSIONS +#endif + +/************************************************************************ + * * + * Private Types and Globals * + * * + ************************************************************************/ + +typedef struct _xsltExtDef xsltExtDef; +typedef xsltExtDef *xsltExtDefPtr; +struct _xsltExtDef { + struct _xsltExtDef *next; + xmlChar *prefix; + xmlChar *URI; + void *data; +}; + +typedef struct _xsltExtModule xsltExtModule; +typedef xsltExtModule *xsltExtModulePtr; +struct _xsltExtModule { + xsltExtInitFunction initFunc; + xsltExtShutdownFunction shutdownFunc; + xsltStyleExtInitFunction styleInitFunc; + xsltStyleExtShutdownFunction styleShutdownFunc; +}; + +typedef struct _xsltExtData xsltExtData; +typedef xsltExtData *xsltExtDataPtr; +struct _xsltExtData { + xsltExtModulePtr extModule; + void *extData; +}; + +typedef struct _xsltExtElement xsltExtElement; +typedef xsltExtElement *xsltExtElementPtr; +struct _xsltExtElement { + xsltPreComputeFunction precomp; + xsltTransformFunction transform; +}; + +static xmlHashTablePtr xsltExtensionsHash = NULL; +static xmlHashTablePtr xsltFunctionsHash = NULL; +static xmlHashTablePtr xsltElementsHash = NULL; +static xmlHashTablePtr xsltTopLevelsHash = NULL; +static xmlHashTablePtr xsltModuleHash = NULL; +static xmlMutexPtr xsltExtMutex = NULL; + +/************************************************************************ + * * + * Type functions * + * * + ************************************************************************/ + +/** + * xsltNewExtDef: + * @prefix: the extension prefix + * @URI: the namespace URI + * + * Create a new XSLT ExtDef + * + * Returns the newly allocated xsltExtDefPtr or NULL in case of error + */ +static xsltExtDefPtr +xsltNewExtDef(const xmlChar * prefix, const xmlChar * URI) +{ + xsltExtDefPtr cur; + + cur = (xsltExtDefPtr) xmlMalloc(sizeof(xsltExtDef)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewExtDef : malloc failed\n"); + return (NULL); + } + memset(cur, 0, sizeof(xsltExtDef)); + if (prefix != NULL) + cur->prefix = xmlStrdup(prefix); + if (URI != NULL) + cur->URI = xmlStrdup(URI); + return (cur); +} + +/** + * xsltFreeExtDef: + * @extensiond: an XSLT extension definition + * + * Free up the memory allocated by @extensiond + */ +static void +xsltFreeExtDef(xsltExtDefPtr extensiond) +{ + if (extensiond == NULL) + return; + if (extensiond->prefix != NULL) + xmlFree(extensiond->prefix); + if (extensiond->URI != NULL) + xmlFree(extensiond->URI); + xmlFree(extensiond); +} + +/** + * xsltFreeExtDefList: + * @extensiond: an XSLT extension definition list + * + * Free up the memory allocated by all the elements of @extensiond + */ +static void +xsltFreeExtDefList(xsltExtDefPtr extensiond) +{ + xsltExtDefPtr cur; + + while (extensiond != NULL) { + cur = extensiond; + extensiond = extensiond->next; + xsltFreeExtDef(cur); + } +} + +/** + * xsltNewExtModule: + * @initFunc: the module initialization function + * @shutdownFunc: the module shutdown function + * @styleInitFunc: the stylesheet module data allocator function + * @styleShutdownFunc: the stylesheet module data free function + * + * Create a new XSLT extension module + * + * Returns the newly allocated xsltExtModulePtr or NULL in case of error + */ +static xsltExtModulePtr +xsltNewExtModule(xsltExtInitFunction initFunc, + xsltExtShutdownFunction shutdownFunc, + xsltStyleExtInitFunction styleInitFunc, + xsltStyleExtShutdownFunction styleShutdownFunc) +{ + xsltExtModulePtr cur; + + cur = (xsltExtModulePtr) xmlMalloc(sizeof(xsltExtModule)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewExtModule : malloc failed\n"); + return (NULL); + } + cur->initFunc = initFunc; + cur->shutdownFunc = shutdownFunc; + cur->styleInitFunc = styleInitFunc; + cur->styleShutdownFunc = styleShutdownFunc; + return (cur); +} + +/** + * xsltFreeExtModule: + * @ext: an XSLT extension module + * + * Free up the memory allocated by @ext + */ +static void +xsltFreeExtModule(xsltExtModulePtr ext) +{ + if (ext == NULL) + return; + xmlFree(ext); +} + +/** + * xsltNewExtData: + * @extModule: the module + * @extData: the associated data + * + * Create a new XSLT extension module data wrapper + * + * Returns the newly allocated xsltExtDataPtr or NULL in case of error + */ +static xsltExtDataPtr +xsltNewExtData(xsltExtModulePtr extModule, void *extData) +{ + xsltExtDataPtr cur; + + if (extModule == NULL) + return (NULL); + cur = (xsltExtDataPtr) xmlMalloc(sizeof(xsltExtData)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewExtData : malloc failed\n"); + return (NULL); + } + cur->extModule = extModule; + cur->extData = extData; + return (cur); +} + +/** + * xsltFreeExtData: + * @ext: an XSLT extension module data wrapper + * + * Free up the memory allocated by @ext + */ +static void +xsltFreeExtData(xsltExtDataPtr ext) +{ + if (ext == NULL) + return; + xmlFree(ext); +} + +/** + * xsltNewExtElement: + * @precomp: the pre-computation function + * @transform: the transformation function + * + * Create a new XSLT extension element + * + * Returns the newly allocated xsltExtElementPtr or NULL in case of + * error + */ +static xsltExtElementPtr +xsltNewExtElement(xsltPreComputeFunction precomp, + xsltTransformFunction transform) +{ + xsltExtElementPtr cur; + + if (transform == NULL) + return (NULL); + + cur = (xsltExtElementPtr) xmlMalloc(sizeof(xsltExtElement)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewExtElement : malloc failed\n"); + return (NULL); + } + cur->precomp = precomp; + cur->transform = transform; + return (cur); +} + +/** + * xsltFreeExtElement: + * @ext: an XSLT extension element + * + * Frees up the memory allocated by @ext + */ +static void +xsltFreeExtElement(xsltExtElementPtr ext) +{ + if (ext == NULL) + return; + xmlFree(ext); +} + + +#ifdef WITH_MODULES +typedef void (*exsltRegisterFunction) (void); + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +/** + * xsltExtModuleRegisterDynamic: + * @URI: the function or element namespace URI + * + * Dynamically loads an extension plugin when available. + * + * The plugin name is derived from the URI by removing the + * initial protocol designation, e.g. "http://", then converting + * the characters ".", "-", "/", and "\" into "_", the removing + * any trailing "/", then concatenating LIBXML_MODULE_EXTENSION. + * + * Plugins are loaded from the directory specified by the + * environment variable LIBXSLT_PLUGINS_PATH, or if NULL, + * by LIBXSLT_DEFAULT_PLUGINS_PATH() which is determined at + * compile time. + * + * Returns 0 if successful, -1 in case of error. + */ + +static int +xsltExtModuleRegisterDynamic(const xmlChar * URI) +{ + + xmlModulePtr m; + exsltRegisterFunction regfunc; + xmlChar *ext_name; + char module_filename[PATH_MAX]; + const xmlChar *ext_directory = NULL; + const xmlChar *protocol = NULL; + xmlChar *i, *regfunc_name; + void *vregfunc; + int rc; + + /* check for bad inputs */ + if (URI == NULL) + return (-1); + + if (NULL == xsltModuleHash) { + xsltModuleHash = xmlHashCreate(5); + if (xsltModuleHash == NULL) + return (-1); + } + + xmlMutexLock(xsltExtMutex); + + /* have we attempted to register this module already? */ + if (xmlHashLookup(xsltModuleHash, URI) != NULL) { + xmlMutexUnlock(xsltExtMutex); + return (-1); + } + xmlMutexUnlock(xsltExtMutex); + + /* transform extension namespace into a module name */ + protocol = xmlStrstr(URI, BAD_CAST "://"); + if (protocol == NULL) { + ext_name = xmlStrdup(URI); + } else { + ext_name = xmlStrdup(protocol + 3); + } + if (ext_name == NULL) { + return (-1); + } + + i = ext_name; + while ('\0' != *i) { + if (('/' == *i) || ('\\' == *i) || ('.' == *i) || ('-' == *i)) + *i = '_'; + i++; + } + + /* Strip underscores from end of string. */ + while (i > ext_name && *(i - 1) == '_') { + i--; + *i = '\0'; + } + + /* determine module directory */ + ext_directory = (xmlChar *) getenv("LIBXSLT_PLUGINS_PATH"); + + if (NULL == ext_directory) { + ext_directory = BAD_CAST LIBXSLT_DEFAULT_PLUGINS_PATH(); + if (NULL == ext_directory) + return (-1); + } +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + else + xsltGenericDebug(xsltGenericDebugContext, + "LIBXSLT_PLUGINS_PATH is %s\n", ext_directory); +#endif + + /* build the module filename, and confirm the module exists */ + xmlStrPrintf((xmlChar *) module_filename, sizeof(module_filename), + BAD_CAST "%s/%s%s", + ext_directory, ext_name, LIBXML_MODULE_EXTENSION); + +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Attempting to load plugin: %s for URI: %s\n", + module_filename, URI); +#endif + + if (1 != xmlCheckFilename(module_filename)) { + +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "xmlCheckFilename failed for plugin: %s\n", module_filename); +#endif + + xmlFree(ext_name); + return (-1); + } + + /* attempt to open the module */ + m = xmlModuleOpen(module_filename, 0); + if (NULL == m) { + +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "xmlModuleOpen failed for plugin: %s\n", module_filename); +#endif + + xmlFree(ext_name); + return (-1); + } + + /* construct initialization func name */ + regfunc_name = xmlStrdup(ext_name); + regfunc_name = xmlStrcat(regfunc_name, BAD_CAST "_init"); + + vregfunc = NULL; + rc = xmlModuleSymbol(m, (const char *) regfunc_name, &vregfunc); + regfunc = vregfunc; + if (0 == rc) { + /* + * Call the module's init function. Note that this function + * calls xsltRegisterExtModuleFull which will add the module + * to xsltExtensionsHash (together with it's entry points). + */ + (*regfunc) (); + + /* register this module in our hash */ + xmlMutexLock(xsltExtMutex); + xmlHashAddEntry(xsltModuleHash, URI, (void *) m); + xmlMutexUnlock(xsltExtMutex); + } else { + +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "xmlModuleSymbol failed for plugin: %s, regfunc: %s\n", + module_filename, regfunc_name); +#endif + + /* if regfunc not found unload the module immediately */ + xmlModuleClose(m); + } + + xmlFree(ext_name); + xmlFree(regfunc_name); + return (NULL == regfunc) ? -1 : 0; +} +#else +static int +xsltExtModuleRegisterDynamic(const xmlChar * URI ATTRIBUTE_UNUSED) +{ + return -1; +} +#endif + +/************************************************************************ + * * + * The stylesheet extension prefixes handling * + * * + ************************************************************************/ + + +/** + * xsltFreeExts: + * @style: an XSLT stylesheet + * + * Free up the memory used by XSLT extensions in a stylesheet + */ +void +xsltFreeExts(xsltStylesheetPtr style) +{ + if (style->nsDefs != NULL) + xsltFreeExtDefList((xsltExtDefPtr) style->nsDefs); +} + +/** + * xsltRegisterExtPrefix: + * @style: an XSLT stylesheet + * @prefix: the prefix used (optional) + * @URI: the URI associated to the extension + * + * Registers an extension namespace + * This is called from xslt.c during compile-time. + * The given prefix is not needed. + * Called by: + * xsltParseExtElemPrefixes() (new function) + * xsltRegisterExtPrefix() (old function) + * + * Returns 0 in case of success, 1 if the @URI was already + * registered as an extension namespace and + * -1 in case of failure + */ +int +xsltRegisterExtPrefix(xsltStylesheetPtr style, + const xmlChar * prefix, const xmlChar * URI) +{ + xsltExtDefPtr def, ret; + + if ((style == NULL) || (URI == NULL)) + return (-1); + +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Registering extension namespace '%s'.\n", URI); +#endif + def = (xsltExtDefPtr) style->nsDefs; +#ifdef XSLT_REFACTORED + /* + * The extension is associated with a namespace name. + */ + while (def != NULL) { + if (xmlStrEqual(URI, def->URI)) + return (1); + def = def->next; + } +#else + while (def != NULL) { + if (xmlStrEqual(prefix, def->prefix)) + return (-1); + def = def->next; + } +#endif + ret = xsltNewExtDef(prefix, URI); + if (ret == NULL) + return (-1); + ret->next = (xsltExtDefPtr) style->nsDefs; + style->nsDefs = ret; + + /* + * check whether there is an extension module with a stylesheet + * initialization function. + */ +#ifdef XSLT_REFACTORED + /* + * Don't initialize modules based on specified namespaces via + * the attribute "[xsl:]extension-element-prefixes". + */ +#else + if (xsltExtensionsHash != NULL) { + xsltExtModulePtr module; + + xmlMutexLock(xsltExtMutex); + module = xmlHashLookup(xsltExtensionsHash, URI); + xmlMutexUnlock(xsltExtMutex); + if (NULL == module) { + if (!xsltExtModuleRegisterDynamic(URI)) { + xmlMutexLock(xsltExtMutex); + module = xmlHashLookup(xsltExtensionsHash, URI); + xmlMutexUnlock(xsltExtMutex); + } + } + if (module != NULL) { + xsltStyleGetExtData(style, URI); + } + } +#endif + return (0); +} + +/************************************************************************ + * * + * The extensions modules interfaces * + * * + ************************************************************************/ + +/** + * xsltRegisterExtFunction: + * @ctxt: an XSLT transformation context + * @name: the name of the element + * @URI: the URI associated to the element + * @function: the actual implementation which should be called + * + * Registers an extension function + * + * Returns 0 in case of success, -1 in case of failure + */ +int +xsltRegisterExtFunction(xsltTransformContextPtr ctxt, const xmlChar * name, + const xmlChar * URI, xmlXPathFunction function) +{ + int ret; + + if ((ctxt == NULL) || (name == NULL) || + (URI == NULL) || (function == NULL)) + return (-1); + if (ctxt->xpathCtxt != NULL) { + xmlXPathRegisterFuncNS(ctxt->xpathCtxt, name, URI, function); + } + if (ctxt->extFunctions == NULL) + ctxt->extFunctions = xmlHashCreate(10); + if (ctxt->extFunctions == NULL) + return (-1); + + ret = xmlHashAddEntry2(ctxt->extFunctions, name, URI, + XML_CAST_FPTR(function)); + + return(ret); +} + +/** + * xsltRegisterExtElement: + * @ctxt: an XSLT transformation context + * @name: the name of the element + * @URI: the URI associated to the element + * @function: the actual implementation which should be called + * + * Registers an extension element + * + * Returns 0 in case of success, -1 in case of failure + */ +int +xsltRegisterExtElement(xsltTransformContextPtr ctxt, const xmlChar * name, + const xmlChar * URI, xsltTransformFunction function) +{ + if ((ctxt == NULL) || (name == NULL) || + (URI == NULL) || (function == NULL)) + return (-1); + if (ctxt->extElements == NULL) + ctxt->extElements = xmlHashCreate(10); + if (ctxt->extElements == NULL) + return (-1); + return (xmlHashAddEntry2 + (ctxt->extElements, name, URI, XML_CAST_FPTR(function))); +} + +/** + * xsltFreeCtxtExts: + * @ctxt: an XSLT transformation context + * + * Free the XSLT extension data + */ +void +xsltFreeCtxtExts(xsltTransformContextPtr ctxt) +{ + if (ctxt->extElements != NULL) + xmlHashFree(ctxt->extElements, NULL); + if (ctxt->extFunctions != NULL) + xmlHashFree(ctxt->extFunctions, NULL); +} + +/** + * xsltStyleGetStylesheetExtData: + * @style: an XSLT stylesheet + * @URI: the URI associated to the exension module + * + * Fires the compile-time initialization callback + * of an extension module and returns a container + * holding the user-data (retrieved via the callback). + * + * Returns the create module-data container + * or NULL if such a module was not registered. + */ +static xsltExtDataPtr +xsltStyleInitializeStylesheetModule(xsltStylesheetPtr style, + const xmlChar * URI) +{ + xsltExtDataPtr dataContainer; + void *userData = NULL; + xsltExtModulePtr module; + + if ((style == NULL) || (URI == NULL)) + return(NULL); + + if (xsltExtensionsHash == NULL) { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Not registered extension module: %s\n", URI); +#endif + return(NULL); + } + + xmlMutexLock(xsltExtMutex); + + module = xmlHashLookup(xsltExtensionsHash, URI); + + xmlMutexUnlock(xsltExtMutex); + + if (module == NULL) { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Not registered extension module: %s\n", URI); +#endif + return (NULL); + } + /* + * The specified module was registered so initialize it. + */ + if (style->extInfos == NULL) { + style->extInfos = xmlHashCreate(10); + if (style->extInfos == NULL) + return (NULL); + } + /* + * Fire the initialization callback if available. + */ + if (module->styleInitFunc == NULL) { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Initializing module with *no* callback: %s\n", URI); +#endif + } else { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Initializing module with callback: %s\n", URI); +#endif + /* + * Fire the initialization callback. + */ + userData = module->styleInitFunc(style, URI); + } + /* + * Store the user-data in the context of the given stylesheet. + */ + dataContainer = xsltNewExtData(module, userData); + if (dataContainer == NULL) + return (NULL); + + if (xmlHashAddEntry(style->extInfos, URI, + (void *) dataContainer) < 0) + { + xsltTransformError(NULL, style, NULL, + "Failed to register module '%s'.\n", URI); + style->errors++; + if (module->styleShutdownFunc) + module->styleShutdownFunc(style, URI, userData); + xsltFreeExtData(dataContainer); + return (NULL); + } + + return(dataContainer); +} + +/** + * xsltStyleGetExtData: + * @style: an XSLT stylesheet + * @URI: the URI associated to the exension module + * + * Retrieve the data associated to the extension module + * in this given stylesheet. + * Called by: + * xsltRegisterExtPrefix(), + * ( xsltExtElementPreCompTest(), xsltExtInitTest ) + * + * Returns the pointer or NULL if not present + */ +void * +xsltStyleGetExtData(xsltStylesheetPtr style, const xmlChar * URI) +{ + xsltExtDataPtr dataContainer = NULL; + xsltStylesheetPtr tmpStyle; + + if ((style == NULL) || (URI == NULL) || + (xsltExtensionsHash == NULL)) + return (NULL); + + +#ifdef XSLT_REFACTORED + /* + * This is intended for global storage, so only the main + * stylesheet will hold the data. + */ + tmpStyle = style; + while (tmpStyle->parent != NULL) + tmpStyle = tmpStyle->parent; + if (tmpStyle->extInfos != NULL) { + dataContainer = + (xsltExtDataPtr) xmlHashLookup(tmpStyle->extInfos, URI); + if (dataContainer != NULL) { + /* + * The module was already initialized in the context + * of this stylesheet; just return the user-data that + * comes with it. + */ + return(dataContainer->extData); + } + } +#else + /* + * Old behaviour. + */ + tmpStyle = style; + while (tmpStyle != NULL) { + if (tmpStyle->extInfos != NULL) { + dataContainer = + (xsltExtDataPtr) xmlHashLookup(tmpStyle->extInfos, URI); + if (dataContainer != NULL) { + return(dataContainer->extData); + } + } + tmpStyle = xsltNextImport(tmpStyle); + } + tmpStyle = style; +#endif + + dataContainer = + xsltStyleInitializeStylesheetModule(tmpStyle, URI); + if (dataContainer != NULL) + return (dataContainer->extData); + return(NULL); +} + +#ifdef XSLT_REFACTORED +/** + * xsltStyleStylesheetLevelGetExtData: + * @style: an XSLT stylesheet + * @URI: the URI associated to the exension module + * + * Retrieve the data associated to the extension module in this given + * stylesheet. + * + * Returns the pointer or NULL if not present + */ +void * +xsltStyleStylesheetLevelGetExtData(xsltStylesheetPtr style, + const xmlChar * URI) +{ + xsltExtDataPtr dataContainer = NULL; + + if ((style == NULL) || (URI == NULL) || + (xsltExtensionsHash == NULL)) + return (NULL); + + if (style->extInfos != NULL) { + dataContainer = (xsltExtDataPtr) xmlHashLookup(style->extInfos, URI); + /* + * The module was already initialized in the context + * of this stylesheet; just return the user-data that + * comes with it. + */ + if (dataContainer) + return(dataContainer->extData); + } + + dataContainer = + xsltStyleInitializeStylesheetModule(style, URI); + if (dataContainer != NULL) + return (dataContainer->extData); + return(NULL); +} +#endif + +/** + * xsltGetExtData: + * @ctxt: an XSLT transformation context + * @URI: the URI associated to the exension module + * + * Retrieve the data associated to the extension module in this given + * transformation. + * + * Returns the pointer or NULL if not present + */ +void * +xsltGetExtData(xsltTransformContextPtr ctxt, const xmlChar * URI) +{ + xsltExtDataPtr data; + + if ((ctxt == NULL) || (URI == NULL)) + return (NULL); + if (ctxt->extInfos == NULL) { + ctxt->extInfos = xmlHashCreate(10); + if (ctxt->extInfos == NULL) + return (NULL); + data = NULL; + } else { + data = (xsltExtDataPtr) xmlHashLookup(ctxt->extInfos, URI); + } + if (data == NULL) { + void *extData; + xsltExtModulePtr module; + + xmlMutexLock(xsltExtMutex); + + module = xmlHashLookup(xsltExtensionsHash, URI); + + xmlMutexUnlock(xsltExtMutex); + + if (module == NULL) { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Not registered extension module: %s\n", URI); +#endif + return (NULL); + } else { + if (module->initFunc == NULL) + return (NULL); + +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Initializing module: %s\n", URI); +#endif + + extData = module->initFunc(ctxt, URI); + if (extData == NULL) + return (NULL); + + data = xsltNewExtData(module, extData); + if (data == NULL) + return (NULL); + if (xmlHashAddEntry(ctxt->extInfos, URI, (void *) data) < 0) { + xsltTransformError(ctxt, NULL, NULL, + "Failed to register module data: %s\n", + URI); + if (module->shutdownFunc) + module->shutdownFunc(ctxt, URI, extData); + xsltFreeExtData(data); + return (NULL); + } + } + } + return (data->extData); +} + +typedef struct _xsltInitExtCtxt xsltInitExtCtxt; +struct _xsltInitExtCtxt { + xsltTransformContextPtr ctxt; + int ret; +}; + +/** + * xsltInitCtxtExt: + * @styleData: the registered stylesheet data for the module + * @ctxt: the XSLT transformation context + the return value + * @URI: the extension URI + * + * Initializes an extension module + */ +static void +xsltInitCtxtExt(xsltExtDataPtr styleData, xsltInitExtCtxt * ctxt, + const xmlChar * URI) +{ + xsltExtModulePtr module; + xsltExtDataPtr ctxtData; + void *extData; + + if ((styleData == NULL) || (ctxt == NULL) || (URI == NULL) || + (ctxt->ret == -1)) { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "xsltInitCtxtExt: NULL param or error\n"); +#endif + return; + } + module = styleData->extModule; + if ((module == NULL) || (module->initFunc == NULL)) { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "xsltInitCtxtExt: no module or no initFunc\n"); +#endif + return; + } + + ctxtData = (xsltExtDataPtr) xmlHashLookup(ctxt->ctxt->extInfos, URI); + if (ctxtData != NULL) { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "xsltInitCtxtExt: already initialized\n"); +#endif + return; + } + + extData = module->initFunc(ctxt->ctxt, URI); + if (extData == NULL) { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "xsltInitCtxtExt: no extData\n"); +#endif + } + ctxtData = xsltNewExtData(module, extData); + if (ctxtData == NULL) { + ctxt->ret = -1; + return; + } + + if (ctxt->ctxt->extInfos == NULL) + ctxt->ctxt->extInfos = xmlHashCreate(10); + if (ctxt->ctxt->extInfos == NULL) { + ctxt->ret = -1; + return; + } + + if (xmlHashAddEntry(ctxt->ctxt->extInfos, URI, ctxtData) < 0) { + xsltGenericError(xsltGenericErrorContext, + "Failed to register module data: %s\n", URI); + if (module->shutdownFunc) + module->shutdownFunc(ctxt->ctxt, URI, extData); + xsltFreeExtData(ctxtData); + ctxt->ret = -1; + return; + } +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, "Registered module %s\n", + URI); +#endif + ctxt->ret++; +} + +/** + * xsltInitCtxtExts: + * @ctxt: an XSLT transformation context + * + * Initialize the set of modules with registered stylesheet data + * + * Returns the number of modules initialized or -1 in case of error + */ +int +xsltInitCtxtExts(xsltTransformContextPtr ctxt) +{ + xsltStylesheetPtr style; + xsltInitExtCtxt ctx; + + if (ctxt == NULL) + return (-1); + + style = ctxt->style; + if (style == NULL) + return (-1); + + ctx.ctxt = ctxt; + ctx.ret = 0; + + while (style != NULL) { + if (style->extInfos != NULL) { + xmlHashScan(style->extInfos, + (xmlHashScanner) xsltInitCtxtExt, &ctx); + if (ctx.ret == -1) + return (-1); + } + style = xsltNextImport(style); + } +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, "Registered %d modules\n", + ctx.ret); +#endif + return (ctx.ret); +} + +/** + * xsltShutdownCtxtExt: + * @data: the registered data for the module + * @ctxt: the XSLT transformation context + * @URI: the extension URI + * + * Shutdown an extension module loaded + */ +static void +xsltShutdownCtxtExt(xsltExtDataPtr data, xsltTransformContextPtr ctxt, + const xmlChar * URI) +{ + xsltExtModulePtr module; + + if ((data == NULL) || (ctxt == NULL) || (URI == NULL)) + return; + module = data->extModule; + if ((module == NULL) || (module->shutdownFunc == NULL)) + return; + +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Shutting down module : %s\n", URI); +#endif + module->shutdownFunc(ctxt, URI, data->extData); +} + +/** + * xsltShutdownCtxtExts: + * @ctxt: an XSLT transformation context + * + * Shutdown the set of modules loaded + */ +void +xsltShutdownCtxtExts(xsltTransformContextPtr ctxt) +{ + if (ctxt == NULL) + return; + if (ctxt->extInfos == NULL) + return; + xmlHashScan(ctxt->extInfos, (xmlHashScanner) xsltShutdownCtxtExt, + ctxt); + xmlHashFree(ctxt->extInfos, (xmlHashDeallocator) xsltFreeExtData); + ctxt->extInfos = NULL; +} + +/** + * xsltShutdownExt: + * @data: the registered data for the module + * @ctxt: the XSLT stylesheet + * @URI: the extension URI + * + * Shutdown an extension module loaded + */ +static void +xsltShutdownExt(xsltExtDataPtr data, xsltStylesheetPtr style, + const xmlChar * URI) +{ + xsltExtModulePtr module; + + if ((data == NULL) || (style == NULL) || (URI == NULL)) + return; + module = data->extModule; + if ((module == NULL) || (module->styleShutdownFunc == NULL)) + return; + +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Shutting down module : %s\n", URI); +#endif + module->styleShutdownFunc(style, URI, data->extData); + /* + * Don't remove the entry from the hash table here, since + * this will produce segfaults - this fixes bug #340624. + * + * xmlHashRemoveEntry(style->extInfos, URI, + * (xmlHashDeallocator) xsltFreeExtData); + */ +} + +/** + * xsltShutdownExts: + * @style: an XSLT stylesheet + * + * Shutdown the set of modules loaded + */ +void +xsltShutdownExts(xsltStylesheetPtr style) +{ + if (style == NULL) + return; + if (style->extInfos == NULL) + return; + xmlHashScan(style->extInfos, (xmlHashScanner) xsltShutdownExt, style); + xmlHashFree(style->extInfos, (xmlHashDeallocator) xsltFreeExtData); + style->extInfos = NULL; +} + +/** + * xsltCheckExtPrefix: + * @style: the stylesheet + * @URI: the namespace prefix (possibly NULL) + * + * Check if the given prefix is one of the declared extensions. + * This is intended to be called only at compile-time. + * Called by: + * xsltGetInheritedNsList() (xslt.c) + * xsltParseTemplateContent (xslt.c) + * + * Returns 1 if this is an extension, 0 otherwise + */ +int +xsltCheckExtPrefix(xsltStylesheetPtr style, const xmlChar * URI) +{ +#ifdef XSLT_REFACTORED + if ((style == NULL) || (style->compCtxt == NULL) || + (XSLT_CCTXT(style)->inode == NULL) || + (XSLT_CCTXT(style)->inode->extElemNs == NULL)) + return (0); + /* + * Lookup the extension namespaces registered + * at the current node in the stylesheet's tree. + */ + if (XSLT_CCTXT(style)->inode->extElemNs != NULL) { + int i; + xsltPointerListPtr list = XSLT_CCTXT(style)->inode->extElemNs; + + for (i = 0; i < list->number; i++) { + if (xmlStrEqual((const xmlChar *) list->items[i], + URI)) + { + return(1); + } + } + } +#else + xsltExtDefPtr cur; + + if ((style == NULL) || (style->nsDefs == NULL)) + return (0); + if (URI == NULL) + URI = BAD_CAST "#default"; + cur = (xsltExtDefPtr) style->nsDefs; + while (cur != NULL) { + /* + * NOTE: This was change to work on namespace names rather + * than namespace prefixes. This fixes bug #339583. + * TODO: Consider renaming the field "prefix" of xsltExtDef + * to "href". + */ + if (xmlStrEqual(URI, cur->prefix)) + return (1); + cur = cur->next; + } +#endif + return (0); +} + +/** + * xsltCheckExtURI: + * @style: the stylesheet + * @URI: the namespace URI (possibly NULL) + * + * Check if the given prefix is one of the declared extensions. + * This is intended to be called only at compile-time. + * Called by: + * xsltPrecomputeStylesheet() (xslt.c) + * xsltParseTemplateContent (xslt.c) + * + * Returns 1 if this is an extension, 0 otherwise + */ +int +xsltCheckExtURI(xsltStylesheetPtr style, const xmlChar * URI) +{ + xsltExtDefPtr cur; + + if ((style == NULL) || (style->nsDefs == NULL)) + return (0); + if (URI == NULL) + return (0); + cur = (xsltExtDefPtr) style->nsDefs; + while (cur != NULL) { + if (xmlStrEqual(URI, cur->URI)) + return (1); + cur = cur->next; + } + return (0); +} + +/** + * xsltRegisterExtModuleFull: + * @URI: URI associated to this module + * @initFunc: the module initialization function + * @shutdownFunc: the module shutdown function + * @styleInitFunc: the module initialization function + * @styleShutdownFunc: the module shutdown function + * + * Register an XSLT extension module to the library. + * + * Returns 0 if sucessful, -1 in case of error + */ +int +xsltRegisterExtModuleFull(const xmlChar * URI, + xsltExtInitFunction initFunc, + xsltExtShutdownFunction shutdownFunc, + xsltStyleExtInitFunction styleInitFunc, + xsltStyleExtShutdownFunction styleShutdownFunc) +{ + int ret; + xsltExtModulePtr module; + + if ((URI == NULL) || (initFunc == NULL)) + return (-1); + if (xsltExtensionsHash == NULL) + xsltExtensionsHash = xmlHashCreate(10); + + if (xsltExtensionsHash == NULL) + return (-1); + + xmlMutexLock(xsltExtMutex); + + module = xmlHashLookup(xsltExtensionsHash, URI); + if (module != NULL) { + if ((module->initFunc == initFunc) && + (module->shutdownFunc == shutdownFunc)) + ret = 0; + else + ret = -1; + goto done; + } + module = xsltNewExtModule(initFunc, shutdownFunc, + styleInitFunc, styleShutdownFunc); + if (module == NULL) { + ret = -1; + goto done; + } + ret = xmlHashAddEntry(xsltExtensionsHash, URI, (void *) module); + +done: + xmlMutexUnlock(xsltExtMutex); + return (ret); +} + +/** + * xsltRegisterExtModule: + * @URI: URI associated to this module + * @initFunc: the module initialization function + * @shutdownFunc: the module shutdown function + * + * Register an XSLT extension module to the library. + * + * Returns 0 if sucessful, -1 in case of error + */ +int +xsltRegisterExtModule(const xmlChar * URI, + xsltExtInitFunction initFunc, + xsltExtShutdownFunction shutdownFunc) +{ + return xsltRegisterExtModuleFull(URI, initFunc, shutdownFunc, + NULL, NULL); +} + +/** + * xsltUnregisterExtModule: + * @URI: URI associated to this module + * + * Unregister an XSLT extension module from the library. + * + * Returns 0 if sucessful, -1 in case of error + */ +int +xsltUnregisterExtModule(const xmlChar * URI) +{ + int ret; + + if (URI == NULL) + return (-1); + if (xsltExtensionsHash == NULL) + return (-1); + + xmlMutexLock(xsltExtMutex); + + ret = xmlHashRemoveEntry(xsltExtensionsHash, URI, + (xmlHashDeallocator) xsltFreeExtModule); + + xmlMutexUnlock(xsltExtMutex); + + return (ret); +} + +/** + * xsltUnregisterAllExtModules: + * + * Unregister all the XSLT extension module from the library. + */ +static void +xsltUnregisterAllExtModules(void) +{ + if (xsltExtensionsHash == NULL) + return; + + xmlMutexLock(xsltExtMutex); + + xmlHashFree(xsltExtensionsHash, + (xmlHashDeallocator) xsltFreeExtModule); + xsltExtensionsHash = NULL; + + xmlMutexUnlock(xsltExtMutex); +} + +/** + * xsltXPathGetTransformContext: + * @ctxt: an XPath transformation context + * + * Provides the XSLT transformation context from the XPath transformation + * context. This is useful when an XPath function in the extension module + * is called by the XPath interpreter and that the XSLT context is needed + * for example to retrieve the associated data pertaining to this XSLT + * transformation. + * + * Returns the XSLT transformation context or NULL in case of error. + */ +xsltTransformContextPtr +xsltXPathGetTransformContext(xmlXPathParserContextPtr ctxt) +{ + if ((ctxt == NULL) || (ctxt->context == NULL)) + return (NULL); + return (ctxt->context->extra); +} + +/** + * xsltRegisterExtModuleFunction: + * @name: the function name + * @URI: the function namespace URI + * @function: the function callback + * + * Registers an extension module function. + * + * Returns 0 if successful, -1 in case of error. + */ +int +xsltRegisterExtModuleFunction(const xmlChar * name, const xmlChar * URI, + xmlXPathFunction function) +{ + if ((name == NULL) || (URI == NULL) || (function == NULL)) + return (-1); + + if (xsltFunctionsHash == NULL) + xsltFunctionsHash = xmlHashCreate(10); + if (xsltFunctionsHash == NULL) + return (-1); + + xmlMutexLock(xsltExtMutex); + + xmlHashUpdateEntry2(xsltFunctionsHash, name, URI, + XML_CAST_FPTR(function), NULL); + + xmlMutexUnlock(xsltExtMutex); + + return (0); +} + +/** + * xsltExtModuleFunctionLookup: + * @name: the function name + * @URI: the function namespace URI + * + * Looks up an extension module function + * + * Returns the function if found, NULL otherwise. + */ +xmlXPathFunction +xsltExtModuleFunctionLookup(const xmlChar * name, const xmlChar * URI) +{ + xmlXPathFunction ret; + + if ((xsltFunctionsHash == NULL) || (name == NULL) || (URI == NULL)) + return (NULL); + + xmlMutexLock(xsltExtMutex); + + XML_CAST_FPTR(ret) = xmlHashLookup2(xsltFunctionsHash, name, URI); + + xmlMutexUnlock(xsltExtMutex); + + /* if lookup fails, attempt a dynamic load on supported platforms */ + if (NULL == ret) { + if (!xsltExtModuleRegisterDynamic(URI)) { + xmlMutexLock(xsltExtMutex); + + XML_CAST_FPTR(ret) = + xmlHashLookup2(xsltFunctionsHash, name, URI); + + xmlMutexUnlock(xsltExtMutex); + } + } + + return ret; +} + +/** + * xsltUnregisterExtModuleFunction: + * @name: the function name + * @URI: the function namespace URI + * + * Unregisters an extension module function + * + * Returns 0 if successful, -1 in case of error. + */ +int +xsltUnregisterExtModuleFunction(const xmlChar * name, const xmlChar * URI) +{ + int ret; + + if ((xsltFunctionsHash == NULL) || (name == NULL) || (URI == NULL)) + return (-1); + + xmlMutexLock(xsltExtMutex); + + ret = xmlHashRemoveEntry2(xsltFunctionsHash, name, URI, NULL); + + xmlMutexUnlock(xsltExtMutex); + + return(ret); +} + +/** + * xsltUnregisterAllExtModuleFunction: + * + * Unregisters all extension module function + */ +static void +xsltUnregisterAllExtModuleFunction(void) +{ + xmlMutexLock(xsltExtMutex); + + xmlHashFree(xsltFunctionsHash, NULL); + xsltFunctionsHash = NULL; + + xmlMutexUnlock(xsltExtMutex); +} + + +/** + * xsltNewElemPreComp: + * @style: the XSLT stylesheet + * @inst: the element node + * @function: the transform function + * + * Creates and initializes an #xsltElemPreComp + * + * Returns the new and initialized #xsltElemPreComp + */ +xsltElemPreCompPtr +xsltNewElemPreComp(xsltStylesheetPtr style, xmlNodePtr inst, + xsltTransformFunction function) +{ + xsltElemPreCompPtr cur; + + cur = (xsltElemPreCompPtr) xmlMalloc(sizeof(xsltElemPreComp)); + if (cur == NULL) { + xsltTransformError(NULL, style, NULL, + "xsltNewExtElement : malloc failed\n"); + return (NULL); + } + memset(cur, 0, sizeof(xsltElemPreComp)); + + xsltInitElemPreComp(cur, style, inst, function, + (xsltElemPreCompDeallocator) xmlFree); + + return (cur); +} + +/** + * xsltInitElemPreComp: + * @comp: an #xsltElemPreComp (or generally a derived structure) + * @style: the XSLT stylesheet + * @inst: the element node + * @function: the transform function + * @freeFunc: the @comp deallocator + * + * Initializes an existing #xsltElemPreComp structure. This is usefull + * when extending an #xsltElemPreComp to store precomputed data. + * This function MUST be called on any extension element precomputed + * data struct. + */ +void +xsltInitElemPreComp(xsltElemPreCompPtr comp, xsltStylesheetPtr style, + xmlNodePtr inst, xsltTransformFunction function, + xsltElemPreCompDeallocator freeFunc) +{ + comp->type = XSLT_FUNC_EXTENSION; + comp->func = function; + comp->inst = inst; + comp->free = freeFunc; + + comp->next = style->preComps; + style->preComps = comp; +} + +/** + * xsltPreComputeExtModuleElement: + * @style: the stylesheet + * @inst: the element node + * + * Precomputes an extension module element + * + * Returns the precomputed data + */ +xsltElemPreCompPtr +xsltPreComputeExtModuleElement(xsltStylesheetPtr style, xmlNodePtr inst) +{ + xsltExtElementPtr ext; + xsltElemPreCompPtr comp = NULL; + + if ((style == NULL) || (inst == NULL) || + (inst->type != XML_ELEMENT_NODE) || (inst->ns == NULL)) + return (NULL); + + xmlMutexLock(xsltExtMutex); + + ext = (xsltExtElementPtr) + xmlHashLookup2(xsltElementsHash, inst->name, inst->ns->href); + + xmlMutexUnlock(xsltExtMutex); + + /* + * EXT TODO: Now what? + */ + if (ext == NULL) + return (NULL); + + if (ext->precomp != NULL) { + /* + * REVISIT TODO: Check if the text below is correct. + * This will return a xsltElemPreComp structure or NULL. + * 1) If the the author of the extension needs a + * custom structure to hold the specific values of + * this extension, he will derive a structure based on + * xsltElemPreComp; thus we obviously *cannot* refactor + * the xsltElemPreComp structure, since all already derived + * user-defined strucures will break. + * Example: For the extension xsl:document, + * in xsltDocumentComp() (preproc.c), the structure + * xsltStyleItemDocument is allocated, filled with + * specific values and returned. + * 2) If the author needs no values to be stored in + * this structure, then he'll return NULL; + */ + comp = ext->precomp(style, inst, ext->transform); + } + if (comp == NULL) { + /* + * Default creation of a xsltElemPreComp structure, if + * the author of this extension did not create a custom + * structure. + */ + comp = xsltNewElemPreComp(style, inst, ext->transform); + } + + return (comp); +} + +/** + * xsltRegisterExtModuleElement: + * @name: the element name + * @URI: the element namespace URI + * @precomp: the pre-computation callback + * @transform: the transformation callback + * + * Registers an extension module element. + * + * Returns 0 if successful, -1 in case of error. + */ +int +xsltRegisterExtModuleElement(const xmlChar * name, const xmlChar * URI, + xsltPreComputeFunction precomp, + xsltTransformFunction transform) +{ + int ret = 0; + + xsltExtElementPtr ext; + + if ((name == NULL) || (URI == NULL) || (transform == NULL)) + return (-1); + + if (xsltElementsHash == NULL) + xsltElementsHash = xmlHashCreate(10); + if (xsltElementsHash == NULL) + return (-1); + + xmlMutexLock(xsltExtMutex); + + ext = xsltNewExtElement(precomp, transform); + if (ext == NULL) { + ret = -1; + goto done; + } + + xmlHashUpdateEntry2(xsltElementsHash, name, URI, (void *) ext, + (xmlHashDeallocator) xsltFreeExtElement); + +done: + xmlMutexUnlock(xsltExtMutex); + + return (ret); +} + +/** + * xsltExtElementLookup: + * @ctxt: an XSLT process context + * @name: the element name + * @URI: the element namespace URI + * + * Looks up an extension element. @ctxt can be NULL to search only in + * module elements. + * + * Returns the element callback or NULL if not found + */ +xsltTransformFunction +xsltExtElementLookup(xsltTransformContextPtr ctxt, + const xmlChar * name, const xmlChar * URI) +{ + xsltTransformFunction ret; + + if ((name == NULL) || (URI == NULL)) + return (NULL); + + if ((ctxt != NULL) && (ctxt->extElements != NULL)) { + XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->extElements, name, URI); + if (ret != NULL) { + return(ret); + } + } + + ret = xsltExtModuleElementLookup(name, URI); + + return (ret); +} + +/** + * xsltExtModuleElementLookup: + * @name: the element name + * @URI: the element namespace URI + * + * Looks up an extension module element + * + * Returns the callback function if found, NULL otherwise. + */ +xsltTransformFunction +xsltExtModuleElementLookup(const xmlChar * name, const xmlChar * URI) +{ + xsltExtElementPtr ext; + + if ((xsltElementsHash == NULL) || (name == NULL) || (URI == NULL)) + return (NULL); + + xmlMutexLock(xsltExtMutex); + + ext = (xsltExtElementPtr) xmlHashLookup2(xsltElementsHash, name, URI); + + xmlMutexUnlock(xsltExtMutex); + + /* + * if function lookup fails, attempt a dynamic load on + * supported platforms + */ + if (NULL == ext) { + if (!xsltExtModuleRegisterDynamic(URI)) { + xmlMutexLock(xsltExtMutex); + + ext = (xsltExtElementPtr) + xmlHashLookup2(xsltElementsHash, name, URI); + + xmlMutexUnlock(xsltExtMutex); + } + } + + if (ext == NULL) + return (NULL); + return (ext->transform); +} + +/** + * xsltExtModuleElementPreComputeLookup: + * @name: the element name + * @URI: the element namespace URI + * + * Looks up an extension module element pre-computation function + * + * Returns the callback function if found, NULL otherwise. + */ +xsltPreComputeFunction +xsltExtModuleElementPreComputeLookup(const xmlChar * name, + const xmlChar * URI) +{ + xsltExtElementPtr ext; + + if ((xsltElementsHash == NULL) || (name == NULL) || (URI == NULL)) + return (NULL); + + xmlMutexLock(xsltExtMutex); + + ext = (xsltExtElementPtr) xmlHashLookup2(xsltElementsHash, name, URI); + + xmlMutexUnlock(xsltExtMutex); + + if (ext == NULL) { + if (!xsltExtModuleRegisterDynamic(URI)) { + xmlMutexLock(xsltExtMutex); + + ext = (xsltExtElementPtr) + xmlHashLookup2(xsltElementsHash, name, URI); + + xmlMutexUnlock(xsltExtMutex); + } + } + + if (ext == NULL) + return (NULL); + return (ext->precomp); +} + +/** + * xsltUnregisterExtModuleElement: + * @name: the element name + * @URI: the element namespace URI + * + * Unregisters an extension module element + * + * Returns 0 if successful, -1 in case of error. + */ +int +xsltUnregisterExtModuleElement(const xmlChar * name, const xmlChar * URI) +{ + int ret; + + if ((xsltElementsHash == NULL) || (name == NULL) || (URI == NULL)) + return (-1); + + xmlMutexLock(xsltExtMutex); + + ret = xmlHashRemoveEntry2(xsltElementsHash, name, URI, + (xmlHashDeallocator) xsltFreeExtElement); + + xmlMutexUnlock(xsltExtMutex); + + return(ret); +} + +/** + * xsltUnregisterAllExtModuleElement: + * + * Unregisters all extension module element + */ +static void +xsltUnregisterAllExtModuleElement(void) +{ + xmlMutexLock(xsltExtMutex); + + xmlHashFree(xsltElementsHash, (xmlHashDeallocator) xsltFreeExtElement); + xsltElementsHash = NULL; + + xmlMutexUnlock(xsltExtMutex); +} + +/** + * xsltRegisterExtModuleTopLevel: + * @name: the top-level element name + * @URI: the top-level element namespace URI + * @function: the top-level element callback + * + * Registers an extension module top-level element. + * + * Returns 0 if successful, -1 in case of error. + */ +int +xsltRegisterExtModuleTopLevel(const xmlChar * name, const xmlChar * URI, + xsltTopLevelFunction function) +{ + if ((name == NULL) || (URI == NULL) || (function == NULL)) + return (-1); + + if (xsltTopLevelsHash == NULL) + xsltTopLevelsHash = xmlHashCreate(10); + if (xsltTopLevelsHash == NULL) + return (-1); + + xmlMutexLock(xsltExtMutex); + + xmlHashUpdateEntry2(xsltTopLevelsHash, name, URI, + XML_CAST_FPTR(function), NULL); + + xmlMutexUnlock(xsltExtMutex); + + return (0); +} + +/** + * xsltExtModuleTopLevelLookup: + * @name: the top-level element name + * @URI: the top-level element namespace URI + * + * Looks up an extension module top-level element + * + * Returns the callback function if found, NULL otherwise. + */ +xsltTopLevelFunction +xsltExtModuleTopLevelLookup(const xmlChar * name, const xmlChar * URI) +{ + xsltTopLevelFunction ret; + + if ((xsltTopLevelsHash == NULL) || (name == NULL) || (URI == NULL)) + return (NULL); + + xmlMutexLock(xsltExtMutex); + + XML_CAST_FPTR(ret) = xmlHashLookup2(xsltTopLevelsHash, name, URI); + + xmlMutexUnlock(xsltExtMutex); + + /* if lookup fails, attempt a dynamic load on supported platforms */ + if (NULL == ret) { + if (!xsltExtModuleRegisterDynamic(URI)) { + xmlMutexLock(xsltExtMutex); + + XML_CAST_FPTR(ret) = xmlHashLookup2(xsltTopLevelsHash, name, URI); + + xmlMutexUnlock(xsltExtMutex); + } + } + + return (ret); +} + +/** + * xsltUnregisterExtModuleTopLevel: + * @name: the top-level element name + * @URI: the top-level element namespace URI + * + * Unregisters an extension module top-level element + * + * Returns 0 if successful, -1 in case of error. + */ +int +xsltUnregisterExtModuleTopLevel(const xmlChar * name, const xmlChar * URI) +{ + int ret; + + if ((xsltTopLevelsHash == NULL) || (name == NULL) || (URI == NULL)) + return (-1); + + xmlMutexLock(xsltExtMutex); + + ret = xmlHashRemoveEntry2(xsltTopLevelsHash, name, URI, NULL); + + xmlMutexUnlock(xsltExtMutex); + + return(ret); +} + +/** + * xsltUnregisterAllExtModuleTopLevel: + * + * Unregisters all extension module function + */ +static void +xsltUnregisterAllExtModuleTopLevel(void) +{ + xmlMutexLock(xsltExtMutex); + + xmlHashFree(xsltTopLevelsHash, NULL); + xsltTopLevelsHash = NULL; + + xmlMutexUnlock(xsltExtMutex); +} + +/** + * xsltGetExtInfo: + * @style: pointer to a stylesheet + * @URI: the namespace URI desired + * + * looks up URI in extInfos of the stylesheet + * + * returns a pointer to the hash table if found, else NULL + */ +xmlHashTablePtr +xsltGetExtInfo(xsltStylesheetPtr style, const xmlChar * URI) +{ + xsltExtDataPtr data; + + /* + * TODO: Why do we have a return type of xmlHashTablePtr? + * Is the user-allocated data for extension modules expected + * to be a xmlHashTablePtr only? Or is this intended for + * the EXSLT module only? + */ + + if (style != NULL && style->extInfos != NULL) { + data = xmlHashLookup(style->extInfos, URI); + if (data != NULL && data->extData != NULL) + return data->extData; + } + return NULL; +} + +/************************************************************************ + * * + * Test module http://xmlsoft.org/XSLT/ * + * * + ************************************************************************/ + +/************************************************************************ + * * + * Test of the extension module API * + * * + ************************************************************************/ + +static xmlChar *testData = NULL; +static xmlChar *testStyleData = NULL; + +/** + * xsltExtFunctionTest: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * function libxslt:test() for testing the extensions support. + */ +static void +xsltExtFunctionTest(xmlXPathParserContextPtr ctxt, + int nargs ATTRIBUTE_UNUSED) +{ + xsltTransformContextPtr tctxt; + void *data = NULL; + + tctxt = xsltXPathGetTransformContext(ctxt); + + if (testData == NULL) { + xsltGenericDebug(xsltGenericDebugContext, + "xsltExtFunctionTest: not initialized," + " calling xsltGetExtData\n"); + data = xsltGetExtData(tctxt, (const xmlChar *) XSLT_DEFAULT_URL); + if (data == NULL) { + xsltTransformError(tctxt, NULL, NULL, + "xsltExtElementTest: not initialized\n"); + return; + } + } + if (tctxt == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "xsltExtFunctionTest: failed to get the transformation context\n"); + return; + } + if (data == NULL) + data = xsltGetExtData(tctxt, (const xmlChar *) XSLT_DEFAULT_URL); + if (data == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "xsltExtFunctionTest: failed to get module data\n"); + return; + } + if (data != testData) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "xsltExtFunctionTest: got wrong module data\n"); + return; + } +#ifdef WITH_XSLT_DEBUG_FUNCTION + xsltGenericDebug(xsltGenericDebugContext, + "libxslt:test() called with %d args\n", nargs); +#endif +} + +/** + * xsltExtElementPreCompTest: + * @style: the stylesheet + * @inst: the instruction in the stylesheet + * + * Process a libxslt:test node + */ +static xsltElemPreCompPtr +xsltExtElementPreCompTest(xsltStylesheetPtr style, xmlNodePtr inst, + xsltTransformFunction function) +{ + xsltElemPreCompPtr ret; + + if (style == NULL) { + xsltTransformError(NULL, NULL, inst, + "xsltExtElementTest: no transformation context\n"); + return (NULL); + } + if (testStyleData == NULL) { + xsltGenericDebug(xsltGenericDebugContext, + "xsltExtElementPreCompTest: not initialized," + " calling xsltStyleGetExtData\n"); + xsltStyleGetExtData(style, (const xmlChar *) XSLT_DEFAULT_URL); + if (testStyleData == NULL) { + xsltTransformError(NULL, style, inst, + "xsltExtElementPreCompTest: not initialized\n"); + if (style != NULL) + style->errors++; + return (NULL); + } + } + if (inst == NULL) { + xsltTransformError(NULL, style, inst, + "xsltExtElementPreCompTest: no instruction\n"); + if (style != NULL) + style->errors++; + return (NULL); + } + ret = xsltNewElemPreComp(style, inst, function); + return (ret); +} + +/** + * xsltExtElementTest: + * @ctxt: an XSLT processing context + * @node: The current node + * @inst: the instruction in the stylesheet + * @comp: precomputed informations + * + * Process a libxslt:test node + */ +static void +xsltExtElementTest(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, + xsltElemPreCompPtr comp ATTRIBUTE_UNUSED) +{ + xmlNodePtr commentNode; + + if (testData == NULL) { + xsltGenericDebug(xsltGenericDebugContext, + "xsltExtElementTest: not initialized," + " calling xsltGetExtData\n"); + xsltGetExtData(ctxt, (const xmlChar *) XSLT_DEFAULT_URL); + if (testData == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsltExtElementTest: not initialized\n"); + return; + } + } + if (ctxt == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsltExtElementTest: no transformation context\n"); + return; + } + if (node == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsltExtElementTest: no current node\n"); + return; + } + if (inst == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsltExtElementTest: no instruction\n"); + return; + } + if (ctxt->insert == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsltExtElementTest: no insertion point\n"); + return; + } + commentNode = xmlNewComment((const xmlChar *) + "libxslt:test element test worked"); + xmlAddChild(ctxt->insert, commentNode); +} + +/** + * xsltExtInitTest: + * @ctxt: an XSLT transformation context + * @URI: the namespace URI for the extension + * + * A function called at initialization time of an XSLT extension module + * + * Returns a pointer to the module specific data for this transformation + */ +static void * +xsltExtInitTest(xsltTransformContextPtr ctxt, const xmlChar * URI) +{ + if (testStyleData == NULL) { + xsltGenericDebug(xsltGenericErrorContext, + "xsltExtInitTest: not initialized," + " calling xsltStyleGetExtData\n"); + testStyleData = xsltStyleGetExtData(ctxt->style, URI); + if (testStyleData == NULL) { + xsltTransformError(ctxt, NULL, NULL, + "xsltExtInitTest: not initialized\n"); + return (NULL); + } + } + if (testData != NULL) { + xsltTransformError(ctxt, NULL, NULL, + "xsltExtInitTest: already initialized\n"); + return (NULL); + } + testData = (void *) "test data"; + xsltGenericDebug(xsltGenericDebugContext, + "Registered test module : %s\n", URI); + return (testData); +} + + +/** + * xsltExtShutdownTest: + * @ctxt: an XSLT transformation context + * @URI: the namespace URI for the extension + * @data: the data associated to this module + * + * A function called at shutdown time of an XSLT extension module + */ +static void +xsltExtShutdownTest(xsltTransformContextPtr ctxt, + const xmlChar * URI, void *data) +{ + if (testData == NULL) { + xsltTransformError(ctxt, NULL, NULL, + "xsltExtShutdownTest: not initialized\n"); + return; + } + if (data != testData) { + xsltTransformError(ctxt, NULL, NULL, + "xsltExtShutdownTest: wrong data\n"); + } + testData = NULL; + xsltGenericDebug(xsltGenericDebugContext, + "Unregistered test module : %s\n", URI); +} + +/** + * xsltExtStyleInitTest: + * @style: an XSLT stylesheet + * @URI: the namespace URI for the extension + * + * A function called at initialization time of an XSLT extension module + * + * Returns a pointer to the module specific data for this transformation + */ +static void * +xsltExtStyleInitTest(xsltStylesheetPtr style ATTRIBUTE_UNUSED, + const xmlChar * URI) +{ + if (testStyleData != NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltExtInitTest: already initialized\n"); + return (NULL); + } + testStyleData = (void *) "test data"; + xsltGenericDebug(xsltGenericDebugContext, + "Registered test module : %s\n", URI); + return (testStyleData); +} + + +/** + * xsltExtStyleShutdownTest: + * @style: an XSLT stylesheet + * @URI: the namespace URI for the extension + * @data: the data associated to this module + * + * A function called at shutdown time of an XSLT extension module + */ +static void +xsltExtStyleShutdownTest(xsltStylesheetPtr style ATTRIBUTE_UNUSED, + const xmlChar * URI, void *data) +{ + if (testStyleData == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltExtShutdownTest: not initialized\n"); + return; + } + if (data != testStyleData) { + xsltTransformError(NULL, NULL, NULL, + "xsltExtShutdownTest: wrong data\n"); + } + testStyleData = NULL; + xsltGenericDebug(xsltGenericDebugContext, + "Unregistered test module : %s\n", URI); +} + +/** + * xsltRegisterTestModule: + * + * Registers the test module + */ +void +xsltRegisterTestModule(void) +{ + xsltInitGlobals(); + xsltRegisterExtModuleFull((const xmlChar *) XSLT_DEFAULT_URL, + xsltExtInitTest, xsltExtShutdownTest, + xsltExtStyleInitTest, + xsltExtStyleShutdownTest); + xsltRegisterExtModuleFunction((const xmlChar *) "test", + (const xmlChar *) XSLT_DEFAULT_URL, + xsltExtFunctionTest); + xsltRegisterExtModuleElement((const xmlChar *) "test", + (const xmlChar *) XSLT_DEFAULT_URL, + xsltExtElementPreCompTest, + xsltExtElementTest); +} + +static void +xsltHashScannerModuleFree(void *payload ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED, + xmlChar * name ATTRIBUTE_UNUSED) +{ +#ifdef WITH_MODULES + xmlModuleClose(payload); +#endif +} + +/** + * xsltInitGlobals: + * + * Initialize the global variables for extensions + */ +void +xsltInitGlobals(void) +{ + if (xsltExtMutex == NULL) { + xsltExtMutex = xmlNewMutex(); + } +} + +/** + * xsltCleanupGlobals: + * + * Unregister all global variables set up by the XSLT library + */ +void +xsltCleanupGlobals(void) +{ + xsltUnregisterAllExtModules(); + xsltUnregisterAllExtModuleFunction(); + xsltUnregisterAllExtModuleElement(); + xsltUnregisterAllExtModuleTopLevel(); + + xmlMutexLock(xsltExtMutex); + /* cleanup dynamic module hash */ + if (NULL != xsltModuleHash) { + xmlHashScan(xsltModuleHash, xsltHashScannerModuleFree, 0); + xmlHashFree(xsltModuleHash, NULL); + xsltModuleHash = NULL; + } + xmlMutexUnlock(xsltExtMutex); + + xmlFreeMutex(xsltExtMutex); + xsltExtMutex = NULL; + xsltFreeLocales(); + xsltUninit(); +} + +static void +xsltDebugDumpExtensionsCallback(void *function ATTRIBUTE_UNUSED, + FILE * output, const xmlChar * name, + const xmlChar * URI, + const xmlChar * not_used ATTRIBUTE_UNUSED) +{ + if (!name || !URI) + return; + fprintf(output, "{%s}%s\n", URI, name); +} + +static void +xsltDebugDumpExtModulesCallback(void *function ATTRIBUTE_UNUSED, + FILE * output, const xmlChar * URI, + const xmlChar * not_used ATTRIBUTE_UNUSED, + const xmlChar * not_used2 ATTRIBUTE_UNUSED) +{ + if (!URI) + return; + fprintf(output, "%s\n", URI); +} + +/** + * xsltDebugDumpExtensions: + * @output: the FILE * for the output, if NULL stdout is used + * + * Dumps a list of the registered XSLT extension functions and elements + */ +void +xsltDebugDumpExtensions(FILE * output) +{ + if (output == NULL) + output = stdout; + fprintf(output, + "Registered XSLT Extensions\n--------------------------\n"); + if (!xsltFunctionsHash) + fprintf(output, "No registered extension functions\n"); + else { + fprintf(output, "Registered Extension Functions:\n"); + xmlMutexLock(xsltExtMutex); + xmlHashScanFull(xsltFunctionsHash, + (xmlHashScannerFull) + xsltDebugDumpExtensionsCallback, output); + xmlMutexUnlock(xsltExtMutex); + } + if (!xsltElementsHash) + fprintf(output, "\nNo registered extension elements\n"); + else { + fprintf(output, "\nRegistered Extension Elements:\n"); + xmlMutexLock(xsltExtMutex); + xmlHashScanFull(xsltElementsHash, + (xmlHashScannerFull) + xsltDebugDumpExtensionsCallback, output); + xmlMutexUnlock(xsltExtMutex); + } + if (!xsltExtensionsHash) + fprintf(output, "\nNo registered extension modules\n"); + else { + fprintf(output, "\nRegistered Extension Modules:\n"); + xmlMutexLock(xsltExtMutex); + xmlHashScanFull(xsltExtensionsHash, + (xmlHashScannerFull) + xsltDebugDumpExtModulesCallback, output); + xmlMutexUnlock(xsltExtMutex); + } + +} diff --git a/libxslt/extensions.h b/libxslt/extensions.h new file mode 100644 index 0000000..900779c --- /dev/null +++ b/libxslt/extensions.h @@ -0,0 +1,262 @@ +/* + * Summary: interface for the extension support + * Description: This provide the API needed for simple and module + * extension support. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_EXTENSION_H__ +#define __XML_XSLT_EXTENSION_H__ + +#include <libxml/xpath.h> +#include "xsltexports.h" +#include "xsltInternals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Extension Modules API. + */ + +/** + * xsltInitGlobals: + * + * Initialize the global variables for extensions + * + */ + +XSLTPUBFUN void XSLTCALL + xsltInitGlobals (void); + +/** + * xsltStyleExtInitFunction: + * @ctxt: an XSLT stylesheet + * @URI: the namespace URI for the extension + * + * A function called at initialization time of an XSLT extension module. + * + * Returns a pointer to the module specific data for this transformation. + */ +typedef void * (*xsltStyleExtInitFunction) (xsltStylesheetPtr style, + const xmlChar *URI); + +/** + * xsltStyleExtShutdownFunction: + * @ctxt: an XSLT stylesheet + * @URI: the namespace URI for the extension + * @data: the data associated to this module + * + * A function called at shutdown time of an XSLT extension module. + */ +typedef void (*xsltStyleExtShutdownFunction) (xsltStylesheetPtr style, + const xmlChar *URI, + void *data); + +/** + * xsltExtInitFunction: + * @ctxt: an XSLT transformation context + * @URI: the namespace URI for the extension + * + * A function called at initialization time of an XSLT extension module. + * + * Returns a pointer to the module specific data for this transformation. + */ +typedef void * (*xsltExtInitFunction) (xsltTransformContextPtr ctxt, + const xmlChar *URI); + +/** + * xsltExtShutdownFunction: + * @ctxt: an XSLT transformation context + * @URI: the namespace URI for the extension + * @data: the data associated to this module + * + * A function called at shutdown time of an XSLT extension module. + */ +typedef void (*xsltExtShutdownFunction) (xsltTransformContextPtr ctxt, + const xmlChar *URI, + void *data); + +XSLTPUBFUN int XSLTCALL + xsltRegisterExtModule (const xmlChar *URI, + xsltExtInitFunction initFunc, + xsltExtShutdownFunction shutdownFunc); +XSLTPUBFUN int XSLTCALL + xsltRegisterExtModuleFull + (const xmlChar * URI, + xsltExtInitFunction initFunc, + xsltExtShutdownFunction shutdownFunc, + xsltStyleExtInitFunction styleInitFunc, + xsltStyleExtShutdownFunction styleShutdownFunc); + +XSLTPUBFUN int XSLTCALL + xsltUnregisterExtModule (const xmlChar * URI); + +XSLTPUBFUN void * XSLTCALL + xsltGetExtData (xsltTransformContextPtr ctxt, + const xmlChar *URI); + +XSLTPUBFUN void * XSLTCALL + xsltStyleGetExtData (xsltStylesheetPtr style, + const xmlChar *URI); +#ifdef XSLT_REFACTORED +XSLTPUBFUN void * XSLTCALL + xsltStyleStylesheetLevelGetExtData( + xsltStylesheetPtr style, + const xmlChar * URI); +#endif +XSLTPUBFUN void XSLTCALL + xsltShutdownCtxtExts (xsltTransformContextPtr ctxt); + +XSLTPUBFUN void XSLTCALL + xsltShutdownExts (xsltStylesheetPtr style); + +XSLTPUBFUN xsltTransformContextPtr XSLTCALL + xsltXPathGetTransformContext + (xmlXPathParserContextPtr ctxt); + +/* + * extension functions +*/ +XSLTPUBFUN int XSLTCALL + xsltRegisterExtModuleFunction + (const xmlChar *name, + const xmlChar *URI, + xmlXPathFunction function); +XSLTPUBFUN xmlXPathFunction XSLTCALL + xsltExtModuleFunctionLookup (const xmlChar *name, + const xmlChar *URI); +XSLTPUBFUN int XSLTCALL + xsltUnregisterExtModuleFunction + (const xmlChar *name, + const xmlChar *URI); + +/* + * extension elements + */ +typedef xsltElemPreCompPtr (*xsltPreComputeFunction) + (xsltStylesheetPtr style, + xmlNodePtr inst, + xsltTransformFunction function); + +XSLTPUBFUN xsltElemPreCompPtr XSLTCALL + xsltNewElemPreComp (xsltStylesheetPtr style, + xmlNodePtr inst, + xsltTransformFunction function); +XSLTPUBFUN void XSLTCALL + xsltInitElemPreComp (xsltElemPreCompPtr comp, + xsltStylesheetPtr style, + xmlNodePtr inst, + xsltTransformFunction function, + xsltElemPreCompDeallocator freeFunc); + +XSLTPUBFUN int XSLTCALL + xsltRegisterExtModuleElement + (const xmlChar *name, + const xmlChar *URI, + xsltPreComputeFunction precomp, + xsltTransformFunction transform); +XSLTPUBFUN xsltTransformFunction XSLTCALL + xsltExtElementLookup (xsltTransformContextPtr ctxt, + const xmlChar *name, + const xmlChar *URI); +XSLTPUBFUN xsltTransformFunction XSLTCALL + xsltExtModuleElementLookup + (const xmlChar *name, + const xmlChar *URI); +XSLTPUBFUN xsltPreComputeFunction XSLTCALL + xsltExtModuleElementPreComputeLookup + (const xmlChar *name, + const xmlChar *URI); +XSLTPUBFUN int XSLTCALL + xsltUnregisterExtModuleElement + (const xmlChar *name, + const xmlChar *URI); + +/* + * top-level elements + */ +typedef void (*xsltTopLevelFunction) (xsltStylesheetPtr style, + xmlNodePtr inst); + +XSLTPUBFUN int XSLTCALL + xsltRegisterExtModuleTopLevel + (const xmlChar *name, + const xmlChar *URI, + xsltTopLevelFunction function); +XSLTPUBFUN xsltTopLevelFunction XSLTCALL + xsltExtModuleTopLevelLookup + (const xmlChar *name, + const xmlChar *URI); +XSLTPUBFUN int XSLTCALL + xsltUnregisterExtModuleTopLevel + (const xmlChar *name, + const xmlChar *URI); + + +/* These 2 functions are deprecated for use within modules. */ +XSLTPUBFUN int XSLTCALL + xsltRegisterExtFunction (xsltTransformContextPtr ctxt, + const xmlChar *name, + const xmlChar *URI, + xmlXPathFunction function); +XSLTPUBFUN int XSLTCALL + xsltRegisterExtElement (xsltTransformContextPtr ctxt, + const xmlChar *name, + const xmlChar *URI, + xsltTransformFunction function); + +/* + * Extension Prefix handling API. + * Those are used by the XSLT (pre)processor. + */ + +XSLTPUBFUN int XSLTCALL + xsltRegisterExtPrefix (xsltStylesheetPtr style, + const xmlChar *prefix, + const xmlChar *URI); +XSLTPUBFUN int XSLTCALL + xsltCheckExtPrefix (xsltStylesheetPtr style, + const xmlChar *URI); +XSLTPUBFUN int XSLTCALL + xsltCheckExtURI (xsltStylesheetPtr style, + const xmlChar *URI); +XSLTPUBFUN int XSLTCALL + xsltInitCtxtExts (xsltTransformContextPtr ctxt); +XSLTPUBFUN void XSLTCALL + xsltFreeCtxtExts (xsltTransformContextPtr ctxt); +XSLTPUBFUN void XSLTCALL + xsltFreeExts (xsltStylesheetPtr style); + +XSLTPUBFUN xsltElemPreCompPtr XSLTCALL + xsltPreComputeExtModuleElement + (xsltStylesheetPtr style, + xmlNodePtr inst); +/* + * Extension Infos access. + * Used by exslt initialisation + */ + +XSLTPUBFUN xmlHashTablePtr XSLTCALL + xsltGetExtInfo (xsltStylesheetPtr style, + const xmlChar *URI); + +/** + * Test module http://xmlsoft.org/XSLT/ + */ +XSLTPUBFUN void XSLTCALL + xsltRegisterTestModule (void); +XSLTPUBFUN void XSLTCALL + xsltDebugDumpExtensions (FILE * output); + + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_EXTENSION_H__ */ + diff --git a/libxslt/extra.c b/libxslt/extra.c new file mode 100644 index 0000000..17df4ba --- /dev/null +++ b/libxslt/extra.c @@ -0,0 +1,332 @@ +/* + * extra.c: Implementation of non-standard features + * + * Reference: + * Michael Kay "XSLT Programmer's Reference" pp 637-643 + * The node-set() extension function + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#include <libxml/xmlmemory.h> +#include <libxml/tree.h> +#include <libxml/hash.h> +#include <libxml/xmlerror.h> +#include <libxml/parserInternals.h> +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "extensions.h" +#include "variables.h" +#include "transform.h" +#include "extra.h" +#include "preproc.h" + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_EXTRA +#endif + +/************************************************************************ + * * + * Handling of XSLT debugging * + * * + ************************************************************************/ + +/** + * xsltDebug: + * @ctxt: an XSLT processing context + * @node: The current node + * @inst: the instruction in the stylesheet + * @comp: precomputed informations + * + * Process an debug node + */ +void +xsltDebug(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED, + xmlNodePtr inst ATTRIBUTE_UNUSED, + xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) +{ + int i, j; + + xsltGenericError(xsltGenericErrorContext, "Templates:\n"); + for (i = 0, j = ctxt->templNr - 1; ((i < 15) && (j >= 0)); i++, j--) { + xsltGenericError(xsltGenericErrorContext, "#%d ", i); + if (ctxt->templTab[j]->name != NULL) + xsltGenericError(xsltGenericErrorContext, "name %s ", + ctxt->templTab[j]->name); + if (ctxt->templTab[j]->match != NULL) + xsltGenericError(xsltGenericErrorContext, "name %s ", + ctxt->templTab[j]->match); + if (ctxt->templTab[j]->mode != NULL) + xsltGenericError(xsltGenericErrorContext, "name %s ", + ctxt->templTab[j]->mode); + xsltGenericError(xsltGenericErrorContext, "\n"); + } + xsltGenericError(xsltGenericErrorContext, "Variables:\n"); + for (i = 0, j = ctxt->varsNr - 1; ((i < 15) && (j >= 0)); i++, j--) { + xsltStackElemPtr cur; + + if (ctxt->varsTab[j] == NULL) + continue; + xsltGenericError(xsltGenericErrorContext, "#%d\n", i); + cur = ctxt->varsTab[j]; + while (cur != NULL) { + if (cur->comp == NULL) { + xsltGenericError(xsltGenericErrorContext, + "corrupted !!!\n"); + } else if (cur->comp->type == XSLT_FUNC_PARAM) { + xsltGenericError(xsltGenericErrorContext, "param "); + } else if (cur->comp->type == XSLT_FUNC_VARIABLE) { + xsltGenericError(xsltGenericErrorContext, "var "); + } + if (cur->name != NULL) + xsltGenericError(xsltGenericErrorContext, "%s ", + cur->name); + else + xsltGenericError(xsltGenericErrorContext, "noname !!!!"); +#ifdef LIBXML_DEBUG_ENABLED + if (cur->value != NULL) { + xmlXPathDebugDumpObject(stdout, cur->value, 1); + } else { + xsltGenericError(xsltGenericErrorContext, "NULL !!!!"); + } +#endif + xsltGenericError(xsltGenericErrorContext, "\n"); + cur = cur->next; + } + + } +} + +/************************************************************************ + * * + * Classic extensions as described by M. Kay * + * * + ************************************************************************/ + +/** + * xsltFunctionNodeSet: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the node-set() XSLT function + * node-set node-set(result-tree) + * + * This function is available in libxslt, saxon or xt namespace. + */ +void +xsltFunctionNodeSet(xmlXPathParserContextPtr ctxt, int nargs){ + if (nargs != 1) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "node-set() : expects one result-tree arg\n"); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + if ((ctxt->value == NULL) || + ((ctxt->value->type != XPATH_XSLT_TREE) && + (ctxt->value->type != XPATH_NODESET))) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "node-set() invalid arg expecting a result tree\n"); + ctxt->error = XPATH_INVALID_TYPE; + return; + } + if (ctxt->value->type == XPATH_XSLT_TREE) { + ctxt->value->type = XPATH_NODESET; + } +} + + +/* + * Okay the following really seems unportable and since it's not + * part of any standard I'm not too ashamed to do this + */ +#if defined(linux) || defined(__sun) +#if defined(HAVE_MKTIME) && defined(HAVE_LOCALTIME) && defined(HAVE_ASCTIME) +#define WITH_LOCALTIME + +/** + * xsltFunctionLocalTime: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the localTime XSLT function used by NORM + * string localTime(???) + * + * This function is available in Norm's extension namespace + * Code (and comments) contributed by Norm + */ +static void +xsltFunctionLocalTime(xmlXPathParserContextPtr ctxt, int nargs) { + xmlXPathObjectPtr obj; + char *str; + char digits[5]; + char result[29]; + long int field; + time_t gmt, lmt; + struct tm gmt_tm; + struct tm *local_tm; + + if (nargs != 1) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "localTime() : invalid number of args %d\n", nargs); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + + obj = valuePop(ctxt); + + if (obj->type != XPATH_STRING) { + obj = xmlXPathConvertString(obj); + } + if (obj == NULL) { + valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); + return; + } + + str = (char *) obj->stringval; + + /* str = "$Date$" */ + memset(digits, 0, sizeof(digits)); + strncpy(digits, str+7, 4); + field = strtol(digits, NULL, 10); + gmt_tm.tm_year = field - 1900; + + memset(digits, 0, sizeof(digits)); + strncpy(digits, str+12, 2); + field = strtol(digits, NULL, 10); + gmt_tm.tm_mon = field - 1; + + memset(digits, 0, sizeof(digits)); + strncpy(digits, str+15, 2); + field = strtol(digits, NULL, 10); + gmt_tm.tm_mday = field; + + memset(digits, 0, sizeof(digits)); + strncpy(digits, str+18, 2); + field = strtol(digits, NULL, 10); + gmt_tm.tm_hour = field; + + memset(digits, 0, sizeof(digits)); + strncpy(digits, str+21, 2); + field = strtol(digits, NULL, 10); + gmt_tm.tm_min = field; + + memset(digits, 0, sizeof(digits)); + strncpy(digits, str+24, 2); + field = strtol(digits, NULL, 10); + gmt_tm.tm_sec = field; + + /* Now turn gmt_tm into a time. */ + gmt = mktime(&gmt_tm); + + + /* + * FIXME: it's been too long since I did manual memory management. + * (I swore never to do it again.) Does this introduce a memory leak? + */ + local_tm = localtime(&gmt); + + /* + * Calling localtime() has the side-effect of setting timezone. + * After we know the timezone, we can adjust for it + */ +#if !defined(__FreeBSD__) + lmt = gmt - timezone; +#else /* FreeBSD DOESN'T have such side-ffect */ + lmt = gmt - local_tm->tm_gmtoff; +#endif + /* + * FIXME: it's been too long since I did manual memory management. + * (I swore never to do it again.) Does this introduce a memory leak? + */ + local_tm = localtime(&lmt); + + /* + * Now convert local_tm back into a string. This doesn't introduce + * a memory leak, so says asctime(3). + */ + + str = asctime(local_tm); /* "Tue Jun 26 05:02:16 2001" */ + /* 0123456789 123456789 123 */ + + memset(result, 0, sizeof(result)); /* "Thu, 26 Jun 2001" */ + /* 0123456789 12345 */ + + strncpy(result, str, 20); + strcpy(result+20, "???"); /* tzname doesn't work, fake it */ + strncpy(result+23, str+19, 5); + + /* Ok, now result contains the string I want to send back. */ + valuePush(ctxt, xmlXPathNewString((xmlChar *)result)); +} +#endif +#endif /* linux or sun */ + + +/** + * xsltRegisterExtras: + * @ctxt: a XSLT process context + * + * Registers the built-in extensions. This function is deprecated, use + * xsltRegisterAllExtras instead. + */ +void +xsltRegisterExtras(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED) { + xsltRegisterAllExtras(); +} + +/** + * xsltRegisterAllExtras: + * + * Registers the built-in extensions + */ +void +xsltRegisterAllExtras (void) { + xsltRegisterExtModuleFunction((const xmlChar *) "node-set", + XSLT_LIBXSLT_NAMESPACE, + xsltFunctionNodeSet); + xsltRegisterExtModuleFunction((const xmlChar *) "node-set", + XSLT_SAXON_NAMESPACE, + xsltFunctionNodeSet); + xsltRegisterExtModuleFunction((const xmlChar *) "node-set", + XSLT_XT_NAMESPACE, + xsltFunctionNodeSet); +#ifdef WITH_LOCALTIME + xsltRegisterExtModuleFunction((const xmlChar *) "localTime", + XSLT_NORM_SAXON_NAMESPACE, + xsltFunctionLocalTime); +#endif + xsltRegisterExtModuleElement((const xmlChar *) "debug", + XSLT_LIBXSLT_NAMESPACE, + NULL, + (xsltTransformFunction) xsltDebug); + xsltRegisterExtModuleElement((const xmlChar *) "output", + XSLT_SAXON_NAMESPACE, + xsltDocumentComp, + (xsltTransformFunction) xsltDocumentElem); + xsltRegisterExtModuleElement((const xmlChar *) "write", + XSLT_XALAN_NAMESPACE, + xsltDocumentComp, + (xsltTransformFunction) xsltDocumentElem); + xsltRegisterExtModuleElement((const xmlChar *) "document", + XSLT_XT_NAMESPACE, + xsltDocumentComp, + (xsltTransformFunction) xsltDocumentElem); + xsltRegisterExtModuleElement((const xmlChar *) "document", + XSLT_NAMESPACE, + xsltDocumentComp, + (xsltTransformFunction) xsltDocumentElem); +} diff --git a/libxslt/extra.h b/libxslt/extra.h new file mode 100644 index 0000000..6929e3c --- /dev/null +++ b/libxslt/extra.h @@ -0,0 +1,80 @@ +/* + * Summary: interface for the non-standard features + * Description: implement some extension outside the XSLT namespace + * but not EXSLT with is in a different library. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_EXTRA_H__ +#define __XML_XSLT_EXTRA_H__ + +#include <libxml/xpath.h> +#include "xsltexports.h" +#include "xsltInternals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * XSLT_LIBXSLT_NAMESPACE: + * + * This is the libxslt namespace for specific extensions. + */ +#define XSLT_LIBXSLT_NAMESPACE ((xmlChar *) "http://xmlsoft.org/XSLT/namespace") + +/** + * XSLT_SAXON_NAMESPACE: + * + * This is Michael Kay's Saxon processor namespace for extensions. + */ +#define XSLT_SAXON_NAMESPACE ((xmlChar *) "http://icl.com/saxon") + +/** + * XSLT_XT_NAMESPACE: + * + * This is James Clark's XT processor namespace for extensions. + */ +#define XSLT_XT_NAMESPACE ((xmlChar *) "http://www.jclark.com/xt") + +/** + * XSLT_XALAN_NAMESPACE: + * + * This is the Apache project XALAN processor namespace for extensions. + */ +#define XSLT_XALAN_NAMESPACE ((xmlChar *) \ + "org.apache.xalan.xslt.extensions.Redirect") + +/** + * XSLT_NORM_SAXON_NAMESPACE: + * + * This is Norm's namespace for SAXON extensions. + */ +#define XSLT_NORM_SAXON_NAMESPACE ((xmlChar *) \ + "http://nwalsh.com/xslt/ext/com.nwalsh.saxon.CVS") + + +XSLTPUBFUN void XSLTCALL + xsltFunctionNodeSet (xmlXPathParserContextPtr ctxt, + int nargs); +XSLTPUBFUN void XSLTCALL + xsltDebug (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); + + +XSLTPUBFUN void XSLTCALL + xsltRegisterExtras (xsltTransformContextPtr ctxt); +XSLTPUBFUN void XSLTCALL + xsltRegisterAllExtras (void); + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_EXTRA_H__ */ + diff --git a/libxslt/functions.c b/libxslt/functions.c new file mode 100644 index 0000000..a5e7021 --- /dev/null +++ b/libxslt/functions.c @@ -0,0 +1,988 @@ +/* + * functions.c: Implementation of the XSLT extra functions + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + * Bjorn Reese <breese@users.sourceforge.net> for number formatting + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_CTYPE_H +#include <ctype.h> +#endif + +#include <libxml/xmlmemory.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/valid.h> +#include <libxml/hash.h> +#include <libxml/xmlerror.h> +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> +#include <libxml/parserInternals.h> +#include <libxml/uri.h> +#include <libxml/xpointer.h> +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "functions.h" +#include "extensions.h" +#include "numbersInternals.h" +#include "keys.h" +#include "documents.h" + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_FUNCTION +#endif + +/* + * Some versions of DocBook XSL use the vendor string to detect + * supporting chunking, this is a workaround to be considered + * in the list of decent XSLT processors <grin/> + */ +#define DOCBOOK_XSL_HACK + +/** + * xsltXPathFunctionLookup: + * @ctxt: a void * but the XSLT transformation context actually + * @name: the function name + * @ns_uri: the function namespace URI + * + * This is the entry point when a function is needed by the XPath + * interpretor. + * + * Returns the callback function or NULL if not found + */ +xmlXPathFunction +xsltXPathFunctionLookup (xmlXPathContextPtr ctxt, + const xmlChar *name, const xmlChar *ns_uri) { + xmlXPathFunction ret; + + if ((ctxt == NULL) || (name == NULL) || (ns_uri == NULL)) + return (NULL); + +#ifdef WITH_XSLT_DEBUG_FUNCTION + xsltGenericDebug(xsltGenericDebugContext, + "Lookup function {%s}%s\n", ns_uri, name); +#endif + + /* give priority to context-level functions */ + /* + ret = (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri); + */ + XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->funcHash, name, ns_uri); + + if (ret == NULL) + ret = xsltExtModuleFunctionLookup(name, ns_uri); + +#ifdef WITH_XSLT_DEBUG_FUNCTION + if (ret != NULL) + xsltGenericDebug(xsltGenericDebugContext, + "found function %s\n", name); +#endif + return(ret); +} + + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +static void +xsltDocumentFunctionLoadDocument(xmlXPathParserContextPtr ctxt, xmlChar* URI) +{ + xsltTransformContextPtr tctxt; + xmlURIPtr uri; + xmlChar *fragment; + xsltDocumentPtr idoc; /* document info */ + xmlDocPtr doc; + xmlXPathContextPtr xptrctxt = NULL; + xmlXPathObjectPtr resObj = NULL; + + tctxt = xsltXPathGetTransformContext(ctxt); + if (tctxt == NULL) { + xsltTransformError(NULL, NULL, NULL, + "document() : internal error tctxt == NULL\n"); + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); + return; + } + + uri = xmlParseURI((const char *) URI); + if (uri == NULL) { + xsltTransformError(tctxt, NULL, NULL, + "document() : failed to parse URI\n"); + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); + return; + } + + /* + * check for and remove fragment identifier + */ + fragment = (xmlChar *)uri->fragment; + if (fragment != NULL) { + xmlChar *newURI; + uri->fragment = NULL; + newURI = xmlSaveUri(uri); + idoc = xsltLoadDocument(tctxt, newURI); + xmlFree(newURI); + } else + idoc = xsltLoadDocument(tctxt, URI); + xmlFreeURI(uri); + + if (idoc == NULL) { + if ((URI == NULL) || + (URI[0] == '#') || + ((tctxt->style->doc != NULL) && + (xmlStrEqual(tctxt->style->doc->URL, URI)))) + { + /* + * This selects the stylesheet's doc itself. + */ + doc = tctxt->style->doc; + } else { + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); + + if (fragment != NULL) + xmlFree(fragment); + + return; + } + } else + doc = idoc->doc; + + if (fragment == NULL) { + valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc)); + return; + } + + /* use XPointer of HTML location for fragment ID */ +#ifdef LIBXML_XPTR_ENABLED + xptrctxt = xmlXPtrNewContext(doc, NULL, NULL); + if (xptrctxt == NULL) { + xsltTransformError(tctxt, NULL, NULL, + "document() : internal error xptrctxt == NULL\n"); + goto out_fragment; + } + + resObj = xmlXPtrEval(fragment, xptrctxt); + xmlXPathFreeContext(xptrctxt); +#endif + + if (resObj == NULL) + goto out_fragment; + + switch (resObj->type) { + case XPATH_NODESET: + break; + case XPATH_UNDEFINED: + case XPATH_BOOLEAN: + case XPATH_NUMBER: + case XPATH_STRING: + case XPATH_POINT: + case XPATH_USERS: + case XPATH_XSLT_TREE: + case XPATH_RANGE: + case XPATH_LOCATIONSET: + xsltTransformError(tctxt, NULL, NULL, + "document() : XPointer does not select a node set: #%s\n", + fragment); + goto out_object; + } + + valuePush(ctxt, resObj); + xmlFree(fragment); + return; + +out_object: + xmlXPathFreeObject(resObj); + +out_fragment: + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); + xmlFree(fragment); +} + +/** + * xsltDocumentFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the document() XSLT function + * node-set document(object, node-set?) + */ +void +xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs) +{ + xmlXPathObjectPtr obj, obj2 = NULL; + xmlChar *base = NULL, *URI; + + + if ((nargs < 1) || (nargs > 2)) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "document() : invalid number of args %d\n", + nargs); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + if (ctxt->value == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "document() : invalid arg value\n"); + ctxt->error = XPATH_INVALID_TYPE; + return; + } + + if (nargs == 2) { + if (ctxt->value->type != XPATH_NODESET) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "document() : invalid arg expecting a nodeset\n"); + ctxt->error = XPATH_INVALID_TYPE; + return; + } + + obj2 = valuePop(ctxt); + } + + if (ctxt->value->type == XPATH_NODESET) { + int i; + xmlXPathObjectPtr newobj, ret; + + obj = valuePop(ctxt); + ret = xmlXPathNewNodeSet(NULL); + + if ((obj != NULL) && obj->nodesetval) { + for (i = 0; i < obj->nodesetval->nodeNr; i++) { + valuePush(ctxt, + xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i])); + xmlXPathStringFunction(ctxt, 1); + if (nargs == 2) { + valuePush(ctxt, xmlXPathObjectCopy(obj2)); + } else { + valuePush(ctxt, + xmlXPathNewNodeSet(obj->nodesetval-> + nodeTab[i])); + } + xsltDocumentFunction(ctxt, 2); + newobj = valuePop(ctxt); + ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, + newobj->nodesetval); + xmlXPathFreeObject(newobj); + } + } + + if (obj != NULL) + xmlXPathFreeObject(obj); + if (obj2 != NULL) + xmlXPathFreeObject(obj2); + valuePush(ctxt, ret); + return; + } + /* + * Make sure it's converted to a string + */ + xmlXPathStringFunction(ctxt, 1); + if (ctxt->value->type != XPATH_STRING) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "document() : invalid arg expecting a string\n"); + ctxt->error = XPATH_INVALID_TYPE; + if (obj2 != NULL) + xmlXPathFreeObject(obj2); + return; + } + obj = valuePop(ctxt); + if (obj->stringval == NULL) { + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); + } else { + xsltTransformContextPtr tctxt; + tctxt = xsltXPathGetTransformContext(ctxt); + if ((obj2 != NULL) && (obj2->nodesetval != NULL) && + (obj2->nodesetval->nodeNr > 0) && + IS_XSLT_REAL_NODE(obj2->nodesetval->nodeTab[0])) { + xmlNodePtr target; + + target = obj2->nodesetval->nodeTab[0]; + if ((target->type == XML_ATTRIBUTE_NODE) || + (target->type == XML_PI_NODE)) { + target = ((xmlAttrPtr) target)->parent; + } + base = xmlNodeGetBase(target->doc, target); + } else { + if ((tctxt != NULL) && (tctxt->inst != NULL)) { + base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst); + } else if ((tctxt != NULL) && (tctxt->style != NULL) && + (tctxt->style->doc != NULL)) { + base = xmlNodeGetBase(tctxt->style->doc, + (xmlNodePtr) tctxt->style->doc); + } + } + URI = xmlBuildURI(obj->stringval, base); + if (base != NULL) + xmlFree(base); + if (URI == NULL) { + if ((tctxt != NULL) && (tctxt->style != NULL) && + (tctxt->style->doc != NULL) && + (xmlStrEqual(URI, tctxt->style->doc->URL))) { + /* This selects the stylesheet's doc itself. */ + valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) tctxt->style->doc)); + } else { + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); + } + } else { + xsltDocumentFunctionLoadDocument( ctxt, URI ); + xmlFree(URI); + } + } + xmlXPathFreeObject(obj); + if (obj2 != NULL) + xmlXPathFreeObject(obj2); +} + +/** + * xsltKeyFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the key() XSLT function + * node-set key(string, object) + */ +void +xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){ + xmlXPathObjectPtr obj1, obj2; + + if (nargs != 2) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "key() : expects two arguments\n"); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + + /* + * Get the key's value. + */ + obj2 = valuePop(ctxt); + xmlXPathStringFunction(ctxt, 1); + if ((obj2 == NULL) || + (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "key() : invalid arg expecting a string\n"); + ctxt->error = XPATH_INVALID_TYPE; + xmlXPathFreeObject(obj2); + + return; + } + /* + * Get the key's name. + */ + obj1 = valuePop(ctxt); + + if ((obj2->type == XPATH_NODESET) || (obj2->type == XPATH_XSLT_TREE)) { + int i; + xmlXPathObjectPtr newobj, ret; + + ret = xmlXPathNewNodeSet(NULL); + + if (obj2->nodesetval != NULL) { + for (i = 0; i < obj2->nodesetval->nodeNr; i++) { + valuePush(ctxt, xmlXPathObjectCopy(obj1)); + valuePush(ctxt, + xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i])); + xmlXPathStringFunction(ctxt, 1); + xsltKeyFunction(ctxt, 2); + newobj = valuePop(ctxt); + ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval, + newobj->nodesetval); + xmlXPathFreeObject(newobj); + } + } + valuePush(ctxt, ret); + } else { + xmlNodeSetPtr nodelist = NULL; + xmlChar *key = NULL, *value; + const xmlChar *keyURI; + xsltTransformContextPtr tctxt; + xmlChar *qname, *prefix; + xmlXPathContextPtr xpctxt = ctxt->context; + xmlNodePtr tmpNode = NULL; + xsltDocumentPtr oldDocInfo; + + tctxt = xsltXPathGetTransformContext(ctxt); + + oldDocInfo = tctxt->document; + + if (xpctxt->node == NULL) { + xsltTransformError(tctxt, NULL, tctxt->inst, + "Internal error in xsltKeyFunction(): " + "The context node is not set on the XPath context.\n"); + tctxt->state = XSLT_STATE_STOPPED; + goto error; + } + /* + * Get the associated namespace URI if qualified name + */ + qname = obj1->stringval; + key = xmlSplitQName2(qname, &prefix); + if (key == NULL) { + key = xmlStrdup(obj1->stringval); + keyURI = NULL; + if (prefix != NULL) + xmlFree(prefix); + } else { + if (prefix != NULL) { + keyURI = xmlXPathNsLookup(xpctxt, prefix); + if (keyURI == NULL) { + xsltTransformError(tctxt, NULL, tctxt->inst, + "key() : prefix %s is not bound\n", prefix); + /* + * TODO: Shouldn't we stop here? + */ + } + xmlFree(prefix); + } else { + keyURI = NULL; + } + } + + /* + * Force conversion of first arg to string + */ + valuePush(ctxt, obj2); + xmlXPathStringFunction(ctxt, 1); + if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { + xsltTransformError(tctxt, NULL, tctxt->inst, + "key() : invalid arg expecting a string\n"); + ctxt->error = XPATH_INVALID_TYPE; + goto error; + } + obj2 = valuePop(ctxt); + value = obj2->stringval; + + /* + * We need to ensure that ctxt->document is available for + * xsltGetKey(). + * First find the relevant doc, which is the context node's + * owner doc; using context->doc is not safe, since + * the doc could have been acquired via the document() function, + * or the doc might be a Result Tree Fragment. + * FUTURE INFO: In XSLT 2.0 the key() function takes an additional + * argument indicating the doc to use. + */ + if (xpctxt->node->type == XML_NAMESPACE_DECL) { + /* + * REVISIT: This is a libxml hack! Check xpath.c for details. + * The XPath module sets the owner element of a ns-node on + * the ns->next field. + */ + if ((((xmlNsPtr) xpctxt->node)->next != NULL) && + (((xmlNsPtr) xpctxt->node)->next->type == XML_ELEMENT_NODE)) + { + tmpNode = (xmlNodePtr) ((xmlNsPtr) xpctxt->node)->next; + } + } else + tmpNode = xpctxt->node; + + if ((tmpNode == NULL) || (tmpNode->doc == NULL)) { + xsltTransformError(tctxt, NULL, tctxt->inst, + "Internal error in xsltKeyFunction(): " + "Couldn't get the doc of the XPath context node.\n"); + goto error; + } + + if ((tctxt->document == NULL) || + (tctxt->document->doc != tmpNode->doc)) + { + if (tmpNode->doc->name && (tmpNode->doc->name[0] == ' ')) { + /* + * This is a Result Tree Fragment. + */ + if (tmpNode->doc->_private == NULL) { + tmpNode->doc->_private = xsltNewDocument(tctxt, tmpNode->doc); + if (tmpNode->doc->_private == NULL) + goto error; + } + tctxt->document = (xsltDocumentPtr) tmpNode->doc->_private; + } else { + /* + * May be the initial source doc or a doc acquired via the + * document() function. + */ + tctxt->document = xsltFindDocument(tctxt, tmpNode->doc); + } + if (tctxt->document == NULL) { + xsltTransformError(tctxt, NULL, tctxt->inst, + "Internal error in xsltKeyFunction(): " + "Could not get the document info of a context doc.\n"); + tctxt->state = XSLT_STATE_STOPPED; + goto error; + } + } + /* + * Get/compute the key value. + */ + nodelist = xsltGetKey(tctxt, key, keyURI, value); + +error: + tctxt->document = oldDocInfo; + valuePush(ctxt, xmlXPathWrapNodeSet( + xmlXPathNodeSetMerge(NULL, nodelist))); + if (key != NULL) + xmlFree(key); + } + + if (obj1 != NULL) + xmlXPathFreeObject(obj1); + if (obj2 != NULL) + xmlXPathFreeObject(obj2); +} + +/** + * xsltUnparsedEntityURIFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the unparsed-entity-uri() XSLT function + * string unparsed-entity-uri(string) + */ +void +xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){ + xmlXPathObjectPtr obj; + xmlChar *str; + + if ((nargs != 1) || (ctxt->value == NULL)) { + xsltGenericError(xsltGenericErrorContext, + "unparsed-entity-uri() : expects one string arg\n"); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + obj = valuePop(ctxt); + if (obj->type != XPATH_STRING) { + obj = xmlXPathConvertString(obj); + } + + str = obj->stringval; + if (str == NULL) { + valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); + } else { + xmlEntityPtr entity; + + entity = xmlGetDocEntity(ctxt->context->doc, str); + if (entity == NULL) { + valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); + } else { + if (entity->URI != NULL) + valuePush(ctxt, xmlXPathNewString(entity->URI)); + else + valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); + } + } + xmlXPathFreeObject(obj); +} + +/** + * xsltFormatNumberFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the format-number() XSLT function + * string format-number(number, string, string?) + */ +void +xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) +{ + xmlXPathObjectPtr numberObj = NULL; + xmlXPathObjectPtr formatObj = NULL; + xmlXPathObjectPtr decimalObj = NULL; + xsltStylesheetPtr sheet; + xsltDecimalFormatPtr formatValues; + xmlChar *result; + xsltTransformContextPtr tctxt; + + tctxt = xsltXPathGetTransformContext(ctxt); + if (tctxt == NULL) + return; + sheet = tctxt->style; + if (sheet == NULL) + return; + formatValues = sheet->decimalFormat; + + switch (nargs) { + case 3: + CAST_TO_STRING; + decimalObj = valuePop(ctxt); + formatValues = xsltDecimalFormatGetByName(sheet, decimalObj->stringval); + if (formatValues == NULL) { + xsltTransformError(tctxt, NULL, NULL, + "format-number() : undeclared decimal format '%s'\n", + decimalObj->stringval); + } + /* Intentional fall-through */ + case 2: + CAST_TO_STRING; + formatObj = valuePop(ctxt); + CAST_TO_NUMBER; + numberObj = valuePop(ctxt); + break; + default: + XP_ERROR(XPATH_INVALID_ARITY); + } + + if (formatValues != NULL) { + if (xsltFormatNumberConversion(formatValues, + formatObj->stringval, + numberObj->floatval, + &result) == XPATH_EXPRESSION_OK) { + valuePush(ctxt, xmlXPathNewString(result)); + xmlFree(result); + } + } + + xmlXPathFreeObject(numberObj); + xmlXPathFreeObject(formatObj); + xmlXPathFreeObject(decimalObj); +} + +/** + * xsltGenerateIdFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the generate-id() XSLT function + * string generate-id(node-set?) + */ +void +xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){ + static char base_address; + xmlNodePtr cur = NULL; + xmlXPathObjectPtr obj = NULL; + long val; + xmlChar str[30]; + + if (nargs == 0) { + cur = ctxt->context->node; + } else if (nargs == 1) { + xmlNodeSetPtr nodelist; + int i, ret; + + if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) { + ctxt->error = XPATH_INVALID_TYPE; + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "generate-id() : invalid arg expecting a node-set\n"); + return; + } + obj = valuePop(ctxt); + nodelist = obj->nodesetval; + if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) { + xmlXPathFreeObject(obj); + valuePush(ctxt, xmlXPathNewCString("")); + return; + } + cur = nodelist->nodeTab[0]; + for (i = 1;i < nodelist->nodeNr;i++) { + ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]); + if (ret == -1) + cur = nodelist->nodeTab[i]; + } + } else { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "generate-id() : invalid number of args %d\n", nargs); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + + if (obj) + xmlXPathFreeObject(obj); + + val = (long)((char *)cur - (char *)&base_address); + if (val >= 0) { + snprintf((char *)str, sizeof(str), "idp%ld", val); + } else { + snprintf((char *)str, sizeof(str), "idm%ld", -val); + } + valuePush(ctxt, xmlXPathNewString(str)); +} + +/** + * xsltSystemPropertyFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the system-property() XSLT function + * object system-property(string) + */ +void +xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){ + xmlXPathObjectPtr obj; + xmlChar *prefix, *name; + const xmlChar *nsURI = NULL; + + if (nargs != 1) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "system-property() : expects one string arg\n"); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "system-property() : invalid arg expecting a string\n"); + ctxt->error = XPATH_INVALID_TYPE; + return; + } + obj = valuePop(ctxt); + if (obj->stringval == NULL) { + valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); + } else { + name = xmlSplitQName2(obj->stringval, &prefix); + if (name == NULL) { + name = xmlStrdup(obj->stringval); + } else { + nsURI = xmlXPathNsLookup(ctxt->context, prefix); + if (nsURI == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "system-property() : prefix %s is not bound\n", prefix); + } + } + + if (xmlStrEqual(nsURI, XSLT_NAMESPACE)) { +#ifdef DOCBOOK_XSL_HACK + if (xmlStrEqual(name, (const xmlChar *)"vendor")) { + xsltStylesheetPtr sheet; + xsltTransformContextPtr tctxt; + + tctxt = xsltXPathGetTransformContext(ctxt); + if ((tctxt != NULL) && (tctxt->inst != NULL) && + (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) && + (tctxt->inst->parent != NULL) && + (xmlStrEqual(tctxt->inst->parent->name, + BAD_CAST "template"))) + sheet = tctxt->style; + else + sheet = NULL; + if ((sheet != NULL) && (sheet->doc != NULL) && + (sheet->doc->URL != NULL) && + (xmlStrstr(sheet->doc->URL, + (const xmlChar *)"chunk") != NULL)) { + valuePush(ctxt, xmlXPathNewString( + (const xmlChar *)"libxslt (SAXON 6.2 compatible)")); + + } else { + valuePush(ctxt, xmlXPathNewString( + (const xmlChar *)XSLT_DEFAULT_VENDOR)); + } + } else +#else + if (xmlStrEqual(name, (const xmlChar *)"vendor")) { + valuePush(ctxt, xmlXPathNewString( + (const xmlChar *)XSLT_DEFAULT_VENDOR)); + } else +#endif + if (xmlStrEqual(name, (const xmlChar *)"version")) { + valuePush(ctxt, xmlXPathNewString( + (const xmlChar *)XSLT_DEFAULT_VERSION)); + } else if (xmlStrEqual(name, (const xmlChar *)"vendor-url")) { + valuePush(ctxt, xmlXPathNewString( + (const xmlChar *)XSLT_DEFAULT_URL)); + } else { + valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); + } + } else { + valuePush(ctxt, xmlXPathNewString((const xmlChar *)"")); + } + if (name != NULL) + xmlFree(name); + if (prefix != NULL) + xmlFree(prefix); + } + xmlXPathFreeObject(obj); +} + +/** + * xsltElementAvailableFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the element-available() XSLT function + * boolean element-available(string) + */ +void +xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ + xmlXPathObjectPtr obj; + xmlChar *prefix, *name; + const xmlChar *nsURI = NULL; + xsltTransformContextPtr tctxt; + + if (nargs != 1) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "element-available() : expects one string arg\n"); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + xmlXPathStringFunction(ctxt, 1); + if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "element-available() : invalid arg expecting a string\n"); + ctxt->error = XPATH_INVALID_TYPE; + return; + } + obj = valuePop(ctxt); + tctxt = xsltXPathGetTransformContext(ctxt); + if (tctxt == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "element-available() : internal error tctxt == NULL\n"); + xmlXPathFreeObject(obj); + valuePush(ctxt, xmlXPathNewBoolean(0)); + return; + } + + + name = xmlSplitQName2(obj->stringval, &prefix); + if (name == NULL) { + xmlNsPtr ns; + + name = xmlStrdup(obj->stringval); + ns = xmlSearchNs(tctxt->inst->doc, tctxt->inst, NULL); + if (ns != NULL) nsURI = xmlStrdup(ns->href); + } else { + nsURI = xmlXPathNsLookup(ctxt->context, prefix); + if (nsURI == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "element-available() : prefix %s is not bound\n", prefix); + } + } + + if (xsltExtElementLookup(tctxt, name, nsURI) != NULL) { + valuePush(ctxt, xmlXPathNewBoolean(1)); + } else { + valuePush(ctxt, xmlXPathNewBoolean(0)); + } + + xmlXPathFreeObject(obj); + if (name != NULL) + xmlFree(name); + if (prefix != NULL) + xmlFree(prefix); +} + +/** + * xsltFunctionAvailableFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the function-available() XSLT function + * boolean function-available(string) + */ +void +xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){ + xmlXPathObjectPtr obj; + xmlChar *prefix, *name; + const xmlChar *nsURI = NULL; + + if (nargs != 1) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "function-available() : expects one string arg\n"); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + xmlXPathStringFunction(ctxt, 1); + if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "function-available() : invalid arg expecting a string\n"); + ctxt->error = XPATH_INVALID_TYPE; + return; + } + obj = valuePop(ctxt); + + name = xmlSplitQName2(obj->stringval, &prefix); + if (name == NULL) { + name = xmlStrdup(obj->stringval); + } else { + nsURI = xmlXPathNsLookup(ctxt->context, prefix); + if (nsURI == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "function-available() : prefix %s is not bound\n", prefix); + } + } + + if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) { + valuePush(ctxt, xmlXPathNewBoolean(1)); + } else { + valuePush(ctxt, xmlXPathNewBoolean(0)); + } + + xmlXPathFreeObject(obj); + if (name != NULL) + xmlFree(name); + if (prefix != NULL) + xmlFree(prefix); +} + +/** + * xsltCurrentFunction: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * Implement the current() XSLT function + * node-set current() + */ +static void +xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){ + xsltTransformContextPtr tctxt; + + if (nargs != 0) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "current() : function uses no argument\n"); + ctxt->error = XPATH_INVALID_ARITY; + return; + } + tctxt = xsltXPathGetTransformContext(ctxt); + if (tctxt == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "current() : internal error tctxt == NULL\n"); + valuePush(ctxt, xmlXPathNewNodeSet(NULL)); + } else { + valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */ + } +} + +/************************************************************************ + * * + * Registration of XSLT and libxslt functions * + * * + ************************************************************************/ + +/** + * xsltRegisterAllFunctions: + * @ctxt: the XPath context + * + * Registers all default XSLT functions in this context + */ +void +xsltRegisterAllFunctions(xmlXPathContextPtr ctxt) +{ + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current", + xsltCurrentFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document", + xsltDocumentFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri", + xsltUnparsedEntityURIFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number", + xsltFormatNumberFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id", + xsltGenerateIdFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property", + xsltSystemPropertyFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available", + xsltElementAvailableFunction); + xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available", + xsltFunctionAvailableFunction); +} diff --git a/libxslt/functions.h b/libxslt/functions.h new file mode 100644 index 0000000..e0e0bf9 --- /dev/null +++ b/libxslt/functions.h @@ -0,0 +1,78 @@ +/* + * Summary: interface for the XSLT functions not from XPath + * Description: a set of extra functions coming from XSLT but not in XPath + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard and Bjorn Reese <breese@users.sourceforge.net> + */ + +#ifndef __XML_XSLT_FUNCTIONS_H__ +#define __XML_XSLT_FUNCTIONS_H__ + +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> +#include "xsltexports.h" +#include "xsltInternals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * XSLT_REGISTER_FUNCTION_LOOKUP: + * + * Registering macro, not general purpose at all but used in different modules. + */ +#define XSLT_REGISTER_FUNCTION_LOOKUP(ctxt) \ + xmlXPathRegisterFuncLookup((ctxt)->xpathCtxt, \ + (xmlXPathFuncLookupFunc) xsltXPathFunctionLookup, \ + (void *)(ctxt->xpathCtxt)); + +XSLTPUBFUN xmlXPathFunction XSLTCALL + xsltXPathFunctionLookup (xmlXPathContextPtr ctxt, + const xmlChar *name, + const xmlChar *ns_uri); + +/* + * Interfaces for the functions implementations. + */ + +XSLTPUBFUN void XSLTCALL + xsltDocumentFunction (xmlXPathParserContextPtr ctxt, + int nargs); +XSLTPUBFUN void XSLTCALL + xsltKeyFunction (xmlXPathParserContextPtr ctxt, + int nargs); +XSLTPUBFUN void XSLTCALL + xsltUnparsedEntityURIFunction (xmlXPathParserContextPtr ctxt, + int nargs); +XSLTPUBFUN void XSLTCALL + xsltFormatNumberFunction (xmlXPathParserContextPtr ctxt, + int nargs); +XSLTPUBFUN void XSLTCALL + xsltGenerateIdFunction (xmlXPathParserContextPtr ctxt, + int nargs); +XSLTPUBFUN void XSLTCALL + xsltSystemPropertyFunction (xmlXPathParserContextPtr ctxt, + int nargs); +XSLTPUBFUN void XSLTCALL + xsltElementAvailableFunction (xmlXPathParserContextPtr ctxt, + int nargs); +XSLTPUBFUN void XSLTCALL + xsltFunctionAvailableFunction (xmlXPathParserContextPtr ctxt, + int nargs); + +/* + * And the registration + */ + +XSLTPUBFUN void XSLTCALL + xsltRegisterAllFunctions (xmlXPathContextPtr ctxt); + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_FUNCTIONS_H__ */ + diff --git a/libxslt/imports.c b/libxslt/imports.c new file mode 100644 index 0000000..7262aab --- /dev/null +++ b/libxslt/imports.c @@ -0,0 +1,414 @@ +/* + * imports.c: Implementation of the XSLT imports + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_MATH_H +#include <math.h> +#endif +#ifdef HAVE_FLOAT_H +#include <float.h> +#endif +#ifdef HAVE_IEEEFP_H +#include <ieeefp.h> +#endif +#ifdef HAVE_NAN_H +#include <nan.h> +#endif +#ifdef HAVE_CTYPE_H +#include <ctype.h> +#endif + +#include <libxml/xmlmemory.h> +#include <libxml/tree.h> +#include <libxml/hash.h> +#include <libxml/xmlerror.h> +#include <libxml/uri.h> +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "preproc.h" +#include "imports.h" +#include "documents.h" +#include "security.h" +#include "pattern.h" + + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ +/** + * xsltFixImportedCompSteps: + * @master: the "master" stylesheet + * @style: the stylesheet being imported by the master + * + * normalize the comp steps for the stylesheet being imported + * by the master, together with any imports within that. + * + */ +static void xsltFixImportedCompSteps(xsltStylesheetPtr master, + xsltStylesheetPtr style) { + xsltStylesheetPtr res; + xmlHashScan(style->templatesHash, + (xmlHashScanner) xsltNormalizeCompSteps, master); + master->extrasNr += style->extrasNr; + for (res = style->imports; res != NULL; res = res->next) { + xsltFixImportedCompSteps(master, res); + } +} + +/** + * xsltParseStylesheetImport: + * @style: the XSLT stylesheet + * @cur: the import element + * + * parse an XSLT stylesheet import element + * + * Returns 0 in case of success -1 in case of failure. + */ + +int +xsltParseStylesheetImport(xsltStylesheetPtr style, xmlNodePtr cur) { + int ret = -1; + xmlDocPtr import = NULL; + xmlChar *base = NULL; + xmlChar *uriRef = NULL; + xmlChar *URI = NULL; + xsltStylesheetPtr res; + xsltSecurityPrefsPtr sec; + + if ((cur == NULL) || (style == NULL)) + return (ret); + + uriRef = xmlGetNsProp(cur, (const xmlChar *)"href", NULL); + if (uriRef == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:import : missing href attribute\n"); + goto error; + } + + base = xmlNodeGetBase(style->doc, cur); + URI = xmlBuildURI(uriRef, base); + if (URI == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:import : invalid URI reference %s\n", uriRef); + goto error; + } + + res = style; + while (res != NULL) { + if (res->doc == NULL) + break; + if (xmlStrEqual(res->doc->URL, URI)) { + xsltTransformError(NULL, style, cur, + "xsl:import : recursion detected on imported URL %s\n", URI); + goto error; + } + res = res->parent; + } + + /* + * Security framework check + */ + sec = xsltGetDefaultSecurityPrefs(); + if (sec != NULL) { + int secres; + + secres = xsltCheckRead(sec, NULL, URI); + if (secres == 0) { + xsltTransformError(NULL, NULL, NULL, + "xsl:import: read rights for %s denied\n", + URI); + goto error; + } + } + + import = xsltDocDefaultLoader(URI, style->dict, XSLT_PARSE_OPTIONS, + (void *) style, XSLT_LOAD_STYLESHEET); + if (import == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:import : unable to load %s\n", URI); + goto error; + } + + res = xsltParseStylesheetImportedDoc(import, style); + if (res != NULL) { + res->next = style->imports; + style->imports = res; + if (style->parent == NULL) { + xsltFixImportedCompSteps(style, res); + } + ret = 0; + } else { + xmlFreeDoc(import); + } + +error: + if (uriRef != NULL) + xmlFree(uriRef); + if (base != NULL) + xmlFree(base); + if (URI != NULL) + xmlFree(URI); + + return (ret); +} + +/** + * xsltParseStylesheetInclude: + * @style: the XSLT stylesheet + * @cur: the include node + * + * parse an XSLT stylesheet include element + * + * Returns 0 in case of success -1 in case of failure + */ + +int +xsltParseStylesheetInclude(xsltStylesheetPtr style, xmlNodePtr cur) { + int ret = -1; + xmlDocPtr oldDoc; + xmlChar *base = NULL; + xmlChar *uriRef = NULL; + xmlChar *URI = NULL; + xsltStylesheetPtr result; + xsltDocumentPtr include; + xsltDocumentPtr docptr; + int oldNopreproc; + + if ((cur == NULL) || (style == NULL)) + return (ret); + + uriRef = xmlGetNsProp(cur, (const xmlChar *)"href", NULL); + if (uriRef == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:include : missing href attribute\n"); + goto error; + } + + base = xmlNodeGetBase(style->doc, cur); + URI = xmlBuildURI(uriRef, base); + if (URI == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:include : invalid URI reference %s\n", uriRef); + goto error; + } + + /* + * in order to detect recursion, we check all previously included + * stylesheets. + */ + docptr = style->includes; + while (docptr != NULL) { + if (xmlStrEqual(docptr->doc->URL, URI)) { + xsltTransformError(NULL, style, cur, + "xsl:include : recursion detected on included URL %s\n", URI); + goto error; + } + docptr = docptr->includes; + } + + include = xsltLoadStyleDocument(style, URI); + if (include == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:include : unable to load %s\n", URI); + goto error; + } +#ifdef XSLT_REFACTORED + if (IS_XSLT_ELEM_FAST(cur) && (cur->psvi != NULL)) { + ((xsltStyleItemIncludePtr) cur->psvi)->include = include; + } else { + xsltTransformError(NULL, style, cur, + "Internal error: (xsltParseStylesheetInclude) " + "The xsl:include element was not compiled.\n", URI); + style->errors++; + } +#endif + oldDoc = style->doc; + style->doc = include->doc; + /* chain to stylesheet for recursion checking */ + include->includes = style->includes; + style->includes = include; + oldNopreproc = style->nopreproc; + style->nopreproc = include->preproc; + /* + * TODO: This will change some values of the + * including stylesheet with every included module + * (e.g. excluded-result-prefixes) + * We need to strictly seperate such stylesheet-owned values. + */ + result = xsltParseStylesheetProcess(style, include->doc); + style->nopreproc = oldNopreproc; + include->preproc = 1; + style->includes = include->includes; + style->doc = oldDoc; + if (result == NULL) { + ret = -1; + goto error; + } + ret = 0; + +error: + if (uriRef != NULL) + xmlFree(uriRef); + if (base != NULL) + xmlFree(base); + if (URI != NULL) + xmlFree(URI); + + return (ret); +} + +/** + * xsltNextImport: + * @cur: the current XSLT stylesheet + * + * Find the next stylesheet in import precedence. + * + * Returns the next stylesheet or NULL if it was the last one + */ + +xsltStylesheetPtr +xsltNextImport(xsltStylesheetPtr cur) { + if (cur == NULL) + return(NULL); + if (cur->imports != NULL) + return(cur->imports); + if (cur->next != NULL) + return(cur->next) ; + do { + cur = cur->parent; + if (cur == NULL) break; + if (cur->next != NULL) return(cur->next); + } while (cur != NULL); + return(cur); +} + +/** + * xsltNeedElemSpaceHandling: + * @ctxt: an XSLT transformation context + * + * Checks whether that stylesheet requires white-space stripping + * + * Returns 1 if space should be stripped, 0 if not + */ + +int +xsltNeedElemSpaceHandling(xsltTransformContextPtr ctxt) { + xsltStylesheetPtr style; + + if (ctxt == NULL) + return(0); + style = ctxt->style; + while (style != NULL) { + if (style->stripSpaces != NULL) + return(1); + style = xsltNextImport(style); + } + return(0); +} + +/** + * xsltFindElemSpaceHandling: + * @ctxt: an XSLT transformation context + * @node: an XML node + * + * Find strip-space or preserve-space informations for an element + * respect the import precedence or the wildcards + * + * Returns 1 if space should be stripped, 0 if not, and 2 if everything + * should be CDTATA wrapped. + */ + +int +xsltFindElemSpaceHandling(xsltTransformContextPtr ctxt, xmlNodePtr node) { + xsltStylesheetPtr style; + const xmlChar *val; + + if ((ctxt == NULL) || (node == NULL)) + return(0); + style = ctxt->style; + while (style != NULL) { + if (node->ns != NULL) { + val = (const xmlChar *) + xmlHashLookup2(style->stripSpaces, node->name, node->ns->href); + if (val == NULL) { + val = (const xmlChar *) + xmlHashLookup2(style->stripSpaces, BAD_CAST "*", + node->ns->href); + } + } else { + val = (const xmlChar *) + xmlHashLookup2(style->stripSpaces, node->name, NULL); + } + if (val != NULL) { + if (xmlStrEqual(val, (xmlChar *) "strip")) + return(1); + if (xmlStrEqual(val, (xmlChar *) "preserve")) + return(0); + } + if (style->stripAll == 1) + return(1); + if (style->stripAll == -1) + return(0); + + style = xsltNextImport(style); + } + return(0); +} + +/** + * xsltFindTemplate: + * @ctxt: an XSLT transformation context + * @name: the template name + * @nameURI: the template name URI + * + * Finds the named template, apply import precedence rule. + * REVISIT TODO: We'll change the nameURI fields of + * templates to be in the string dict, so if the + * specified @nameURI is in the same dict, then use pointer + * comparison. Check if this can be done in a sane way. + * Maybe this function is not needed internally at + * transformation-time if we hard-wire the called templates + * to the caller. + * + * Returns the xsltTemplatePtr or NULL if not found + */ +xsltTemplatePtr +xsltFindTemplate(xsltTransformContextPtr ctxt, const xmlChar *name, + const xmlChar *nameURI) { + xsltTemplatePtr cur; + xsltStylesheetPtr style; + + if ((ctxt == NULL) || (name == NULL)) + return(NULL); + style = ctxt->style; + while (style != NULL) { + if (style->namedTemplates != NULL) { + cur = (xsltTemplatePtr) + xmlHashLookup2(style->namedTemplates, name, nameURI); + if (cur != NULL) + return(cur); + } + + style = xsltNextImport(style); + } + return(NULL); +} + diff --git a/libxslt/imports.h b/libxslt/imports.h new file mode 100644 index 0000000..95e44e5 --- /dev/null +++ b/libxslt/imports.h @@ -0,0 +1,75 @@ +/* + * Summary: interface for the XSLT import support + * Description: macros and fuctions needed to implement and + * access the import tree + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_IMPORTS_H__ +#define __XML_XSLT_IMPORTS_H__ + +#include <libxml/tree.h> +#include "xsltexports.h" +#include "xsltInternals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * XSLT_GET_IMPORT_PTR: + * + * A macro to import pointers from the stylesheet cascading order. + */ +#define XSLT_GET_IMPORT_PTR(res, style, name) { \ + xsltStylesheetPtr st = style; \ + res = NULL; \ + while (st != NULL) { \ + if (st->name != NULL) { res = st->name; break; } \ + st = xsltNextImport(st); \ + }} + +/** + * XSLT_GET_IMPORT_INT: + * + * A macro to import intergers from the stylesheet cascading order. + */ +#define XSLT_GET_IMPORT_INT(res, style, name) { \ + xsltStylesheetPtr st = style; \ + res = -1; \ + while (st != NULL) { \ + if (st->name != -1) { res = st->name; break; } \ + st = xsltNextImport(st); \ + }} + +/* + * Module interfaces + */ +XSLTPUBFUN int XSLTCALL + xsltParseStylesheetImport(xsltStylesheetPtr style, + xmlNodePtr cur); +XSLTPUBFUN int XSLTCALL + xsltParseStylesheetInclude + (xsltStylesheetPtr style, + xmlNodePtr cur); +XSLTPUBFUN xsltStylesheetPtr XSLTCALL + xsltNextImport (xsltStylesheetPtr style); +XSLTPUBFUN int XSLTCALL + xsltNeedElemSpaceHandling(xsltTransformContextPtr ctxt); +XSLTPUBFUN int XSLTCALL + xsltFindElemSpaceHandling(xsltTransformContextPtr ctxt, + xmlNodePtr node); +XSLTPUBFUN xsltTemplatePtr XSLTCALL + xsltFindTemplate (xsltTransformContextPtr ctxt, + const xmlChar *name, + const xmlChar *nameURI); + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_IMPORTS_H__ */ + diff --git a/libxslt/keys.c b/libxslt/keys.c new file mode 100644 index 0000000..43a343e --- /dev/null +++ b/libxslt/keys.c @@ -0,0 +1,935 @@ +/* + * keys.c: Implemetation of the keys support + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> + +#include <libxml/xmlmemory.h> +#include <libxml/tree.h> +#include <libxml/valid.h> +#include <libxml/hash.h> +#include <libxml/xmlerror.h> +#include <libxml/parserInternals.h> +#include <libxml/xpathInternals.h> +#include <libxml/xpath.h> +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "imports.h" +#include "templates.h" +#include "keys.h" + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_KEYS +#endif + +static int +xsltInitDocKeyTable(xsltTransformContextPtr ctxt, const xmlChar *name, + const xmlChar *nameURI); + +/************************************************************************ + * * + * Type functions * + * * + ************************************************************************/ + +/** + * xsltNewKeyDef: + * @name: the key name or NULL + * @nameURI: the name URI or NULL + * + * Create a new XSLT KeyDef + * + * Returns the newly allocated xsltKeyDefPtr or NULL in case of error + */ +static xsltKeyDefPtr +xsltNewKeyDef(const xmlChar *name, const xmlChar *nameURI) { + xsltKeyDefPtr cur; + + cur = (xsltKeyDefPtr) xmlMalloc(sizeof(xsltKeyDef)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewKeyDef : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltKeyDef)); + if (name != NULL) + cur->name = xmlStrdup(name); + if (nameURI != NULL) + cur->nameURI = xmlStrdup(nameURI); + cur->nsList = NULL; + return(cur); +} + +/** + * xsltFreeKeyDef: + * @keyd: an XSLT key definition + * + * Free up the memory allocated by @keyd + */ +static void +xsltFreeKeyDef(xsltKeyDefPtr keyd) { + if (keyd == NULL) + return; + if (keyd->comp != NULL) + xmlXPathFreeCompExpr(keyd->comp); + if (keyd->usecomp != NULL) + xmlXPathFreeCompExpr(keyd->usecomp); + if (keyd->name != NULL) + xmlFree(keyd->name); + if (keyd->nameURI != NULL) + xmlFree(keyd->nameURI); + if (keyd->match != NULL) + xmlFree(keyd->match); + if (keyd->use != NULL) + xmlFree(keyd->use); + if (keyd->nsList != NULL) + xmlFree(keyd->nsList); + memset(keyd, -1, sizeof(xsltKeyDef)); + xmlFree(keyd); +} + +/** + * xsltFreeKeyDefList: + * @keyd: an XSLT key definition list + * + * Free up the memory allocated by all the elements of @keyd + */ +static void +xsltFreeKeyDefList(xsltKeyDefPtr keyd) { + xsltKeyDefPtr cur; + + while (keyd != NULL) { + cur = keyd; + keyd = keyd->next; + xsltFreeKeyDef(cur); + } +} + +/** + * xsltNewKeyTable: + * @name: the key name or NULL + * @nameURI: the name URI or NULL + * + * Create a new XSLT KeyTable + * + * Returns the newly allocated xsltKeyTablePtr or NULL in case of error + */ +static xsltKeyTablePtr +xsltNewKeyTable(const xmlChar *name, const xmlChar *nameURI) { + xsltKeyTablePtr cur; + + cur = (xsltKeyTablePtr) xmlMalloc(sizeof(xsltKeyTable)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewKeyTable : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltKeyTable)); + if (name != NULL) + cur->name = xmlStrdup(name); + if (nameURI != NULL) + cur->nameURI = xmlStrdup(nameURI); + cur->keys = xmlHashCreate(0); + return(cur); +} + +/** + * xsltFreeKeyTable: + * @keyt: an XSLT key table + * + * Free up the memory allocated by @keyt + */ +static void +xsltFreeKeyTable(xsltKeyTablePtr keyt) { + if (keyt == NULL) + return; + if (keyt->name != NULL) + xmlFree(keyt->name); + if (keyt->nameURI != NULL) + xmlFree(keyt->nameURI); + if (keyt->keys != NULL) + xmlHashFree(keyt->keys, + (xmlHashDeallocator) xmlXPathFreeNodeSet); + memset(keyt, -1, sizeof(xsltKeyTable)); + xmlFree(keyt); +} + +/** + * xsltFreeKeyTableList: + * @keyt: an XSLT key table list + * + * Free up the memory allocated by all the elements of @keyt + */ +static void +xsltFreeKeyTableList(xsltKeyTablePtr keyt) { + xsltKeyTablePtr cur; + + while (keyt != NULL) { + cur = keyt; + keyt = keyt->next; + xsltFreeKeyTable(cur); + } +} + +/************************************************************************ + * * + * The interpreter for the precompiled patterns * + * * + ************************************************************************/ + + +/** + * xsltFreeKeys: + * @style: an XSLT stylesheet + * + * Free up the memory used by XSLT keys in a stylesheet + */ +void +xsltFreeKeys(xsltStylesheetPtr style) { + if (style->keys) + xsltFreeKeyDefList((xsltKeyDefPtr) style->keys); +} + +/** + * skipString: + * @cur: the current pointer + * @end: the current offset + * + * skip a string delimited by " or ' + * + * Returns the byte after the string or -1 in case of error + */ +static int +skipString(const xmlChar *cur, int end) { + xmlChar limit; + + if ((cur == NULL) || (end < 0)) return(-1); + if ((cur[end] == '\'') || (cur[end] == '"')) limit = cur[end]; + else return(end); + end++; + while (cur[end] != 0) { + if (cur[end] == limit) + return(end + 1); + end++; + } + return(-1); +} + +/** + * skipPredicate: + * @cur: the current pointer + * @end: the current offset + * + * skip a predicate + * + * Returns the byte after the predicate or -1 in case of error + */ +static int +skipPredicate(const xmlChar *cur, int end) { + if ((cur == NULL) || (end < 0)) return(-1); + if (cur[end] != '[') return(end); + end++; + while (cur[end] != 0) { + if ((cur[end] == '\'') || (cur[end] == '"')) { + end = skipString(cur, end); + if (end <= 0) + return(-1); + continue; + } else if (cur[end] == '[') { + end = skipPredicate(cur, end); + if (end <= 0) + return(-1); + continue; + } else if (cur[end] == ']') + return(end + 1); + end++; + } + return(-1); +} + +/** + * xsltAddKey: + * @style: an XSLT stylesheet + * @name: the key name or NULL + * @nameURI: the name URI or NULL + * @match: the match value + * @use: the use value + * @inst: the key instruction + * + * add a key definition to a stylesheet + * + * Returns 0 in case of success, and -1 in case of failure. + */ +int +xsltAddKey(xsltStylesheetPtr style, const xmlChar *name, + const xmlChar *nameURI, const xmlChar *match, + const xmlChar *use, xmlNodePtr inst) { + xsltKeyDefPtr key; + xmlChar *pattern = NULL; + int current, end, start, i = 0; + + if ((style == NULL) || (name == NULL) || (match == NULL) || (use == NULL)) + return(-1); + +#ifdef WITH_XSLT_DEBUG_KEYS + xsltGenericDebug(xsltGenericDebugContext, + "Add key %s, match %s, use %s\n", name, match, use); +#endif + + key = xsltNewKeyDef(name, nameURI); + key->match = xmlStrdup(match); + key->use = xmlStrdup(use); + key->inst = inst; + key->nsList = xmlGetNsList(inst->doc, inst); + if (key->nsList != NULL) { + while (key->nsList[i] != NULL) + i++; + } + key->nsNr = i; + + /* + * Split the | and register it as as many keys + */ + current = end = 0; + while (match[current] != 0) { + start = current; + while (IS_BLANK_CH(match[current])) + current++; + end = current; + while ((match[end] != 0) && (match[end] != '|')) { + if (match[end] == '[') { + end = skipPredicate(match, end); + if (end <= 0) { + xsltTransformError(NULL, style, inst, + "xsl:key : 'match' pattern is malformed: %s", + key->match); + if (style != NULL) style->errors++; + goto error; + } + } else + end++; + } + if (current == end) { + xsltTransformError(NULL, style, inst, + "xsl:key : 'match' pattern is empty\n"); + if (style != NULL) style->errors++; + goto error; + } + if (match[start] != '/') { + pattern = xmlStrcat(pattern, (xmlChar *)"//"); + if (pattern == NULL) { + if (style != NULL) style->errors++; + goto error; + } + } + pattern = xmlStrncat(pattern, &match[start], end - start); + if (pattern == NULL) { + if (style != NULL) style->errors++; + goto error; + } + + if (match[end] == '|') { + pattern = xmlStrcat(pattern, (xmlChar *)"|"); + end++; + } + current = end; + } + if (pattern == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:key : 'match' pattern is empty\n"); + if (style != NULL) style->errors++; + goto error; + } +#ifdef WITH_XSLT_DEBUG_KEYS + xsltGenericDebug(xsltGenericDebugContext, + " resulting pattern %s\n", pattern); +#endif + /* + * XSLT-1: "It is an error for the value of either the use + * attribute or the match attribute to contain a + * VariableReference." + * TODO: We should report a variable-reference at compile-time. + * Maybe a search for "$", if it occurs outside of quotation + * marks, could be sufficient. + */ +#ifdef XML_XPATH_NOVAR + key->comp = xsltXPathCompileFlags(style, pattern, XML_XPATH_NOVAR); +#else + key->comp = xsltXPathCompile(style, pattern); +#endif + if (key->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:key : 'match' pattern compilation failed '%s'\n", + pattern); + if (style != NULL) style->errors++; + } +#ifdef XML_XPATH_NOVAR + key->usecomp = xsltXPathCompileFlags(style, use, XML_XPATH_NOVAR); +#else + key->usecomp = xsltXPathCompile(style, use); +#endif + if (key->usecomp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:key : 'use' expression compilation failed '%s'\n", + use); + if (style != NULL) style->errors++; + } + + /* + * Sometimes the stylesheet writer use the order to ease the + * resolution of keys when they are dependant, keep the provided + * order so add the new one at the end. + */ + if (style->keys == NULL) { + style->keys = key; + } else { + xsltKeyDefPtr prev = style->keys; + + while (prev->next != NULL) + prev = prev->next; + + prev->next = key; + } + key->next = NULL; + +error: + if (pattern != NULL) + xmlFree(pattern); + return(0); +} + +/** + * xsltGetKey: + * @ctxt: an XSLT transformation context + * @name: the key name or NULL + * @nameURI: the name URI or NULL + * @value: the key value to look for + * + * Looks up a key of the in current source doc (the document info + * on @ctxt->document). Computes the key if not already done + * for the current source doc. + * + * Returns the nodeset resulting from the query or NULL + */ +xmlNodeSetPtr +xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name, + const xmlChar *nameURI, const xmlChar *value) { + xmlNodeSetPtr ret; + xsltKeyTablePtr table; + int init_table = 0; + + if ((ctxt == NULL) || (name == NULL) || (value == NULL) || + (ctxt->document == NULL)) + return(NULL); + +#ifdef WITH_XSLT_DEBUG_KEYS + xsltGenericDebug(xsltGenericDebugContext, + "Get key %s, value %s\n", name, value); +#endif + + /* + * keys are computed only on-demand on first key access for a document + */ + if ((ctxt->document->nbKeysComputed < ctxt->nbKeys) && + (ctxt->keyInitLevel == 0)) { + /* + * If non-recursive behaviour, just try to initialize all keys + */ + if (xsltInitAllDocKeys(ctxt)) + return(NULL); + } + +retry: + table = (xsltKeyTablePtr) ctxt->document->keys; + while (table != NULL) { + if (((nameURI != NULL) == (table->nameURI != NULL)) && + xmlStrEqual(table->name, name) && + xmlStrEqual(table->nameURI, nameURI)) + { + ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value); + return(ret); + } + table = table->next; + } + + if ((ctxt->keyInitLevel != 0) && (init_table == 0)) { + /* + * Apparently one key is recursive and this one is needed, + * initialize just it, that time and retry + */ + xsltInitDocKeyTable(ctxt, name, nameURI); + init_table = 1; + goto retry; + } + + return(NULL); +} + + +/** + * xsltInitDocKeyTable: + * + * INTERNAL ROUTINE ONLY + * + * Check if any keys on the current document need to be computed + */ +static int +xsltInitDocKeyTable(xsltTransformContextPtr ctxt, const xmlChar *name, + const xmlChar *nameURI) +{ + xsltStylesheetPtr style; + xsltKeyDefPtr keyd = NULL; + int found = 0; + +#ifdef KEY_INIT_DEBUG +fprintf(stderr, "xsltInitDocKeyTable %s\n", name); +#endif + + style = ctxt->style; + while (style != NULL) { + keyd = (xsltKeyDefPtr) style->keys; + while (keyd != NULL) { + if (((keyd->nameURI != NULL) == + (nameURI != NULL)) && + xmlStrEqual(keyd->name, name) && + xmlStrEqual(keyd->nameURI, nameURI)) + { + xsltInitCtxtKey(ctxt, ctxt->document, keyd); + if (ctxt->document->nbKeysComputed == ctxt->nbKeys) + return(0); + found = 1; + } + keyd = keyd->next; + } + style = xsltNextImport(style); + } + if (found == 0) { +#ifdef WITH_XSLT_DEBUG_KEYS + XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, + "xsltInitDocKeyTable: did not found %s\n", name)); +#endif + xsltTransformError(ctxt, NULL, keyd? keyd->inst : NULL, + "Failed to find key definition for %s\n", name); + ctxt->state = XSLT_STATE_STOPPED; + return(-1); + } +#ifdef KEY_INIT_DEBUG +fprintf(stderr, "xsltInitDocKeyTable %s done\n", name); +#endif + return(0); +} + +/** + * xsltInitAllDocKeys: + * @ctxt: transformation context + * + * INTERNAL ROUTINE ONLY + * + * Check if any keys on the current document need to be computed + * + * Returns 0 in case of success, -1 in case of failure + */ +int +xsltInitAllDocKeys(xsltTransformContextPtr ctxt) +{ + xsltStylesheetPtr style; + xsltKeyDefPtr keyd; + xsltKeyTablePtr table; + + if (ctxt == NULL) + return(-1); + +#ifdef KEY_INIT_DEBUG +fprintf(stderr, "xsltInitAllDocKeys %d %d\n", + ctxt->document->nbKeysComputed, ctxt->nbKeys); +#endif + + if (ctxt->document->nbKeysComputed == ctxt->nbKeys) + return(0); + + + /* + * TODO: This could be further optimized + */ + style = ctxt->style; + while (style) { + keyd = (xsltKeyDefPtr) style->keys; + while (keyd != NULL) { +#ifdef KEY_INIT_DEBUG +fprintf(stderr, "Init key %s\n", keyd->name); +#endif + /* + * Check if keys with this QName have been already + * computed. + */ + table = (xsltKeyTablePtr) ctxt->document->keys; + while (table) { + if (((keyd->nameURI != NULL) == (table->nameURI != NULL)) && + xmlStrEqual(keyd->name, table->name) && + xmlStrEqual(keyd->nameURI, table->nameURI)) + { + break; + } + table = table->next; + } + if (table == NULL) { + /* + * Keys with this QName have not been yet computed. + */ + xsltInitDocKeyTable(ctxt, keyd->name, keyd->nameURI); + } + keyd = keyd->next; + } + style = xsltNextImport(style); + } +#ifdef KEY_INIT_DEBUG +fprintf(stderr, "xsltInitAllDocKeys: done\n"); +#endif + return(0); +} + +/** + * xsltInitCtxtKey: + * @ctxt: an XSLT transformation context + * @idoc: the document information (holds key values) + * @keyDef: the key definition + * + * Computes the key tables this key and for the current input document. + * + * Returns: 0 on success, -1 on error + */ +int +xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc, + xsltKeyDefPtr keyDef) +{ + int i, len, k; + xmlNodeSetPtr matchList = NULL, keylist; + xmlXPathObjectPtr matchRes = NULL, useRes = NULL; + xmlChar *str = NULL; + xsltKeyTablePtr table; + xmlNodePtr oldInst, cur; + xmlNodePtr oldContextNode; + xsltDocumentPtr oldDocInfo; + int oldXPPos, oldXPSize; + xmlDocPtr oldXPDoc; + int oldXPNsNr; + xmlNsPtr *oldXPNamespaces; + xmlXPathContextPtr xpctxt; + +#ifdef KEY_INIT_DEBUG +fprintf(stderr, "xsltInitCtxtKey %s : %d\n", keyDef->name, ctxt->keyInitLevel); +#endif + + if ((keyDef->comp == NULL) || (keyDef->usecomp == NULL)) + return(-1); + + /* + * Detect recursive keys + */ + if (ctxt->keyInitLevel > ctxt->nbKeys) { +#ifdef WITH_XSLT_DEBUG_KEYS + XSLT_TRACE(ctxt,XSLT_TRACE_KEYS, + xsltGenericDebug(xsltGenericDebugContext, + "xsltInitCtxtKey: key definition of %s is recursive\n", + keyDef->name)); +#endif + xsltTransformError(ctxt, NULL, keyDef->inst, + "Key definition for %s is recursive\n", keyDef->name); + ctxt->state = XSLT_STATE_STOPPED; + return(-1); + } + ctxt->keyInitLevel++; + + xpctxt = ctxt->xpathCtxt; + idoc->nbKeysComputed++; + /* + * Save context state. + */ + oldInst = ctxt->inst; + oldDocInfo = ctxt->document; + oldContextNode = ctxt->node; + + oldXPDoc = xpctxt->doc; + oldXPPos = xpctxt->proximityPosition; + oldXPSize = xpctxt->contextSize; + oldXPNsNr = xpctxt->nsNr; + oldXPNamespaces = xpctxt->namespaces; + + /* + * Set up contexts. + */ + ctxt->document = idoc; + ctxt->node = (xmlNodePtr) idoc->doc; + ctxt->inst = keyDef->inst; + + xpctxt->doc = idoc->doc; + xpctxt->node = (xmlNodePtr) idoc->doc; + /* TODO : clarify the use of namespaces in keys evaluation */ + xpctxt->namespaces = keyDef->nsList; + xpctxt->nsNr = keyDef->nsNr; + + /* + * Evaluate the 'match' expression of the xsl:key. + * TODO: The 'match' is a *pattern*. + */ + matchRes = xmlXPathCompiledEval(keyDef->comp, xpctxt); + if (matchRes == NULL) { + +#ifdef WITH_XSLT_DEBUG_KEYS + XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, + "xsltInitCtxtKey: %s evaluation failed\n", keyDef->match)); +#endif + xsltTransformError(ctxt, NULL, keyDef->inst, + "Failed to evaluate the 'match' expression.\n"); + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } else { + if (matchRes->type == XPATH_NODESET) { + matchList = matchRes->nodesetval; + +#ifdef WITH_XSLT_DEBUG_KEYS + if (matchList != NULL) + XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, + "xsltInitCtxtKey: %s evaluates to %d nodes\n", + keyDef->match, matchList->nodeNr)); +#endif + } else { + /* + * Is not a node set, but must be. + */ +#ifdef WITH_XSLT_DEBUG_KEYS + XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, + "xsltInitCtxtKey: %s is not a node set\n", keyDef->match)); +#endif + xsltTransformError(ctxt, NULL, keyDef->inst, + "The 'match' expression did not evaluate to a node set.\n"); + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + } + if ((matchList == NULL) || (matchList->nodeNr <= 0)) + goto exit; + + /** + * Multiple key definitions for the same name are allowed, so + * we must check if the key is already present for this doc + */ + table = (xsltKeyTablePtr) idoc->keys; + while (table != NULL) { + if (xmlStrEqual(table->name, keyDef->name) && + (((keyDef->nameURI == NULL) && (table->nameURI == NULL)) || + ((keyDef->nameURI != NULL) && (table->nameURI != NULL) && + (xmlStrEqual(table->nameURI, keyDef->nameURI))))) + break; + table = table->next; + } + /** + * If the key was not previously defined, create it now and + * chain it to the list of keys for the doc + */ + if (table == NULL) { + table = xsltNewKeyTable(keyDef->name, keyDef->nameURI); + if (table == NULL) + goto error; + table->next = idoc->keys; + idoc->keys = table; + } + + /* + * SPEC XSLT 1.0 (XSLT 2.0 does not clarify the context size!) + * "...the use attribute of the xsl:key element is evaluated with x as + " the current node and with a node list containing just x as the + * current node list" + */ + xpctxt->contextSize = 1; + xpctxt->proximityPosition = 1; + + for (i = 0; i < matchList->nodeNr; i++) { + cur = matchList->nodeTab[i]; + if (! IS_XSLT_REAL_NODE(cur)) + continue; + ctxt->node = cur; + xpctxt->node = cur; + /* + * Process the 'use' of the xsl:key. + * SPEC XSLT 1.0: + * "The use attribute is an expression specifying the values of + * the key; the expression is evaluated once for each node that + * matches the pattern." + */ + if (useRes != NULL) + xmlXPathFreeObject(useRes); + useRes = xmlXPathCompiledEval(keyDef->usecomp, xpctxt); + if (useRes == NULL) { + xsltTransformError(ctxt, NULL, keyDef->inst, + "Failed to evaluate the 'use' expression.\n"); + ctxt->state = XSLT_STATE_STOPPED; + break; + } + if (useRes->type == XPATH_NODESET) { + if ((useRes->nodesetval != NULL) && + (useRes->nodesetval->nodeNr != 0)) + { + len = useRes->nodesetval->nodeNr; + str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[0]); + } else { + continue; + } + } else { + len = 1; + if (useRes->type == XPATH_STRING) { + /* + * Consume the string value. + */ + str = useRes->stringval; + useRes->stringval = NULL; + } else { + str = xmlXPathCastToString(useRes); + } + } + /* + * Process all strings. + */ + k = 0; + while (1) { + if (str == NULL) + goto next_string; + +#ifdef WITH_XSLT_DEBUG_KEYS + XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, + "xsl:key : node associated to ('%s', '%s')\n", keyDef->name, str)); +#endif + + keylist = xmlHashLookup(table->keys, str); + if (keylist == NULL) { + keylist = xmlXPathNodeSetCreate(cur); + if (keylist == NULL) + goto error; + xmlHashAddEntry(table->keys, str, keylist); + } else { + /* + * TODO: How do we know if this function failed? + */ + xmlXPathNodeSetAdd(keylist, cur); + } + switch (cur->type) { + case XML_ELEMENT_NODE: + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + cur->psvi = keyDef; + break; + case XML_ATTRIBUTE_NODE: + ((xmlAttrPtr) cur)->psvi = keyDef; + break; + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + ((xmlDocPtr) cur)->psvi = keyDef; + break; + default: + break; + } + xmlFree(str); + str = NULL; + +next_string: + k++; + if (k >= len) + break; + str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[k]); + } + } + +exit: +error: + ctxt->keyInitLevel--; + /* + * Restore context state. + */ + xpctxt->doc = oldXPDoc; + xpctxt->nsNr = oldXPNsNr; + xpctxt->namespaces = oldXPNamespaces; + xpctxt->proximityPosition = oldXPPos; + xpctxt->contextSize = oldXPSize; + + ctxt->node = oldContextNode; + ctxt->document = oldDocInfo; + ctxt->inst = oldInst; + + if (str) + xmlFree(str); + if (useRes != NULL) + xmlXPathFreeObject(useRes); + if (matchRes != NULL) + xmlXPathFreeObject(matchRes); + return(0); +} + +/** + * xsltInitCtxtKeys: + * @ctxt: an XSLT transformation context + * @idoc: a document info + * + * Computes all the keys tables for the current input document. + * Should be done before global varibales are initialized. + * NOTE: Not used anymore in the refactored code. + */ +void +xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc) { + xsltStylesheetPtr style; + xsltKeyDefPtr keyDef; + + if ((ctxt == NULL) || (idoc == NULL)) + return; + +#ifdef KEY_INIT_DEBUG +fprintf(stderr, "xsltInitCtxtKeys on document\n"); +#endif + +#ifdef WITH_XSLT_DEBUG_KEYS + if ((idoc->doc != NULL) && (idoc->doc->URL != NULL)) + XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n", + idoc->doc->URL)); +#endif + style = ctxt->style; + while (style != NULL) { + keyDef = (xsltKeyDefPtr) style->keys; + while (keyDef != NULL) { + xsltInitCtxtKey(ctxt, idoc, keyDef); + + keyDef = keyDef->next; + } + + style = xsltNextImport(style); + } + +#ifdef KEY_INIT_DEBUG +fprintf(stderr, "xsltInitCtxtKeys on document: done\n"); +#endif + +} + +/** + * xsltFreeDocumentKeys: + * @idoc: a XSLT document + * + * Free the keys associated to a document + */ +void +xsltFreeDocumentKeys(xsltDocumentPtr idoc) { + if (idoc != NULL) + xsltFreeKeyTableList(idoc->keys); +} + diff --git a/libxslt/keys.h b/libxslt/keys.h new file mode 100644 index 0000000..757d122 --- /dev/null +++ b/libxslt/keys.h @@ -0,0 +1,53 @@ +/* + * Summary: interface for the key matching used in key() and template matches. + * Description: implementation of the key mechanims. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_KEY_H__ +#define __XML_XSLT_KEY_H__ + +#include <libxml/xpath.h> +#include "xsltexports.h" +#include "xsltInternals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * NODE_IS_KEYED: + * + * check for bit 15 set + */ +#define NODE_IS_KEYED (1 >> 15) + +XSLTPUBFUN int XSLTCALL + xsltAddKey (xsltStylesheetPtr style, + const xmlChar *name, + const xmlChar *nameURI, + const xmlChar *match, + const xmlChar *use, + xmlNodePtr inst); +XSLTPUBFUN xmlNodeSetPtr XSLTCALL + xsltGetKey (xsltTransformContextPtr ctxt, + const xmlChar *name, + const xmlChar *nameURI, + const xmlChar *value); +XSLTPUBFUN void XSLTCALL + xsltInitCtxtKeys (xsltTransformContextPtr ctxt, + xsltDocumentPtr doc); +XSLTPUBFUN void XSLTCALL + xsltFreeKeys (xsltStylesheetPtr style); +XSLTPUBFUN void XSLTCALL + xsltFreeDocumentKeys (xsltDocumentPtr doc); + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_H__ */ + diff --git a/libxslt/libxslt.3 b/libxslt/libxslt.3 new file mode 100644 index 0000000..06f256d --- /dev/null +++ b/libxslt/libxslt.3 @@ -0,0 +1,31 @@ +.TH libxslt 3 "30 August 2001" +.SH NAME +libxslt \- library used to do XSL transformations on XML documents +.SH DESCRIPTION +The +.I libxslt +library is used to do XSL transformations on XML documents that +have been loaded into memory with functions from +.I libxml. +.LP +.SH FILES +.TP 2.2i +.B /usr/lib/libxslt_1.0.0/libxslt.a +static library +.TP +.B /usr/lib/libxslt_1.0.0/libxslt.so +sharable library +.TP +.B /usr/package/libxslt_1.0.0/bin/xsltproc +binary application to do XSL transformations on the command line +.SH AUTHORS +Daniel Veillard (daniel@veillard.com). +If you download and install this package look at instructions on the +Web site http://xmlsoft.org/XSLT/ . +Manual page by Heiko W. Rupp (hwr@pilhuhn.de) +.SH SEE ALSO +.IR libexslt (3), +.IR libxml (3), +.IR xsltproc (1), +.IR xmllint (1) +.\" end of manual page diff --git a/libxslt/libxslt.h b/libxslt/libxslt.h new file mode 100644 index 0000000..e6d4c83 --- /dev/null +++ b/libxslt/libxslt.h @@ -0,0 +1,36 @@ +/* + * Summary: internal header only used during the compilation of libxslt + * Description: internal header only used during the compilation of libxslt + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XSLT_LIBXSLT_H__ +#define __XSLT_LIBXSLT_H__ + +#if defined(WIN32) && !defined (__CYGWIN__) && !defined (__MINGW32__) +#include <win32config.h> +#else +#include "config.h" +#endif + +#include <libxslt/xsltconfig.h> +#include <libxml/xmlversion.h> + +#if !defined LIBXSLT_PUBLIC +#if (defined (__CYGWIN__) || defined _MSC_VER) && !defined IN_LIBXSLT && !defined LIBXSLT_STATIC +#define LIBXSLT_PUBLIC __declspec(dllimport) +#else +#define LIBXSLT_PUBLIC +#endif +#endif + +#if defined(_MSC_VER) || defined(__MINGW32__) +#include <io.h> +#include <direct.h> +#define mkdir(p,m) _mkdir(p) +#endif + +#endif /* ! __XSLT_LIBXSLT_H__ */ diff --git a/libxslt/libxslt.syms b/libxslt/libxslt.syms new file mode 100644 index 0000000..3d9b5c6 --- /dev/null +++ b/libxslt/libxslt.syms @@ -0,0 +1,490 @@ +# +# Officially exported symbols, for which header +# file definitions are installed in /usr/include/libxslt +# +# Automatically generated from symbols.xml and syms.xsl +# +# Versions here are *fixed* to match the libxslt version +# at which the symbol was introduced. This ensures that +# a new client app requiring symbol foo() can't accidentally +# run with old libxslt.so not providing foo() - the global +# soname version info can't enforce this since we never +# change the soname +# + +LIBXML2_1.0.11 { + global: + +# attributes + xsltApplyAttributeSet; + xsltFreeAttributeSetsHashes; + xsltParseStylesheetAttributeSet; + +# documents + xsltFindDocument; + xsltFreeDocuments; + xsltFreeStyleDocuments; + xsltLoadDocument; + xsltLoadStyleDocument; + xsltNewDocument; + xsltNewStyleDocument; + +# extensions + xsltCheckExtPrefix; + xsltExtElementLookup; + xsltExtModuleElementLookup; + xsltExtModuleFunctionLookup; + xsltExtModuleTopLevelLookup; + xsltFreeCtxtExts; + xsltFreeExts; + xsltGetExtData; + xsltInitCtxtExts; + xsltInitElemPreComp; + xsltNewElemPreComp; + xsltPreComputeExtModuleElement; + xsltRegisterExtElement; + xsltRegisterExtFunction; + xsltRegisterExtModuleElement; + xsltRegisterExtModuleFull; + xsltRegisterExtModuleFunction; + xsltRegisterExtModule; + xsltRegisterExtModuleTopLevel; + xsltRegisterExtPrefix; + xsltRegisterTestModule; + xsltShutdownCtxtExts; + xsltShutdownExts; + xsltStyleGetExtData; + xsltUnregisterExtModuleElement; + xsltUnregisterExtModuleFunction; + xsltUnregisterExtModule; + xsltUnregisterExtModuleTopLevel; + +# extra + xsltDebug; + xsltFunctionNodeSet; + xsltRegisterAllExtras; + xsltRegisterExtras; + +# functions + xsltDocumentFunction; + xsltElementAvailableFunction; + xsltFormatNumberFunction; + xsltFunctionAvailableFunction; + xsltGenerateIdFunction; + xsltKeyFunction; + xsltRegisterAllFunctions; + xsltSystemPropertyFunction; + xsltUnparsedEntityURIFunction; + xsltXPathFunctionLookup; + +# imports + xsltFindElemSpaceHandling; + xsltFindTemplate; + xsltNeedElemSpaceHandling; + xsltNextImport; + xsltParseStylesheetImport; + xsltParseStylesheetInclude; + +# keys + xsltAddKey; + xsltFreeDocumentKeys; + xsltFreeKeys; + xsltGetKey; + xsltInitCtxtKeys; + +# namespaces + xsltCopyNamespaceList; + xsltCopyNamespace; + xsltFreeNamespaceAliasHashes; + xsltGetNamespace; + xsltGetSpecialNamespace; + xsltNamespaceAlias; + +# pattern + xsltAddTemplate; + xsltCleanupTemplates; + xsltCompilePattern; + xsltFreeCompMatchList; + xsltFreeTemplateHashes; + xsltGetTemplate; + xsltMatchPattern; + xsltTestCompMatchList; + +# preproc + xsltDocumentComp; + xsltFreeStylePreComps; + xsltStylePreCompute; + +# templates + xsltAttrListTemplateProcess; + xsltAttrTemplateProcess; + xsltAttrTemplateValueProcess; + xsltEvalAttrValueTemplate; + xsltEvalStaticAttrValueTemplate; + xsltEvalTemplateString; + xsltEvalXPathPredicate; + xsltEvalXPathString; + xsltTemplateProcess; + +# transform + xslHandleDebugger; + xsltApplyImports; + xsltApplyOneTemplate; + xsltApplyStripSpaces; + xsltApplyStylesheet; + xsltApplyStylesheetUser; + xsltApplyTemplates; + xsltAttribute; + xsltCallTemplate; + xsltChoose; + xsltComment; + xsltCopyOf; + xsltCopy; + xsltDocumentElem; + xsltElement; + xsltForEach; + xsltFreeTransformContext; + xsltGetXIncludeDefault; + xsltIf; + xsltNewTransformContext; + xsltNumber; + xsltProcessingInstruction; + xsltProfileStylesheet; + xsltRegisterAllElement; + xsltRunStylesheet; + xsltSetXIncludeDefault; + xsltSort; + xsltText; + xsltValueOf; + +# variables + xsltAddStackElemList; + xsltEvalGlobalVariables; + xsltEvalOneUserParam; + xsltEvalUserParams; + xsltFreeGlobalVariables; + xsltParseGlobalParam; + xsltParseGlobalVariable; + xsltParseStylesheetCallerParam; + xsltParseStylesheetParam; + xsltParseStylesheetVariable; + xsltQuoteOneUserParam; + xsltQuoteUserParams; + xsltVariableLookup; + xsltXPathVariableLookup; + +# xsltInternals + xsltDecimalFormatGetByName; + xsltFormatNumberConversion; + xsltFreeStackElemList; + xsltFreeStylesheet; + xsltIsBlank; + xsltLoadStylesheetPI; + xsltNewStylesheet; + xsltNumberFormat; + xsltParseStylesheetDoc; + xsltParseStylesheetFile; + xsltParseStylesheetOutput; + xsltParseStylesheetProcess; + xsltParseTemplateContent; + +# xsltutils + xslAddCall; + xslDropCall; + xsltCalibrateAdjust; + xsltDocumentSortFunction; + xsltDoSortFunction; + xsltGetNsProp; + xsltGetQNameURI; + xsltMessage; + xsltPrintErrorContext; + xsltSaveProfiling; + xsltSaveResultToFd; + xsltSaveResultToFilename; + xsltSaveResultToFile; + xsltSaveResultTo; + xsltSetDebuggerCallbacks; + xsltSetGenericDebugFunc; + xsltSetGenericErrorFunc; + xsltTimestamp; + +# xslt + xsltCleanupGlobals; +} ; + +LIBXML2_1.0.12 { + global: + +# xsltInternals + xsltAllocateExtraCtxt; + xsltAllocateExtra; +} LIBXML2_1.0.11; + +LIBXML2_1.0.13 { + global: + +# extensions + xsltExtModuleElementPreComputeLookup; + xsltXPathGetTransformContext; +} LIBXML2_1.0.12; + +LIBXML2_1.0.16 { + global: + +# attributes + xsltResolveStylesheetAttributeSet; +} LIBXML2_1.0.13; + +LIBXML2_1.0.17 { + global: + +# transform + xsltRunStylesheetUser; +} LIBXML2_1.0.16; + +LIBXML2_1.0.18 { + global: + +# extensions + xsltDebugDumpExtensions; + +# xsltutils + xsltSaveResultToString; +} LIBXML2_1.0.17; + +LIBXML2_1.0.22 { + global: + +# templates + xsltAttrTemplateValueProcessNode; + +# security + xsltCheckRead; + xsltCheckWrite; + +# templates + xsltEvalXPathStringNs; + +# security + xsltFreeSecurityPrefs; + xsltGetDefaultSecurityPrefs; + xsltGetSecurityPrefs; + xsltNewSecurityPrefs; + xsltSecurityAllow; + xsltSecurityForbid; + xsltSetCtxtSecurityPrefs; + xsltSetDefaultSecurityPrefs; + xsltSetSecurityPrefs; + +# xsltutils + xsltSetTransformErrorFunc; + xsltTransformError; +} LIBXML2_1.0.18; + +LIBXML2_1.0.24 { + global: + +# xsltutils + xslDebugStatus; # variable + xsltComputeSortResult; + xsltDefaultSortFunction; + +# xslt + xsltEngineVersion; # variable + +# preproc + xsltExtMarker; # variable + +# xsltutils + xsltGenericDebugContext; # variable + xsltGenericDebug; # variable + xsltGenericErrorContext; # variable + xsltGenericError; # variable + xsltGetProfileInformation; + xsltGetUTF8Char; + +# xslt + xsltLibxmlVersion; # variable + xsltLibxsltVersion; # variable + xsltMaxDepth; # variable + xsltMaxVars; # variable + +# xsltInternals + xsltParseStylesheetImportedDoc; + +# xsltutils + xsltSetCtxtSortFunc; + xsltSetSortFunc; +} LIBXML2_1.0.22; + +LIBXML2_1.0.30 { + global: + +# xsltInternals + xsltCreateRVT; + xsltFreeRVTs; + xsltRegisterPersistRVT; + xsltRegisterTmpRVT; +} LIBXML2_1.0.24; + +LIBXML2_1.0.32 { + global: + +# transform + xsltCopyTextString; + +# extensions + xsltGetExtInfo; +} LIBXML2_1.0.30; + +LIBXML2_1.0.33 { + global: + +# pattern + xsltNormalizeCompSteps; +} LIBXML2_1.0.32; + +LIBXML2_1.1.0 { + global: + +# xsltutils + xsltGetDebuggerStatus; + xsltSetDebuggerStatus; +} LIBXML2_1.0.33; + +LIBXML2_1.1.1 { + global: + +# xsltutils + xsltDebugGetDefaultTrace; + xsltDebugSetDefaultTrace; +} LIBXML2_1.1.0; + +LIBXML2_1.1.2 { + global: + +# xsltutils + xsltSetCtxtParseOptions; +} LIBXML2_1.1.1; + +LIBXML2_1.1.3 { + global: + +# xsltInternals + xsltCompileAttr; + xsltEvalAVT; + xsltFreeAVTList; + +# xsltutils + xsltGetCNsProp; + xsltSplitQName; + xsltXPathCompile; +} LIBXML2_1.1.2; + +LIBXML2_1.1.5 { + global: + +# xsltutils + xsltGetQNameURI2; +} LIBXML2_1.1.3; + +LIBXML2_1.1.7 { + global: + +# namespaces + xsltGetPlainNamespace; +} LIBXML2_1.1.5; + +LIBXML2_1.1.9 { + global: + +# documents + xsltDocDefaultLoader; # variable + xsltSetLoaderFunc; +} LIBXML2_1.1.7; + +LIBXML2_1.1.18 { + global: + +# xsltInternals + xsltConstNamespaceNameXSLT; # variable + xsltExtensionInstructionResultFinalize; + xsltExtensionInstructionResultRegister; + xsltInitCtxtKey; + +# xslt + xsltInit; + +# xsltInternals + xsltParseAnyXSLTElem; + xsltParseSequenceConstructor; + xsltPointerListAddSize; + xsltPointerListClear; + xsltPointerListCreate; + xsltPointerListFree; + xsltRegisterLocalRVT; + xsltReleaseRVT; + xsltRestoreDocumentNamespaces; + +# extensions + xsltStyleStylesheetLevelGetExtData; + +# xsltInternals +# xsltTransStorageAdd; removed in 1.1.28 +# xsltTransStorageRemove; removed in 1.1.28 + xsltUninit; + xsltXSLTAttrMarker; # variable +} LIBXML2_1.1.9; + +LIBXML2_1.1.20 { + global: + +# transform + xsltLocalVariablePop; + xsltLocalVariablePush; +} LIBXML2_1.1.18; + +LIBXML2_1.1.23 { + global: + +# xsltInternals + xsltInitAllDocKeys; +} LIBXML2_1.1.20; + +LIBXML2_1.1.24 { + global: + +# extensions + xsltCheckExtURI; +} LIBXML2_1.1.23; + +LIBXML2_1.1.25 { + global: + +# xsltlocale + xsltFreeLocale; + xsltLocaleStrcmp; + xsltNewLocale; + xsltStrxfrm; + +# extensions + xsltInitGlobals; +} LIBXML2_1.1.24; + +LIBXML2_1.1.26 { + global: + +# transform + xsltProcessOneNode; +} LIBXML2_1.1.25; + +LIBXML2_1.1.27 { + global: + +# xsltlocale + xsltFreeLocales; + +# xsltutils + xsltXPathCompileFlags; +} LIBXML2_1.1.26; + diff --git a/libxslt/namespaces.c b/libxslt/namespaces.c new file mode 100644 index 0000000..07a7705 --- /dev/null +++ b/libxslt/namespaces.c @@ -0,0 +1,853 @@ +/* + * namespaces.c: Implementation of the XSLT namespaces handling + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_MATH_H +#include <math.h> +#endif +#ifdef HAVE_FLOAT_H +#include <float.h> +#endif +#ifdef HAVE_IEEEFP_H +#include <ieeefp.h> +#endif +#ifdef HAVE_NAN_H +#include <nan.h> +#endif +#ifdef HAVE_CTYPE_H +#include <ctype.h> +#endif +#ifndef XSLT_NEED_TRIO +#include <stdio.h> +#else +#include <trio.h> +#endif + +#include <libxml/xmlmemory.h> +#include <libxml/tree.h> +#include <libxml/hash.h> +#include <libxml/xmlerror.h> +#include <libxml/uri.h> +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "namespaces.h" +#include "imports.h" + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +#ifdef XSLT_REFACTORED +static xsltNsAliasPtr +xsltNewNsAlias(xsltCompilerCtxtPtr cctxt) +{ + xsltNsAliasPtr ret; + + if (cctxt == NULL) + return(NULL); + + ret = (xsltNsAliasPtr) xmlMalloc(sizeof(xsltNsAlias)); + if (ret == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "Internal error in xsltNewNsAlias(): Memory allocation failed.\n"); + cctxt->style->errors++; + return(NULL); + } + memset(ret, 0, sizeof(xsltNsAlias)); + /* + * TODO: Store the item at current stylesheet-level. + */ + ret->next = cctxt->nsAliases; + cctxt->nsAliases = ret; + + return(ret); +} +#endif /* XSLT_REFACTORED */ +/** + * xsltNamespaceAlias: + * @style: the XSLT stylesheet + * @node: the xsl:namespace-alias node + * + * Read the stylesheet-prefix and result-prefix attributes, register + * them as well as the corresponding namespace. + */ +void +xsltNamespaceAlias(xsltStylesheetPtr style, xmlNodePtr node) +{ + xmlChar *resultPrefix = NULL; + xmlChar *stylePrefix = NULL; + xmlNsPtr literalNs = NULL; + xmlNsPtr targetNs = NULL; + +#ifdef XSLT_REFACTORED + xsltNsAliasPtr alias; + + if ((style == NULL) || (node == NULL)) + return; + + /* + * SPEC XSLT 1.0: + * "If a namespace URI is declared to be an alias for multiple + * different namespace URIs, then the declaration with the highest + * import precedence is used. It is an error if there is more than + * one such declaration. An XSLT processor may signal the error; + * if it does not signal the error, it must recover by choosing, + * from amongst the declarations with the highest import precedence, + * the one that occurs last in the stylesheet." + * + * SPEC TODO: Check for the errors mentioned above. + */ + /* + * NOTE that the XSLT 2.0 also *does* use the NULL namespace if + * "#default" is used and there's no default namespace is scope. + * I.e., this is *not* an error. + * Most XSLT 1.0 implementations work this way. + * The XSLT 1.0 spec has nothing to say on the subject. + */ + /* + * Attribute "stylesheet-prefix". + */ + stylePrefix = xmlGetNsProp(node, (const xmlChar *)"stylesheet-prefix", NULL); + if (stylePrefix == NULL) { + xsltTransformError(NULL, style, node, + "The attribute 'stylesheet-prefix' is missing.\n"); + return; + } + if (xmlStrEqual(stylePrefix, (const xmlChar *)"#default")) + literalNs = xmlSearchNs(node->doc, node, NULL); + else { + literalNs = xmlSearchNs(node->doc, node, stylePrefix); + if (literalNs == NULL) { + xsltTransformError(NULL, style, node, + "Attribute 'stylesheet-prefix': There's no namespace " + "declaration in scope for the prefix '%s'.\n", + stylePrefix); + goto error; + } + } + /* + * Attribute "result-prefix". + */ + resultPrefix = xmlGetNsProp(node, (const xmlChar *)"result-prefix", NULL); + if (resultPrefix == NULL) { + xsltTransformError(NULL, style, node, + "The attribute 'result-prefix' is missing.\n"); + goto error; + } + if (xmlStrEqual(resultPrefix, (const xmlChar *)"#default")) + targetNs = xmlSearchNs(node->doc, node, NULL); + else { + targetNs = xmlSearchNs(node->doc, node, resultPrefix); + + if (targetNs == NULL) { + xsltTransformError(NULL, style, node, + "Attribute 'result-prefix': There's no namespace " + "declaration in scope for the prefix '%s'.\n", + stylePrefix); + goto error; + } + } + /* + * + * Same alias for multiple different target namespace URIs: + * TODO: The one with the highest import precedence is used. + * Example: + * <xsl:namespace-alias stylesheet-prefix="foo" + * result-prefix="bar"/> + * + * <xsl:namespace-alias stylesheet-prefix="foo" + * result-prefix="zar"/> + * + * Same target namespace URI for multiple different aliases: + * All alias-definitions will be used. + * Example: + * <xsl:namespace-alias stylesheet-prefix="bar" + * result-prefix="foo"/> + * + * <xsl:namespace-alias stylesheet-prefix="zar" + * result-prefix="foo"/> + * Cases using #default: + * <xsl:namespace-alias stylesheet-prefix="#default" + * result-prefix="#default"/> + * TODO: Has this an effect at all? + * + * <xsl:namespace-alias stylesheet-prefix="foo" + * result-prefix="#default"/> + * From namespace to no namespace. + * + * <xsl:namespace-alias stylesheet-prefix="#default" + * result-prefix="foo"/> + * From no namespace to namespace. + */ + + + /* + * Store the ns-node in the alias-object. + */ + alias = xsltNewNsAlias(XSLT_CCTXT(style)); + if (alias == NULL) + return; + alias->literalNs = literalNs; + alias->targetNs = targetNs; + XSLT_CCTXT(style)->hasNsAliases = 1; + + +#else /* XSLT_REFACTORED */ + const xmlChar *literalNsName; + const xmlChar *targetNsName; + + + if ((style == NULL) || (node == NULL)) + return; + + stylePrefix = xmlGetNsProp(node, (const xmlChar *)"stylesheet-prefix", NULL); + if (stylePrefix == NULL) { + xsltTransformError(NULL, style, node, + "namespace-alias: stylesheet-prefix attribute missing\n"); + return; + } + resultPrefix = xmlGetNsProp(node, (const xmlChar *)"result-prefix", NULL); + if (resultPrefix == NULL) { + xsltTransformError(NULL, style, node, + "namespace-alias: result-prefix attribute missing\n"); + goto error; + } + + if (xmlStrEqual(stylePrefix, (const xmlChar *)"#default")) { + literalNs = xmlSearchNs(node->doc, node, NULL); + if (literalNs == NULL) { + literalNsName = NULL; + } else + literalNsName = literalNs->href; /* Yes - set for nsAlias table */ + } else { + literalNs = xmlSearchNs(node->doc, node, stylePrefix); + + if ((literalNs == NULL) || (literalNs->href == NULL)) { + xsltTransformError(NULL, style, node, + "namespace-alias: prefix %s not bound to any namespace\n", + stylePrefix); + goto error; + } else + literalNsName = literalNs->href; + } + + /* + * When "#default" is used for result, if a default namespace has not + * been explicitly declared the special value UNDEFINED_DEFAULT_NS is + * put into the nsAliases table + */ + if (xmlStrEqual(resultPrefix, (const xmlChar *)"#default")) { + targetNs = xmlSearchNs(node->doc, node, NULL); + if (targetNs == NULL) { + targetNsName = UNDEFINED_DEFAULT_NS; + } else + targetNsName = targetNs->href; + } else { + targetNs = xmlSearchNs(node->doc, node, resultPrefix); + + if ((targetNs == NULL) || (targetNs->href == NULL)) { + xsltTransformError(NULL, style, node, + "namespace-alias: prefix %s not bound to any namespace\n", + resultPrefix); + goto error; + } else + targetNsName = targetNs->href; + } + /* + * Special case: if #default is used for + * the stylesheet-prefix (literal namespace) and there's no default + * namespace in scope, we'll use style->defaultAlias for this. + */ + if (literalNsName == NULL) { + if (targetNs != NULL) { + /* + * BUG TODO: Is it not sufficient to have only 1 field for + * this, since subsequently alias declarations will + * overwrite this. + * Example: + * <xsl:namespace-alias result-prefix="foo" + * stylesheet-prefix="#default"/> + * <xsl:namespace-alias result-prefix="bar" + * stylesheet-prefix="#default"/> + * The mapping for "foo" won't be visible anymore. + */ + style->defaultAlias = targetNs->href; + } + } else { + if (style->nsAliases == NULL) + style->nsAliases = xmlHashCreate(10); + if (style->nsAliases == NULL) { + xsltTransformError(NULL, style, node, + "namespace-alias: cannot create hash table\n"); + goto error; + } + xmlHashAddEntry((xmlHashTablePtr) style->nsAliases, + literalNsName, (void *) targetNsName); + } +#endif /* else of XSLT_REFACTORED */ + +error: + if (stylePrefix != NULL) + xmlFree(stylePrefix); + if (resultPrefix != NULL) + xmlFree(resultPrefix); +} + +/** + * xsltGetSpecialNamespace: + * @ctxt: the transformation context + * @invocNode: the invoking node; e.g. a literal result element/attr; + * only used for error reports + * @nsName: the namespace name (or NULL) + * @nsPrefix: the suggested namespace prefix (or NULL) + * @target: the result element on which to anchor a namespace + * + * Find a matching (prefix and ns-name) ns-declaration + * for the requested @nsName and @nsPrefix in the result tree. + * If none is found then a new ns-declaration will be + * added to @resultElem. If, in this case, the given prefix is + * already in use, then a ns-declaration with a modified ns-prefix + * be we created. Note that this function's priority is to + * preserve ns-prefixes; it will only change a prefix if there's + * a namespace clash. + * If both @nsName and @nsPrefix are NULL, then this will try to + * "undeclare" a default namespace by declaring an xmlns="". + * + * Returns a namespace declaration or NULL. + */ +xmlNsPtr +xsltGetSpecialNamespace(xsltTransformContextPtr ctxt, xmlNodePtr invocNode, + const xmlChar *nsName, const xmlChar *nsPrefix, + xmlNodePtr target) +{ + xmlNsPtr ns; + int prefixOccupied = 0; + + if ((ctxt == NULL) || (target == NULL) || + (target->type != XML_ELEMENT_NODE)) + return(NULL); + + /* + * NOTE: Namespace exclusion and ns-aliasing is performed at + * compilation-time in the refactored code; so this need not be done + * here (it was in the old code). + * NOTE: @invocNode was named @cur in the old code and was documented to + * be an input node; since it was only used to anchor an error report + * somewhere, we can safely change this to @invocNode, which now + * will be the XSLT instruction (also a literal result element/attribute), + * which was responsible for this call. + */ + /* + * OPTIMIZE TODO: This all could be optimized by keeping track of + * the ns-decls currently in-scope via a specialized context. + */ + if ((nsPrefix == NULL) && ((nsName == NULL) || (nsName[0] == 0))) { + /* + * NOTE: the "undeclaration" of the default namespace was + * part of the logic of the old xsltGetSpecialNamespace() code, + * so we'll keep that mechanism. + * Related to the old code: bug #302020: + */ + /* + * OPTIMIZE TODO: This all could be optimized by keeping track of + * the ns-decls currently in-scope via a specialized context. + */ + /* + * Search on the result element itself. + */ + if (target->nsDef != NULL) { + ns = target->nsDef; + do { + if (ns->prefix == NULL) { + if ((ns->href != NULL) && (ns->href[0] != 0)) { + /* + * Raise a namespace normalization error. + */ + xsltTransformError(ctxt, NULL, invocNode, + "Namespace normalization error: Cannot undeclare " + "the default namespace, since the default namespace " + "'%s' is already declared on the result element " + "'%s'.\n", ns->href, target->name); + return(NULL); + } else { + /* + * The default namespace was undeclared on the + * result element. + */ + return(NULL); + } + break; + } + ns = ns->next; + } while (ns != NULL); + } + if ((target->parent != NULL) && + (target->parent->type == XML_ELEMENT_NODE)) + { + /* + * The parent element is in no namespace, so assume + * that there is no default namespace in scope. + */ + if (target->parent->ns == NULL) + return(NULL); + + ns = xmlSearchNs(target->doc, target->parent, + NULL); + /* + * Fine if there's no default ns is scope, or if the + * default ns was undeclared. + */ + if ((ns == NULL) || (ns->href == NULL) || (ns->href[0] == 0)) + return(NULL); + + /* + * Undeclare the default namespace. + */ + xmlNewNs(target, BAD_CAST "", NULL); + /* TODO: Check result */ + return(NULL); + } + return(NULL); + } + /* + * Handle the XML namespace. + * QUESTION: Is this faster than using xmlStrEqual() anyway? + */ + if ((nsPrefix != NULL) && + (nsPrefix[0] == 'x') && (nsPrefix[1] == 'm') && + (nsPrefix[2] == 'l') && (nsPrefix[3] == 0)) + { + return(xmlSearchNs(target->doc, target, nsPrefix)); + } + /* + * First: search on the result element itself. + */ + if (target->nsDef != NULL) { + ns = target->nsDef; + do { + if ((ns->prefix == NULL) == (nsPrefix == NULL)) { + if (ns->prefix == nsPrefix) { + if (xmlStrEqual(ns->href, nsName)) + return(ns); + prefixOccupied = 1; + break; + } else if (xmlStrEqual(ns->prefix, nsPrefix)) { + if (xmlStrEqual(ns->href, nsName)) + return(ns); + prefixOccupied = 1; + break; + } + } + ns = ns->next; + } while (ns != NULL); + } + if (prefixOccupied) { + /* + * If the ns-prefix is occupied by an other ns-decl on the + * result element, then this means: + * 1) The desired prefix is shadowed + * 2) There's no way around changing the prefix + * + * Try a desperate search for an in-scope ns-decl + * with a matching ns-name before we use the last option, + * which is to recreate the ns-decl with a modified prefix. + */ + ns = xmlSearchNsByHref(target->doc, target, nsName); + if (ns != NULL) + return(ns); + + /* + * Fallback to changing the prefix. + */ + } else if ((target->parent != NULL) && + (target->parent->type == XML_ELEMENT_NODE)) + { + /* + * Try to find a matching ns-decl in the ancestor-axis. + * + * Check the common case: The parent element of the current + * result element is in the same namespace (with an equal ns-prefix). + */ + if ((target->parent->ns != NULL) && + ((target->parent->ns->prefix != NULL) == (nsPrefix != NULL))) + { + ns = target->parent->ns; + + if (nsPrefix == NULL) { + if (xmlStrEqual(ns->href, nsName)) + return(ns); + } else if (xmlStrEqual(ns->prefix, nsPrefix) && + xmlStrEqual(ns->href, nsName)) + { + return(ns); + } + } + /* + * Lookup the remaining in-scope namespaces. + */ + ns = xmlSearchNs(target->doc, target->parent, nsPrefix); + if (ns != NULL) { + if (xmlStrEqual(ns->href, nsName)) + return(ns); + /* + * Now check for a nasty case: We need to ensure that the new + * ns-decl won't shadow a prefix in-use by an existing attribute. + * <foo xmlns:a="urn:test:a"> + * <bar a:a="val-a"> + * <xsl:attribute xmlns:a="urn:test:b" name="a:b"> + * val-b</xsl:attribute> + * </bar> + * </foo> + */ + if (target->properties) { + xmlAttrPtr attr = target->properties; + do { + if ((attr->ns) && + xmlStrEqual(attr->ns->prefix, nsPrefix)) + { + /* + * Bad, this prefix is already in use. + * Since we'll change the prefix anyway, try + * a search for a matching ns-decl based on the + * namespace name. + */ + ns = xmlSearchNsByHref(target->doc, target, nsName); + if (ns != NULL) + return(ns); + goto declare_new_prefix; + } + attr = attr->next; + } while (attr != NULL); + } + } else { + /* + * Either no matching ns-prefix was found or the namespace is + * shadowed. + * Create a new ns-decl on the current result element. + * + * Hmm, we could also try to reuse an in-scope + * namespace with a matching ns-name but a different + * ns-prefix. + * What has higher priority? + * 1) If keeping the prefix: create a new ns-decl. + * 2) If reusal: first lookup ns-names; then fallback + * to creation of a new ns-decl. + * REVISIT: this currently uses case 1) although + * the old way was use xmlSearchNsByHref() and to let change + * the prefix. + */ +#if 0 + ns = xmlSearchNsByHref(target->doc, target, nsName); + if (ns != NULL) + return(ns); +#endif + } + /* + * Create the ns-decl on the current result element. + */ + ns = xmlNewNs(target, nsName, nsPrefix); + /* TODO: check errors */ + return(ns); + } else { + /* + * This is either the root of the tree or something weird is going on. + */ + ns = xmlNewNs(target, nsName, nsPrefix); + /* TODO: Check result */ + return(ns); + } + +declare_new_prefix: + /* + * Fallback: we need to generate a new prefix and declare the namespace + * on the result element. + */ + { + xmlChar pref[30]; + int counter = 1; + + if (nsPrefix == NULL) { + nsPrefix = BAD_CAST "ns"; + } + + do { + snprintf((char *) pref, 30, "%s_%d", nsPrefix, counter++); + ns = xmlSearchNs(target->doc, target, BAD_CAST pref); + if (counter > 1000) { + xsltTransformError(ctxt, NULL, invocNode, + "Internal error in xsltAcquireResultInScopeNs(): " + "Failed to compute a unique ns-prefix for the " + "generated element"); + return(NULL); + } + } while (ns != NULL); + ns = xmlNewNs(target, nsName, BAD_CAST pref); + /* TODO: Check result */ + return(ns); + } + return(NULL); +} + +/** + * xsltGetNamespace: + * @ctxt: a transformation context + * @cur: the input node + * @ns: the namespace + * @out: the output node (or its parent) + * + * Find a matching (prefix and ns-name) ns-declaration + * for the requested @ns->prefix and @ns->href in the result tree. + * If none is found then a new ns-declaration will be + * added to @resultElem. If, in this case, the given prefix is + * already in use, then a ns-declaration with a modified ns-prefix + * be we created. + * + * Called by: + * - xsltCopyPropList() (*not* anymore) + * - xsltShallowCopyElement() + * - xsltCopyTreeInternal() (*not* anymore) + * - xsltApplySequenceConstructor() (*not* in the refactored code), + * - xsltElement() (*not* anymore) + * + * Returns a namespace declaration or NULL in case of + * namespace fixup failures or API or internal errors. + */ +xmlNsPtr +xsltGetNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, xmlNsPtr ns, + xmlNodePtr out) +{ + + if (ns == NULL) + return(NULL); + +#ifdef XSLT_REFACTORED + /* + * Namespace exclusion and ns-aliasing is performed at + * compilation-time in the refactored code. + * Additionally, aliasing is not intended for non Literal + * Result Elements. + */ + return(xsltGetSpecialNamespace(ctxt, cur, ns->href, ns->prefix, out)); +#else + { + xsltStylesheetPtr style; + const xmlChar *URI = NULL; /* the replacement URI */ + + if ((ctxt == NULL) || (cur == NULL) || (out == NULL)) + return(NULL); + + style = ctxt->style; + while (style != NULL) { + if (style->nsAliases != NULL) + URI = (const xmlChar *) + xmlHashLookup(style->nsAliases, ns->href); + if (URI != NULL) + break; + + style = xsltNextImport(style); + } + + + if (URI == UNDEFINED_DEFAULT_NS) { + return(xsltGetSpecialNamespace(ctxt, cur, NULL, NULL, out)); +#if 0 + /* + * TODO: Removed, since wrong. If there was no default + * namespace in the stylesheet then this must resolve to + * the NULL namespace. + */ + xmlNsPtr dflt; + dflt = xmlSearchNs(cur->doc, cur, NULL); + if (dflt != NULL) + URI = dflt->href; + else + return NULL; +#endif + } else if (URI == NULL) + URI = ns->href; + + return(xsltGetSpecialNamespace(ctxt, cur, URI, ns->prefix, out)); + } +#endif +} + +/** + * xsltGetPlainNamespace: + * @ctxt: a transformation context + * @cur: the input node + * @ns: the namespace + * @out: the result element + * + * Obsolete. + * *Not* called by any Libxslt/Libexslt function. + * Exaclty the same as xsltGetNamespace(). + * + * Returns a namespace declaration or NULL in case of + * namespace fixup failures or API or internal errors. + */ +xmlNsPtr +xsltGetPlainNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, + xmlNsPtr ns, xmlNodePtr out) +{ + return(xsltGetNamespace(ctxt, cur, ns, out)); +} + +/** + * xsltCopyNamespaceList: + * @ctxt: a transformation context + * @node: the target node + * @cur: the first namespace + * + * Do a copy of an namespace list. If @node is non-NULL the + * new namespaces are added automatically. This handles namespaces + * aliases. + * This function is intended only for *internal* use at + * transformation-time for copying ns-declarations of Literal + * Result Elements. + * + * Called by: + * xsltCopyTreeInternal() (transform.c) + * xsltShallowCopyElem() (transform.c) + * + * REVISIT: This function won't be used in the refactored code. + * + * Returns: a new xmlNsPtr, or NULL in case of error. + */ +xmlNsPtr +xsltCopyNamespaceList(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNsPtr cur) { + xmlNsPtr ret = NULL, tmp; + xmlNsPtr p = NULL,q; + + if (cur == NULL) + return(NULL); + if (cur->type != XML_NAMESPACE_DECL) + return(NULL); + + /* + * One can add namespaces only on element nodes + */ + if ((node != NULL) && (node->type != XML_ELEMENT_NODE)) + node = NULL; + + while (cur != NULL) { + if (cur->type != XML_NAMESPACE_DECL) + break; + + /* + * Avoid duplicating namespace declarations in the tree if + * a matching declaration is in scope. + */ + if (node != NULL) { + if ((node->ns != NULL) && + (xmlStrEqual(node->ns->prefix, cur->prefix)) && + (xmlStrEqual(node->ns->href, cur->href))) { + cur = cur->next; + continue; + } + tmp = xmlSearchNs(node->doc, node, cur->prefix); + if ((tmp != NULL) && (xmlStrEqual(tmp->href, cur->href))) { + cur = cur->next; + continue; + } + } +#ifdef XSLT_REFACTORED + /* + * Namespace exclusion and ns-aliasing is performed at + * compilation-time in the refactored code. + */ + q = xmlNewNs(node, cur->href, cur->prefix); + if (p == NULL) { + ret = p = q; + } else { + p->next = q; + p = q; + } +#else + /* + * TODO: Remove this if the refactored code gets enabled. + */ + if (!xmlStrEqual(cur->href, XSLT_NAMESPACE)) { + const xmlChar *URI; + /* TODO apply cascading */ + URI = (const xmlChar *) xmlHashLookup(ctxt->style->nsAliases, + cur->href); + if (URI == UNDEFINED_DEFAULT_NS) { + cur = cur->next; + continue; + } + if (URI != NULL) { + q = xmlNewNs(node, URI, cur->prefix); + } else { + q = xmlNewNs(node, cur->href, cur->prefix); + } + if (p == NULL) { + ret = p = q; + } else { + p->next = q; + p = q; + } + } +#endif + cur = cur->next; + } + return(ret); +} + +/** + * xsltCopyNamespace: + * @ctxt: a transformation context + * @elem: the target element node + * @ns: the namespace node + * + * Copies a namespace node (declaration). If @elem is not NULL, + * then the new namespace will be declared on @elem. + * + * Returns: a new xmlNsPtr, or NULL in case of an error. + */ +xmlNsPtr +xsltCopyNamespace(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, + xmlNodePtr elem, xmlNsPtr ns) +{ + if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL)) + return(NULL); + /* + * One can add namespaces only on element nodes + */ + if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE)) + return(xmlNewNs(NULL, ns->href, ns->prefix)); + else + return(xmlNewNs(elem, ns->href, ns->prefix)); +} + + +/** + * xsltFreeNamespaceAliasHashes: + * @style: an XSLT stylesheet + * + * Free up the memory used by namespaces aliases + */ +void +xsltFreeNamespaceAliasHashes(xsltStylesheetPtr style) { + if (style->nsAliases != NULL) + xmlHashFree((xmlHashTablePtr) style->nsAliases, NULL); + style->nsAliases = NULL; +} diff --git a/libxslt/namespaces.h b/libxslt/namespaces.h new file mode 100644 index 0000000..fa2d3b4 --- /dev/null +++ b/libxslt/namespaces.h @@ -0,0 +1,68 @@ +/* + * Summary: interface for the XSLT namespace handling + * Description: set of function easing the processing and generation + * of namespace nodes in XSLT. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_NAMESPACES_H__ +#define __XML_XSLT_NAMESPACES_H__ + +#include <libxml/tree.h> +#include "xsltexports.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Used within nsAliases hashtable when the default namespace is required + * but it's not been explicitly defined + */ +/** + * UNDEFINED_DEFAULT_NS: + * + * Special value for undefined namespace, internal + */ +#define UNDEFINED_DEFAULT_NS (const xmlChar *) -1L + +XSLTPUBFUN void XSLTCALL + xsltNamespaceAlias (xsltStylesheetPtr style, + xmlNodePtr node); +XSLTPUBFUN xmlNsPtr XSLTCALL + xsltGetNamespace (xsltTransformContextPtr ctxt, + xmlNodePtr cur, + xmlNsPtr ns, + xmlNodePtr out); +XSLTPUBFUN xmlNsPtr XSLTCALL + xsltGetPlainNamespace (xsltTransformContextPtr ctxt, + xmlNodePtr cur, + xmlNsPtr ns, + xmlNodePtr out); +XSLTPUBFUN xmlNsPtr XSLTCALL + xsltGetSpecialNamespace (xsltTransformContextPtr ctxt, + xmlNodePtr cur, + const xmlChar *URI, + const xmlChar *prefix, + xmlNodePtr out); +XSLTPUBFUN xmlNsPtr XSLTCALL + xsltCopyNamespace (xsltTransformContextPtr ctxt, + xmlNodePtr elem, + xmlNsPtr ns); +XSLTPUBFUN xmlNsPtr XSLTCALL + xsltCopyNamespaceList (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNsPtr cur); +XSLTPUBFUN void XSLTCALL + xsltFreeNamespaceAliasHashes + (xsltStylesheetPtr style); + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_NAMESPACES_H__ */ + diff --git a/libxslt/numbers.c b/libxslt/numbers.c new file mode 100644 index 0000000..e78c46b --- /dev/null +++ b/libxslt/numbers.c @@ -0,0 +1,1361 @@ +/* + * numbers.c: Implementation of the XSLT number functions + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + * Bjorn Reese <breese@users.sourceforge.net> + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <math.h> +#include <limits.h> +#include <float.h> +#include <string.h> + +#include <libxml/xmlmemory.h> +#include <libxml/parserInternals.h> +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> +#include <libxml/encoding.h> +#include "xsltutils.h" +#include "pattern.h" +#include "templates.h" +#include "transform.h" +#include "numbersInternals.h" + +#ifndef FALSE +# define FALSE (0 == 1) +# define TRUE (1 == 1) +#endif + +#define SYMBOL_QUOTE ((xmlChar)'\'') + +#define DEFAULT_TOKEN (xmlChar)'0' +#define DEFAULT_SEPARATOR "." + +#define MAX_TOKENS 1024 + +typedef struct _xsltFormatToken xsltFormatToken; +typedef xsltFormatToken *xsltFormatTokenPtr; +struct _xsltFormatToken { + xmlChar *separator; + xmlChar token; + int width; +}; + +typedef struct _xsltFormat xsltFormat; +typedef xsltFormat *xsltFormatPtr; +struct _xsltFormat { + xmlChar *start; + xsltFormatToken tokens[MAX_TOKENS]; + int nTokens; + xmlChar *end; +}; + +static char alpha_upper_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +static char alpha_lower_list[] = "abcdefghijklmnopqrstuvwxyz"; +static xsltFormatToken default_token; + +/* + * **** Start temp insert **** + * + * The following two routines (xsltUTF8Size and xsltUTF8Charcmp) + * will be replaced with calls to the corresponding libxml routines + * at a later date (when other inter-library dependencies require it) + */ + +/** + * xsltUTF8Size: + * @utf: pointer to the UTF8 character + * + * returns the numbers of bytes in the character, -1 on format error + */ +static int +xsltUTF8Size(xmlChar *utf) { + xmlChar mask; + int len; + + if (utf == NULL) + return -1; + if (*utf < 0x80) + return 1; + /* check valid UTF8 character */ + if (!(*utf & 0x40)) + return -1; + /* determine number of bytes in char */ + len = 2; + for (mask=0x20; mask != 0; mask>>=1) { + if (!(*utf & mask)) + return len; + len++; + } + return -1; +} + +/** + * xsltUTF8Charcmp + * @utf1: pointer to first UTF8 char + * @utf2: pointer to second UTF8 char + * + * returns result of comparing the two UCS4 values + * as with xmlStrncmp + */ +static int +xsltUTF8Charcmp(xmlChar *utf1, xmlChar *utf2) { + + if (utf1 == NULL ) { + if (utf2 == NULL) + return 0; + return -1; + } + return xmlStrncmp(utf1, utf2, xsltUTF8Size(utf1)); +} + +/***** Stop temp insert *****/ +/************************************************************************ + * * + * Utility functions * + * * + ************************************************************************/ + +#define IS_SPECIAL(self,letter) \ + ((xsltUTF8Charcmp((letter), (self)->zeroDigit) == 0) || \ + (xsltUTF8Charcmp((letter), (self)->digit) == 0) || \ + (xsltUTF8Charcmp((letter), (self)->decimalPoint) == 0) || \ + (xsltUTF8Charcmp((letter), (self)->grouping) == 0) || \ + (xsltUTF8Charcmp((letter), (self)->patternSeparator) == 0)) + +#define IS_DIGIT_ZERO(x) xsltIsDigitZero(x) +#define IS_DIGIT_ONE(x) xsltIsDigitZero((xmlChar)(x)-1) + +static int +xsltIsDigitZero(unsigned int ch) +{ + /* + * Reference: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt + */ + switch (ch) { + case 0x0030: case 0x0660: case 0x06F0: case 0x0966: + case 0x09E6: case 0x0A66: case 0x0AE6: case 0x0B66: + case 0x0C66: case 0x0CE6: case 0x0D66: case 0x0E50: + case 0x0E60: case 0x0F20: case 0x1040: case 0x17E0: + case 0x1810: case 0xFF10: + return TRUE; + default: + return FALSE; + } +} + +static void +xsltNumberFormatDecimal(xmlBufferPtr buffer, + double number, + int digit_zero, + int width, + int digitsPerGroup, + int groupingCharacter, + int groupingCharacterLen) +{ + /* + * This used to be + * xmlChar temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 4]; + * which would be length 68 on x86 arch. It was changed to be a longer, + * fixed length in order to try to cater for (reasonable) UTF8 + * separators and numeric characters. The max UTF8 char size will be + * 6 or less, so the value used [500] should be *much* larger than needed + */ + xmlChar temp_string[500]; + xmlChar *pointer; + xmlChar temp_char[6]; + int i; + int val; + int len; + + /* Build buffer from back */ + pointer = &temp_string[sizeof(temp_string)] - 1; /* last char */ + *pointer = 0; + i = 0; + while (pointer > temp_string) { + if ((i >= width) && (fabs(number) < 1.0)) + break; /* for */ + if ((i > 0) && (groupingCharacter != 0) && + (digitsPerGroup > 0) && + ((i % digitsPerGroup) == 0)) { + if (pointer - groupingCharacterLen < temp_string) { + i = -1; /* flag error */ + break; + } + pointer -= groupingCharacterLen; + xmlCopyCharMultiByte(pointer, groupingCharacter); + } + + val = digit_zero + (int)fmod(number, 10.0); + if (val < 0x80) { /* shortcut if ASCII */ + if (pointer <= temp_string) { /* Check enough room */ + i = -1; + break; + } + *(--pointer) = val; + } + else { + /* + * Here we have a multibyte character. It's a little messy, + * because until we generate the char we don't know how long + * it is. So, we generate it into the buffer temp_char, then + * copy from there into temp_string. + */ + len = xmlCopyCharMultiByte(temp_char, val); + if ( (pointer - len) < temp_string ) { + i = -1; + break; + } + pointer -= len; + memcpy(pointer, temp_char, len); + } + number /= 10.0; + ++i; + } + if (i < 0) + xsltGenericError(xsltGenericErrorContext, + "xsltNumberFormatDecimal: Internal buffer size exceeded"); + xmlBufferCat(buffer, pointer); +} + +static void +xsltNumberFormatAlpha(xsltNumberDataPtr data, + xmlBufferPtr buffer, + double number, + int is_upper) +{ + char temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 1]; + char *pointer; + int i; + char *alpha_list; + double alpha_size = (double)(sizeof(alpha_upper_list) - 1); + + /* + * XSLT 1.0 isn't clear on how to handle zero, but XSLT 2.0 says: + * + * For all format tokens other than the first kind above (one that + * consists of decimal digits), there may be implementation-defined + * lower and upper bounds on the range of numbers that can be + * formatted using this format token; indeed, for some numbering + * sequences there may be intrinsic limits. [...] Numbers that fall + * outside this range must be formatted using the format token 1. + * + * The "a" token has an intrinsic lower limit of 1. + */ + if (number < 1.0) { + xsltNumberFormatDecimal(buffer, number, '0', 1, + data->digitsPerGroup, + data->groupingCharacter, + data->groupingCharacterLen); + return; + } + + /* Build buffer from back */ + pointer = &temp_string[sizeof(temp_string)]; + *(--pointer) = 0; + alpha_list = (is_upper) ? alpha_upper_list : alpha_lower_list; + + for (i = 1; i < (int)sizeof(temp_string); i++) { + number--; + *(--pointer) = alpha_list[((int)fmod(number, alpha_size))]; + number /= alpha_size; + if (number < 1.0) + break; /* for */ + } + xmlBufferCCat(buffer, pointer); +} + +static void +xsltNumberFormatRoman(xsltNumberDataPtr data, + xmlBufferPtr buffer, + double number, + int is_upper) +{ + /* + * See discussion in xsltNumberFormatAlpha. Also use a reasonable upper + * bound to avoid denial of service. + */ + if (number < 1.0 || number > 5000.0) { + xsltNumberFormatDecimal(buffer, number, '0', 1, + data->digitsPerGroup, + data->groupingCharacter, + data->groupingCharacterLen); + return; + } + + /* + * Based on an example by Jim Walsh + */ + while (number >= 1000.0) { + xmlBufferCCat(buffer, (is_upper) ? "M" : "m"); + number -= 1000.0; + } + if (number >= 900.0) { + xmlBufferCCat(buffer, (is_upper) ? "CM" : "cm"); + number -= 900.0; + } + while (number >= 500.0) { + xmlBufferCCat(buffer, (is_upper) ? "D" : "d"); + number -= 500.0; + } + if (number >= 400.0) { + xmlBufferCCat(buffer, (is_upper) ? "CD" : "cd"); + number -= 400.0; + } + while (number >= 100.0) { + xmlBufferCCat(buffer, (is_upper) ? "C" : "c"); + number -= 100.0; + } + if (number >= 90.0) { + xmlBufferCCat(buffer, (is_upper) ? "XC" : "xc"); + number -= 90.0; + } + while (number >= 50.0) { + xmlBufferCCat(buffer, (is_upper) ? "L" : "l"); + number -= 50.0; + } + if (number >= 40.0) { + xmlBufferCCat(buffer, (is_upper) ? "XL" : "xl"); + number -= 40.0; + } + while (number >= 10.0) { + xmlBufferCCat(buffer, (is_upper) ? "X" : "x"); + number -= 10.0; + } + if (number >= 9.0) { + xmlBufferCCat(buffer, (is_upper) ? "IX" : "ix"); + number -= 9.0; + } + while (number >= 5.0) { + xmlBufferCCat(buffer, (is_upper) ? "V" : "v"); + number -= 5.0; + } + if (number >= 4.0) { + xmlBufferCCat(buffer, (is_upper) ? "IV" : "iv"); + number -= 4.0; + } + while (number >= 1.0) { + xmlBufferCCat(buffer, (is_upper) ? "I" : "i"); + number--; + } +} + +static void +xsltNumberFormatTokenize(const xmlChar *format, + xsltFormatPtr tokens) +{ + int ix = 0; + int j; + int val; + int len; + + default_token.token = DEFAULT_TOKEN; + default_token.width = 1; + default_token.separator = BAD_CAST(DEFAULT_SEPARATOR); + + + tokens->start = NULL; + tokens->tokens[0].separator = NULL; + tokens->end = NULL; + + /* + * Insert initial non-alphanumeric token. + * There is always such a token in the list, even if NULL + */ + while (! (IS_LETTER(val=xmlStringCurrentChar(NULL, format+ix, &len)) || + IS_DIGIT(val)) ) { + if (format[ix] == 0) /* if end of format string */ + break; /* while */ + ix += len; + } + if (ix > 0) + tokens->start = xmlStrndup(format, ix); + + + for (tokens->nTokens = 0; tokens->nTokens < MAX_TOKENS; + tokens->nTokens++) { + if (format[ix] == 0) + break; /* for */ + + /* + * separator has already been parsed (except for the first + * number) in tokens->end, recover it. + */ + if (tokens->nTokens > 0) { + tokens->tokens[tokens->nTokens].separator = tokens->end; + tokens->end = NULL; + } + + val = xmlStringCurrentChar(NULL, format+ix, &len); + if (IS_DIGIT_ONE(val) || + IS_DIGIT_ZERO(val)) { + tokens->tokens[tokens->nTokens].width = 1; + while (IS_DIGIT_ZERO(val)) { + tokens->tokens[tokens->nTokens].width++; + ix += len; + val = xmlStringCurrentChar(NULL, format+ix, &len); + } + if (IS_DIGIT_ONE(val)) { + tokens->tokens[tokens->nTokens].token = val - 1; + ix += len; + val = xmlStringCurrentChar(NULL, format+ix, &len); + } + } else if ( (val == (xmlChar)'A') || + (val == (xmlChar)'a') || + (val == (xmlChar)'I') || + (val == (xmlChar)'i') ) { + tokens->tokens[tokens->nTokens].token = val; + ix += len; + val = xmlStringCurrentChar(NULL, format+ix, &len); + } else { + /* XSLT section 7.7 + * "Any other format token indicates a numbering sequence + * that starts with that token. If an implementation does + * not support a numbering sequence that starts with that + * token, it must use a format token of 1." + */ + tokens->tokens[tokens->nTokens].token = (xmlChar)'0'; + tokens->tokens[tokens->nTokens].width = 1; + } + /* + * Skip over remaining alphanumeric characters from the Nd + * (Number, decimal digit), Nl (Number, letter), No (Number, + * other), Lu (Letter, uppercase), Ll (Letter, lowercase), Lt + * (Letters, titlecase), Lm (Letters, modifiers), and Lo + * (Letters, other (uncased)) Unicode categories. This happens + * to correspond to the Letter and Digit classes from XML (and + * one wonders why XSLT doesn't refer to these instead). + */ + while (IS_LETTER(val) || IS_DIGIT(val)) { + ix += len; + val = xmlStringCurrentChar(NULL, format+ix, &len); + } + + /* + * Insert temporary non-alphanumeric final tooken. + */ + j = ix; + while (! (IS_LETTER(val) || IS_DIGIT(val))) { + if (val == 0) + break; /* while */ + ix += len; + val = xmlStringCurrentChar(NULL, format+ix, &len); + } + if (ix > j) + tokens->end = xmlStrndup(&format[j], ix - j); + } +} + +static void +xsltNumberFormatInsertNumbers(xsltNumberDataPtr data, + double *numbers, + int numbers_max, + xsltFormatPtr tokens, + xmlBufferPtr buffer) +{ + int i = 0; + double number; + xsltFormatTokenPtr token; + + /* + * Handle initial non-alphanumeric token + */ + if (tokens->start != NULL) + xmlBufferCat(buffer, tokens->start); + + for (i = 0; i < numbers_max; i++) { + /* Insert number */ + number = numbers[(numbers_max - 1) - i]; + /* Round to nearest like XSLT 2.0 */ + number = floor(number + 0.5); + /* + * XSLT 1.0 isn't clear on how to handle negative numbers, but XSLT + * 2.0 says: + * + * It is a non-recoverable dynamic error if any undiscarded item + * in the atomized sequence supplied as the value of the value + * attribute of xsl:number cannot be converted to an integer, or + * if the resulting integer is less than 0 (zero). + */ + if (number < 0.0) { + xsltTransformError(NULL, NULL, NULL, + "xsl-number : negative value\n"); + /* Recover by treating negative values as zero. */ + number = 0.0; + } + if (i < tokens->nTokens) { + /* + * The "n"th format token will be used to format the "n"th + * number in the list + */ + token = &(tokens->tokens[i]); + } else if (tokens->nTokens > 0) { + /* + * If there are more numbers than format tokens, then the + * last format token will be used to format the remaining + * numbers. + */ + token = &(tokens->tokens[tokens->nTokens - 1]); + } else { + /* + * If there are no format tokens, then a format token of + * 1 is used to format all numbers. + */ + token = &default_token; + } + + /* Print separator, except for the first number */ + if (i > 0) { + if (token->separator != NULL) + xmlBufferCat(buffer, token->separator); + else + xmlBufferCCat(buffer, DEFAULT_SEPARATOR); + } + + switch (xmlXPathIsInf(number)) { + case -1: + xmlBufferCCat(buffer, "-Infinity"); + break; + case 1: + xmlBufferCCat(buffer, "Infinity"); + break; + default: + if (xmlXPathIsNaN(number)) { + xmlBufferCCat(buffer, "NaN"); + } else { + + switch (token->token) { + case 'A': + xsltNumberFormatAlpha(data, buffer, number, TRUE); + break; + case 'a': + xsltNumberFormatAlpha(data, buffer, number, FALSE); + break; + case 'I': + xsltNumberFormatRoman(data, buffer, number, TRUE); + break; + case 'i': + xsltNumberFormatRoman(data, buffer, number, FALSE); + break; + default: + if (IS_DIGIT_ZERO(token->token)) { + xsltNumberFormatDecimal(buffer, + number, + token->token, + token->width, + data->digitsPerGroup, + data->groupingCharacter, + data->groupingCharacterLen); + } + break; + } + } + + } + } + + /* + * Handle final non-alphanumeric token + */ + if (tokens->end != NULL) + xmlBufferCat(buffer, tokens->end); + +} + +static int +xsltTestCompMatchCount(xsltTransformContextPtr context, + xmlNodePtr node, + xsltCompMatchPtr countPat, + xmlNodePtr cur) +{ + if (countPat != NULL) { + return xsltTestCompMatchList(context, node, countPat); + } + else { + /* + * 7.7 Numbering + * + * If count attribute is not specified, then it defaults to the + * pattern that matches any node with the same node type as the + * current node and, if the current node has an expanded-name, with + * the same expanded-name as the current node. + */ + if (node->type != cur->type) + return 0; + if (node->type == XML_NAMESPACE_DECL) + /* + * Namespace nodes have no preceding siblings and no parents + * that are namespace nodes. This means that node == cur. + */ + return 1; + /* TODO: Skip node types without expanded names like text nodes. */ + if (!xmlStrEqual(node->name, cur->name)) + return 0; + if (node->ns == cur->ns) + return 1; + if ((node->ns == NULL) || (cur->ns == NULL)) + return 0; + return (xmlStrEqual(node->ns->href, cur->ns->href)); + } +} + +static int +xsltNumberFormatGetAnyLevel(xsltTransformContextPtr context, + xmlNodePtr node, + xsltCompMatchPtr countPat, + xsltCompMatchPtr fromPat, + double *array) +{ + int amount = 0; + int cnt = 0; + xmlNodePtr cur = node; + + while (cur != NULL) { + /* process current node */ + if (xsltTestCompMatchCount(context, cur, countPat, node)) + cnt++; + if ((fromPat != NULL) && + xsltTestCompMatchList(context, cur, fromPat)) { + break; /* while */ + } + + /* Skip to next preceding or ancestor */ + if ((cur->type == XML_DOCUMENT_NODE) || +#ifdef LIBXML_DOCB_ENABLED + (cur->type == XML_DOCB_DOCUMENT_NODE) || +#endif + (cur->type == XML_HTML_DOCUMENT_NODE)) + break; /* while */ + + if (cur->type == XML_NAMESPACE_DECL) { + /* + * The XPath module stores the parent of a namespace node in + * the ns->next field. + */ + cur = (xmlNodePtr) ((xmlNsPtr) cur)->next; + } else if (cur->type == XML_ATTRIBUTE_NODE) { + cur = cur->parent; + } else { + while ((cur->prev != NULL) && ((cur->prev->type == XML_DTD_NODE) || + (cur->prev->type == XML_XINCLUDE_START) || + (cur->prev->type == XML_XINCLUDE_END))) + cur = cur->prev; + if (cur->prev != NULL) { + for (cur = cur->prev; cur->last != NULL; cur = cur->last); + } else { + cur = cur->parent; + } + } + } + + array[amount++] = (double) cnt; + + return(amount); +} + +static int +xsltNumberFormatGetMultipleLevel(xsltTransformContextPtr context, + xmlNodePtr node, + xsltCompMatchPtr countPat, + xsltCompMatchPtr fromPat, + double *array, + int max) +{ + int amount = 0; + int cnt; + xmlNodePtr ancestor; + xmlNodePtr preceding; + xmlXPathParserContextPtr parser; + + context->xpathCtxt->node = node; + parser = xmlXPathNewParserContext(NULL, context->xpathCtxt); + if (parser) { + /* ancestor-or-self::*[count] */ + for (ancestor = node; + (ancestor != NULL) && (ancestor->type != XML_DOCUMENT_NODE); + ancestor = xmlXPathNextAncestor(parser, ancestor)) { + + if ((fromPat != NULL) && + xsltTestCompMatchList(context, ancestor, fromPat)) + break; /* for */ + + if (xsltTestCompMatchCount(context, ancestor, countPat, node)) { + /* count(preceding-sibling::*) */ + cnt = 1; + for (preceding = + xmlXPathNextPrecedingSibling(parser, ancestor); + preceding != NULL; + preceding = + xmlXPathNextPrecedingSibling(parser, preceding)) { + + if (xsltTestCompMatchCount(context, preceding, countPat, + node)) + cnt++; + } + array[amount++] = (double)cnt; + if (amount >= max) + break; /* for */ + } + } + xmlXPathFreeParserContext(parser); + } + return amount; +} + +static int +xsltNumberFormatGetValue(xmlXPathContextPtr context, + xmlNodePtr node, + const xmlChar *value, + double *number) +{ + int amount = 0; + xmlBufferPtr pattern; + xmlXPathObjectPtr obj; + + pattern = xmlBufferCreate(); + if (pattern != NULL) { + xmlBufferCCat(pattern, "number("); + xmlBufferCat(pattern, value); + xmlBufferCCat(pattern, ")"); + context->node = node; + obj = xmlXPathEvalExpression(xmlBufferContent(pattern), + context); + if (obj != NULL) { + *number = obj->floatval; + amount++; + xmlXPathFreeObject(obj); + } + xmlBufferFree(pattern); + } + return amount; +} + +/** + * xsltNumberFormat: + * @ctxt: the XSLT transformation context + * @data: the formatting informations + * @node: the data to format + * + * Convert one number. + */ +void +xsltNumberFormat(xsltTransformContextPtr ctxt, + xsltNumberDataPtr data, + xmlNodePtr node) +{ + xmlBufferPtr output = NULL; + int amount, i; + double number; + xsltFormat tokens; + + if (data->format != NULL) { + xsltNumberFormatTokenize(data->format, &tokens); + } + else { + xmlChar *format; + + /* The format needs to be recomputed each time */ + if (data->has_format == 0) + return; + format = xsltEvalAttrValueTemplate(ctxt, data->node, + (const xmlChar *) "format", + XSLT_NAMESPACE); + if (format == NULL) + return; + xsltNumberFormatTokenize(format, &tokens); + xmlFree(format); + } + + output = xmlBufferCreate(); + if (output == NULL) + goto XSLT_NUMBER_FORMAT_END; + + /* + * Evaluate the XPath expression to find the value(s) + */ + if (data->value) { + amount = xsltNumberFormatGetValue(ctxt->xpathCtxt, + node, + data->value, + &number); + if (amount == 1) { + xsltNumberFormatInsertNumbers(data, + &number, + 1, + &tokens, + output); + } + + } else if (data->level) { + + if (xmlStrEqual(data->level, (const xmlChar *) "single")) { + amount = xsltNumberFormatGetMultipleLevel(ctxt, + node, + data->countPat, + data->fromPat, + &number, + 1); + if (amount == 1) { + xsltNumberFormatInsertNumbers(data, + &number, + 1, + &tokens, + output); + } + } else if (xmlStrEqual(data->level, (const xmlChar *) "multiple")) { + double numarray[1024]; + int max = sizeof(numarray)/sizeof(numarray[0]); + amount = xsltNumberFormatGetMultipleLevel(ctxt, + node, + data->countPat, + data->fromPat, + numarray, + max); + if (amount > 0) { + xsltNumberFormatInsertNumbers(data, + numarray, + amount, + &tokens, + output); + } + } else if (xmlStrEqual(data->level, (const xmlChar *) "any")) { + amount = xsltNumberFormatGetAnyLevel(ctxt, + node, + data->countPat, + data->fromPat, + &number); + if (amount > 0) { + xsltNumberFormatInsertNumbers(data, + &number, + 1, + &tokens, + output); + } + } + } + /* Insert number as text node */ + xsltCopyTextString(ctxt, ctxt->insert, xmlBufferContent(output), 0); + + xmlBufferFree(output); + +XSLT_NUMBER_FORMAT_END: + if (tokens.start != NULL) + xmlFree(tokens.start); + if (tokens.end != NULL) + xmlFree(tokens.end); + for (i = 0;i < tokens.nTokens;i++) { + if (tokens.tokens[i].separator != NULL) + xmlFree(tokens.tokens[i].separator); + } +} + +static int +xsltFormatNumberPreSuffix(xsltDecimalFormatPtr self, xmlChar **format, xsltFormatNumberInfoPtr info) +{ + int count=0; /* will hold total length of prefix/suffix */ + int len; + + while (1) { + /* + * prefix / suffix ends at end of string or at + * first 'special' character + */ + if (**format == 0) + return count; + /* if next character 'escaped' just count it */ + if (**format == SYMBOL_QUOTE) { + if (*++(*format) == 0) + return -1; + } + else if (IS_SPECIAL(self, *format)) + return count; + /* + * else treat percent/per-mille as special cases, + * depending on whether +ve or -ve + */ + else { + /* + * for +ve prefix/suffix, allow only a + * single occurence of either + */ + if (xsltUTF8Charcmp(*format, self->percent) == 0) { + if (info->is_multiplier_set) + return -1; + info->multiplier = 100; + info->is_multiplier_set = TRUE; + } else if (xsltUTF8Charcmp(*format, self->permille) == 0) { + if (info->is_multiplier_set) + return -1; + info->multiplier = 1000; + info->is_multiplier_set = TRUE; + } + } + + if ((len=xsltUTF8Size(*format)) < 1) + return -1; + count += len; + *format += len; + } +} + +/** + * xsltFormatNumberConversion: + * @self: the decimal format + * @format: the format requested + * @number: the value to format + * @result: the place to ouput the result + * + * format-number() uses the JDK 1.1 DecimalFormat class: + * + * http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html + * + * Structure: + * + * pattern := subpattern{;subpattern} + * subpattern := {prefix}integer{.fraction}{suffix} + * prefix := '\\u0000'..'\\uFFFD' - specialCharacters + * suffix := '\\u0000'..'\\uFFFD' - specialCharacters + * integer := '#'* '0'* '0' + * fraction := '0'* '#'* + * + * Notation: + * X* 0 or more instances of X + * (X | Y) either X or Y. + * X..Y any character from X up to Y, inclusive. + * S - T characters in S, except those in T + * + * Special Characters: + * + * Symbol Meaning + * 0 a digit + * # a digit, zero shows as absent + * . placeholder for decimal separator + * , placeholder for grouping separator. + * ; separates formats. + * - default negative prefix. + * % multiply by 100 and show as percentage + * ? multiply by 1000 and show as per mille + * X any other characters can be used in the prefix or suffix + * ' used to quote special characters in a prefix or suffix. + * + * Returns a possible XPath error + */ +xmlXPathError +xsltFormatNumberConversion(xsltDecimalFormatPtr self, + xmlChar *format, + double number, + xmlChar **result) +{ + xmlXPathError status = XPATH_EXPRESSION_OK; + xmlBufferPtr buffer; + xmlChar *the_format, *prefix = NULL, *suffix = NULL; + xmlChar *nprefix, *nsuffix = NULL; + xmlChar pchar; + int prefix_length, suffix_length = 0, nprefix_length, nsuffix_length; + double scale; + int j, len; + int self_grouping_len; + xsltFormatNumberInfo format_info; + /* + * delayed_multiplier allows a 'trailing' percent or + * permille to be treated as suffix + */ + int delayed_multiplier = 0; + /* flag to show no -ve format present for -ve number */ + char default_sign = 0; + /* flag to show error found, should use default format */ + char found_error = 0; + + if (xmlStrlen(format) <= 0) { + xsltTransformError(NULL, NULL, NULL, + "xsltFormatNumberConversion : " + "Invalid format (0-length)\n"); + } + *result = NULL; + switch (xmlXPathIsInf(number)) { + case -1: + if (self->minusSign == NULL) + *result = xmlStrdup(BAD_CAST "-"); + else + *result = xmlStrdup(self->minusSign); + /* no-break on purpose */ + case 1: + if ((self == NULL) || (self->infinity == NULL)) + *result = xmlStrcat(*result, BAD_CAST "Infinity"); + else + *result = xmlStrcat(*result, self->infinity); + return(status); + default: + if (xmlXPathIsNaN(number)) { + if ((self == NULL) || (self->noNumber == NULL)) + *result = xmlStrdup(BAD_CAST "NaN"); + else + *result = xmlStrdup(self->noNumber); + return(status); + } + } + + buffer = xmlBufferCreate(); + if (buffer == NULL) { + return XPATH_MEMORY_ERROR; + } + + format_info.integer_hash = 0; + format_info.integer_digits = 0; + format_info.frac_digits = 0; + format_info.frac_hash = 0; + format_info.group = -1; + format_info.multiplier = 1; + format_info.add_decimal = FALSE; + format_info.is_multiplier_set = FALSE; + format_info.is_negative_pattern = FALSE; + + the_format = format; + + /* + * First we process the +ve pattern to get percent / permille, + * as well as main format + */ + prefix = the_format; + prefix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info); + if (prefix_length < 0) { + found_error = 1; + goto OUTPUT_NUMBER; + } + + /* + * Here we process the "number" part of the format. It gets + * a little messy because of the percent/per-mille - if that + * appears at the end, it may be part of the suffix instead + * of part of the number, so the variable delayed_multiplier + * is used to handle it + */ + self_grouping_len = xmlStrlen(self->grouping); + while ((*the_format != 0) && + (xsltUTF8Charcmp(the_format, self->decimalPoint) != 0) && + (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) { + + if (delayed_multiplier != 0) { + format_info.multiplier = delayed_multiplier; + format_info.is_multiplier_set = TRUE; + delayed_multiplier = 0; + } + if (xsltUTF8Charcmp(the_format, self->digit) == 0) { + if (format_info.integer_digits > 0) { + found_error = 1; + goto OUTPUT_NUMBER; + } + format_info.integer_hash++; + if (format_info.group >= 0) + format_info.group++; + } else if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) { + format_info.integer_digits++; + if (format_info.group >= 0) + format_info.group++; + } else if ((self_grouping_len > 0) && + (!xmlStrncmp(the_format, self->grouping, self_grouping_len))) { + /* Reset group count */ + format_info.group = 0; + the_format += self_grouping_len; + continue; + } else if (xsltUTF8Charcmp(the_format, self->percent) == 0) { + if (format_info.is_multiplier_set) { + found_error = 1; + goto OUTPUT_NUMBER; + } + delayed_multiplier = 100; + } else if (xsltUTF8Charcmp(the_format, self->permille) == 0) { + if (format_info.is_multiplier_set) { + found_error = 1; + goto OUTPUT_NUMBER; + } + delayed_multiplier = 1000; + } else + break; /* while */ + + if ((len=xsltUTF8Size(the_format)) < 1) { + found_error = 1; + goto OUTPUT_NUMBER; + } + the_format += len; + + } + + /* We have finished the integer part, now work on fraction */ + if ( (*the_format != 0) && + (xsltUTF8Charcmp(the_format, self->decimalPoint) == 0) ) { + format_info.add_decimal = TRUE; + the_format += xsltUTF8Size(the_format); /* Skip over the decimal */ + } + + while (*the_format != 0) { + + if (xsltUTF8Charcmp(the_format, self->zeroDigit) == 0) { + if (format_info.frac_hash != 0) { + found_error = 1; + goto OUTPUT_NUMBER; + } + format_info.frac_digits++; + } else if (xsltUTF8Charcmp(the_format, self->digit) == 0) { + format_info.frac_hash++; + } else if (xsltUTF8Charcmp(the_format, self->percent) == 0) { + if (format_info.is_multiplier_set) { + found_error = 1; + goto OUTPUT_NUMBER; + } + delayed_multiplier = 100; + if ((len = xsltUTF8Size(the_format)) < 1) { + found_error = 1; + goto OUTPUT_NUMBER; + } + the_format += len; + continue; /* while */ + } else if (xsltUTF8Charcmp(the_format, self->permille) == 0) { + if (format_info.is_multiplier_set) { + found_error = 1; + goto OUTPUT_NUMBER; + } + delayed_multiplier = 1000; + if ((len = xsltUTF8Size(the_format)) < 1) { + found_error = 1; + goto OUTPUT_NUMBER; + } + the_format += len; + continue; /* while */ + } else if (xsltUTF8Charcmp(the_format, self->grouping) != 0) { + break; /* while */ + } + if ((len = xsltUTF8Size(the_format)) < 1) { + found_error = 1; + goto OUTPUT_NUMBER; + } + the_format += len; + if (delayed_multiplier != 0) { + format_info.multiplier = delayed_multiplier; + delayed_multiplier = 0; + format_info.is_multiplier_set = TRUE; + } + } + + /* + * If delayed_multiplier is set after processing the + * "number" part, should be in suffix + */ + if (delayed_multiplier != 0) { + the_format -= len; + delayed_multiplier = 0; + } + + suffix = the_format; + suffix_length = xsltFormatNumberPreSuffix(self, &the_format, &format_info); + if ( (suffix_length < 0) || + ((*the_format != 0) && + (xsltUTF8Charcmp(the_format, self->patternSeparator) != 0)) ) { + found_error = 1; + goto OUTPUT_NUMBER; + } + + /* + * We have processed the +ve prefix, number part and +ve suffix. + * If the number is -ve, we must substitute the -ve prefix / suffix + */ + if (number < 0) { + /* + * Note that j is the number of UTF8 chars before the separator, + * not the number of bytes! (bug 151975) + */ + j = xmlUTF8Strloc(format, self->patternSeparator); + if (j < 0) { + /* No -ve pattern present, so use default signing */ + default_sign = 1; + } + else { + /* Skip over pattern separator (accounting for UTF8) */ + the_format = (xmlChar *)xmlUTF8Strpos(format, j + 1); + /* + * Flag changes interpretation of percent/permille + * in -ve pattern + */ + format_info.is_negative_pattern = TRUE; + format_info.is_multiplier_set = FALSE; + + /* First do the -ve prefix */ + nprefix = the_format; + nprefix_length = xsltFormatNumberPreSuffix(self, + &the_format, &format_info); + if (nprefix_length<0) { + found_error = 1; + goto OUTPUT_NUMBER; + } + + while (*the_format != 0) { + if ( (xsltUTF8Charcmp(the_format, (self)->percent) == 0) || + (xsltUTF8Charcmp(the_format, (self)->permille)== 0) ) { + if (format_info.is_multiplier_set) { + found_error = 1; + goto OUTPUT_NUMBER; + } + format_info.is_multiplier_set = TRUE; + delayed_multiplier = 1; + } + else if (IS_SPECIAL(self, the_format)) + delayed_multiplier = 0; + else + break; /* while */ + if ((len = xsltUTF8Size(the_format)) < 1) { + found_error = 1; + goto OUTPUT_NUMBER; + } + the_format += len; + } + if (delayed_multiplier != 0) { + format_info.is_multiplier_set = FALSE; + the_format -= len; + } + + /* Finally do the -ve suffix */ + if (*the_format != 0) { + nsuffix = the_format; + nsuffix_length = xsltFormatNumberPreSuffix(self, + &the_format, &format_info); + if (nsuffix_length < 0) { + found_error = 1; + goto OUTPUT_NUMBER; + } + } + else + nsuffix_length = 0; + if (*the_format != 0) { + found_error = 1; + goto OUTPUT_NUMBER; + } + /* + * Here's another Java peculiarity: + * if -ve prefix/suffix == +ve ones, discard & use default + */ + if ((nprefix_length != prefix_length) || + (nsuffix_length != suffix_length) || + ((nprefix_length > 0) && + (xmlStrncmp(nprefix, prefix, prefix_length) !=0 )) || + ((nsuffix_length > 0) && + (xmlStrncmp(nsuffix, suffix, suffix_length) !=0 ))) { + prefix = nprefix; + prefix_length = nprefix_length; + suffix = nsuffix; + suffix_length = nsuffix_length; + } /* else { + default_sign = 1; + } + */ + } + } + +OUTPUT_NUMBER: + if (found_error != 0) { + xsltTransformError(NULL, NULL, NULL, + "xsltFormatNumberConversion : " + "error in format string '%s', using default\n", format); + default_sign = (number < 0.0) ? 1 : 0; + prefix_length = suffix_length = 0; + format_info.integer_hash = 0; + format_info.integer_digits = 1; + format_info.frac_digits = 1; + format_info.frac_hash = 4; + format_info.group = -1; + format_info.multiplier = 1; + format_info.add_decimal = TRUE; + } + + /* Ready to output our number. First see if "default sign" is required */ + if (default_sign != 0) + xmlBufferAdd(buffer, self->minusSign, xsltUTF8Size(self->minusSign)); + + /* Put the prefix into the buffer */ + for (j = 0; j < prefix_length; j++) { + if ((pchar = *prefix++) == SYMBOL_QUOTE) { + len = xsltUTF8Size(prefix); + xmlBufferAdd(buffer, prefix, len); + prefix += len; + j += len - 1; /* length of symbol less length of quote */ + } else + xmlBufferAdd(buffer, &pchar, 1); + } + + /* Next do the integer part of the number */ + number = fabs(number) * (double)format_info.multiplier; + scale = pow(10.0, (double)(format_info.frac_digits + format_info.frac_hash)); + number = floor((scale * number + 0.5)) / scale; + if ((self->grouping != NULL) && + (self->grouping[0] != 0)) { + + len = xmlStrlen(self->grouping); + pchar = xsltGetUTF8Char(self->grouping, &len); + xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0], + format_info.integer_digits, + format_info.group, + pchar, len); + } else + xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0], + format_info.integer_digits, + format_info.group, + ',', 1); + + /* Special case: java treats '.#' like '.0', '.##' like '.0#', etc. */ + if ((format_info.integer_digits + format_info.integer_hash + + format_info.frac_digits == 0) && (format_info.frac_hash > 0)) { + ++format_info.frac_digits; + --format_info.frac_hash; + } + + /* Add leading zero, if required */ + if ((floor(number) == 0) && + (format_info.integer_digits + format_info.frac_digits == 0)) { + xmlBufferAdd(buffer, self->zeroDigit, xsltUTF8Size(self->zeroDigit)); + } + + /* Next the fractional part, if required */ + if (format_info.frac_digits + format_info.frac_hash == 0) { + if (format_info.add_decimal) + xmlBufferAdd(buffer, self->decimalPoint, + xsltUTF8Size(self->decimalPoint)); + } + else { + number -= floor(number); + if ((number != 0) || (format_info.frac_digits != 0)) { + xmlBufferAdd(buffer, self->decimalPoint, + xsltUTF8Size(self->decimalPoint)); + number = floor(scale * number + 0.5); + for (j = format_info.frac_hash; j > 0; j--) { + if (fmod(number, 10.0) >= 1.0) + break; /* for */ + number /= 10.0; + } + xsltNumberFormatDecimal(buffer, floor(number), self->zeroDigit[0], + format_info.frac_digits + j, + 0, 0, 0); + } + } + /* Put the suffix into the buffer */ + for (j = 0; j < suffix_length; j++) { + if ((pchar = *suffix++) == SYMBOL_QUOTE) { + len = xsltUTF8Size(suffix); + xmlBufferAdd(buffer, suffix, len); + suffix += len; + j += len - 1; /* length of symbol less length of escape */ + } else + xmlBufferAdd(buffer, &pchar, 1); + } + + *result = xmlStrdup(xmlBufferContent(buffer)); + xmlBufferFree(buffer); + return status; +} + diff --git a/libxslt/numbersInternals.h b/libxslt/numbersInternals.h new file mode 100644 index 0000000..8524592 --- /dev/null +++ b/libxslt/numbersInternals.h @@ -0,0 +1,73 @@ +/* + * Summary: Implementation of the XSLT number functions + * Description: Implementation of the XSLT number functions + * + * Copy: See Copyright for the status of this software. + * + * Author: Bjorn Reese <breese@users.sourceforge.net> and Daniel Veillard + */ + +#ifndef __XML_XSLT_NUMBERSINTERNALS_H__ +#define __XML_XSLT_NUMBERSINTERNALS_H__ + +#include <libxml/tree.h> +#include "xsltexports.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct _xsltCompMatch; + +/** + * xsltNumberData: + * + * This data structure is just a wrapper to pass xsl:number data in. + */ +typedef struct _xsltNumberData xsltNumberData; +typedef xsltNumberData *xsltNumberDataPtr; + +struct _xsltNumberData { + const xmlChar *level; + const xmlChar *count; + const xmlChar *from; + const xmlChar *value; + const xmlChar *format; + int has_format; + int digitsPerGroup; + int groupingCharacter; + int groupingCharacterLen; + xmlDocPtr doc; + xmlNodePtr node; + struct _xsltCompMatch *countPat; + struct _xsltCompMatch *fromPat; + + /* + * accelerators + */ +}; + +/** + * xsltFormatNumberInfo,: + * + * This data structure lists the various parameters needed to format numbers. + */ +typedef struct _xsltFormatNumberInfo xsltFormatNumberInfo; +typedef xsltFormatNumberInfo *xsltFormatNumberInfoPtr; + +struct _xsltFormatNumberInfo { + int integer_hash; /* Number of '#' in integer part */ + int integer_digits; /* Number of '0' in integer part */ + int frac_digits; /* Number of '0' in fractional part */ + int frac_hash; /* Number of '#' in fractional part */ + int group; /* Number of chars per display 'group' */ + int multiplier; /* Scaling for percent or permille */ + char add_decimal; /* Flag for whether decimal point appears in pattern */ + char is_multiplier_set; /* Flag to catch multiple occurences of percent/permille */ + char is_negative_pattern;/* Flag for processing -ve prefix/suffix */ +}; + +#ifdef __cplusplus +} +#endif +#endif /* __XML_XSLT_NUMBERSINTERNALS_H__ */ diff --git a/libxslt/pattern.c b/libxslt/pattern.c new file mode 100644 index 0000000..e211a01 --- /dev/null +++ b/libxslt/pattern.c @@ -0,0 +1,2593 @@ +/* + * pattern.c: Implemetation of the template match compilation and lookup + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +/* + * TODO: handle pathological cases like *[*[@a="b"]] + * TODO: detect [number] at compilation, optimize accordingly + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> + +#include <libxml/xmlmemory.h> +#include <libxml/tree.h> +#include <libxml/valid.h> +#include <libxml/hash.h> +#include <libxml/xmlerror.h> +#include <libxml/parserInternals.h> +#include <libxml/xpath.h> +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "imports.h" +#include "templates.h" +#include "keys.h" +#include "pattern.h" +#include "documents.h" + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_PATTERN +#endif + +/* + * Types are private: + */ + +typedef enum { + XSLT_OP_END=0, + XSLT_OP_ROOT, + XSLT_OP_ELEM, + XSLT_OP_ATTR, + XSLT_OP_PARENT, + XSLT_OP_ANCESTOR, + XSLT_OP_ID, + XSLT_OP_KEY, + XSLT_OP_NS, + XSLT_OP_ALL, + XSLT_OP_PI, + XSLT_OP_COMMENT, + XSLT_OP_TEXT, + XSLT_OP_NODE, + XSLT_OP_PREDICATE +} xsltOp; + +typedef enum { + AXIS_CHILD=1, + AXIS_ATTRIBUTE +} xsltAxis; + +typedef struct _xsltStepState xsltStepState; +typedef xsltStepState *xsltStepStatePtr; +struct _xsltStepState { + int step; + xmlNodePtr node; +}; + +typedef struct _xsltStepStates xsltStepStates; +typedef xsltStepStates *xsltStepStatesPtr; +struct _xsltStepStates { + int nbstates; + int maxstates; + xsltStepStatePtr states; +}; + +typedef struct _xsltStepOp xsltStepOp; +typedef xsltStepOp *xsltStepOpPtr; +struct _xsltStepOp { + xsltOp op; + xmlChar *value; + xmlChar *value2; + xmlChar *value3; + xmlXPathCompExprPtr comp; + /* + * Optimisations for count + */ + int previousExtra; + int indexExtra; + int lenExtra; +}; + +struct _xsltCompMatch { + struct _xsltCompMatch *next; /* siblings in the name hash */ + float priority; /* the priority */ + const xmlChar *pattern; /* the pattern */ + const xmlChar *mode; /* the mode */ + const xmlChar *modeURI; /* the mode URI */ + xsltTemplatePtr template; /* the associated template */ + + int direct; + /* TODO fix the statically allocated size steps[] */ + int nbStep; + int maxStep; + xmlNsPtr *nsList; /* the namespaces in scope */ + int nsNr; /* the number of namespaces in scope */ + xsltStepOpPtr steps; /* ops for computation */ +}; + +typedef struct _xsltParserContext xsltParserContext; +typedef xsltParserContext *xsltParserContextPtr; +struct _xsltParserContext { + xsltStylesheetPtr style; /* the stylesheet */ + xsltTransformContextPtr ctxt; /* the transformation or NULL */ + const xmlChar *cur; /* the current char being parsed */ + const xmlChar *base; /* the full expression */ + xmlDocPtr doc; /* the source document */ + xmlNodePtr elem; /* the source element */ + int error; /* error code */ + xsltCompMatchPtr comp; /* the result */ +}; + +/************************************************************************ + * * + * Type functions * + * * + ************************************************************************/ + +/** + * xsltNewCompMatch: + * + * Create a new XSLT CompMatch + * + * Returns the newly allocated xsltCompMatchPtr or NULL in case of error + */ +static xsltCompMatchPtr +xsltNewCompMatch(void) { + xsltCompMatchPtr cur; + + cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewCompMatch : out of memory error\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltCompMatch)); + cur->maxStep = 10; + cur->nbStep = 0; + cur-> steps = (xsltStepOpPtr) xmlMalloc(sizeof(xsltStepOp) * + cur->maxStep); + if (cur->steps == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewCompMatch : out of memory error\n"); + xmlFree(cur); + return(NULL); + } + cur->nsNr = 0; + cur->nsList = NULL; + cur->direct = 0; + return(cur); +} + +/** + * xsltFreeCompMatch: + * @comp: an XSLT comp + * + * Free up the memory allocated by @comp + */ +static void +xsltFreeCompMatch(xsltCompMatchPtr comp) { + xsltStepOpPtr op; + int i; + + if (comp == NULL) + return; + if (comp->pattern != NULL) + xmlFree((xmlChar *)comp->pattern); + if (comp->nsList != NULL) + xmlFree(comp->nsList); + for (i = 0;i < comp->nbStep;i++) { + op = &comp->steps[i]; + if (op->value != NULL) + xmlFree(op->value); + if (op->value2 != NULL) + xmlFree(op->value2); + if (op->value3 != NULL) + xmlFree(op->value3); + if (op->comp != NULL) + xmlXPathFreeCompExpr(op->comp); + } + xmlFree(comp->steps); + memset(comp, -1, sizeof(xsltCompMatch)); + xmlFree(comp); +} + +/** + * xsltFreeCompMatchList: + * @comp: an XSLT comp list + * + * Free up the memory allocated by all the elements of @comp + */ +void +xsltFreeCompMatchList(xsltCompMatchPtr comp) { + xsltCompMatchPtr cur; + + while (comp != NULL) { + cur = comp; + comp = comp->next; + xsltFreeCompMatch(cur); + } +} + +/** + * xsltNormalizeCompSteps: + * @payload: pointer to template hash table entry + * @data: pointer to the stylesheet + * @name: template match name + * + * This is a hashtable scanner function to normalize the compiled + * steps of an imported stylesheet. + */ +void xsltNormalizeCompSteps(void *payload, + void *data, const xmlChar *name ATTRIBUTE_UNUSED) { + xsltCompMatchPtr comp = payload; + xsltStylesheetPtr style = data; + int ix; + + for (ix = 0; ix < comp->nbStep; ix++) { + comp->steps[ix].previousExtra += style->extrasNr; + comp->steps[ix].indexExtra += style->extrasNr; + comp->steps[ix].lenExtra += style->extrasNr; + } +} + +/** + * xsltNewParserContext: + * @style: the stylesheet + * @ctxt: the transformation context, if done at run-time + * + * Create a new XSLT ParserContext + * + * Returns the newly allocated xsltParserContextPtr or NULL in case of error + */ +static xsltParserContextPtr +xsltNewParserContext(xsltStylesheetPtr style, xsltTransformContextPtr ctxt) { + xsltParserContextPtr cur; + + cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewParserContext : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltParserContext)); + cur->style = style; + cur->ctxt = ctxt; + return(cur); +} + +/** + * xsltFreeParserContext: + * @ctxt: an XSLT parser context + * + * Free up the memory allocated by @ctxt + */ +static void +xsltFreeParserContext(xsltParserContextPtr ctxt) { + if (ctxt == NULL) + return; + memset(ctxt, -1, sizeof(xsltParserContext)); + xmlFree(ctxt); +} + +/** + * xsltCompMatchAdd: + * @comp: the compiled match expression + * @op: an op + * @value: the first value + * @value2: the second value + * @novar: flag to set XML_XPATH_NOVAR + * + * Add an step to an XSLT Compiled Match + * + * Returns -1 in case of failure, 0 otherwise. + */ +static int +xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp, + xsltOp op, xmlChar * value, xmlChar * value2, int novar) +{ + if (comp->nbStep >= comp->maxStep) { + xsltStepOpPtr tmp; + + tmp = (xsltStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 * + sizeof(xsltStepOp)); + if (tmp == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltCompMatchAdd: memory re-allocation failure.\n"); + if (ctxt->style != NULL) + ctxt->style->errors++; + if (value) + xmlFree(value); + if (value2) + xmlFree(value2); + return (-1); + } + comp->maxStep *= 2; + comp->steps = tmp; + } + comp->steps[comp->nbStep].op = op; + comp->steps[comp->nbStep].value = value; + comp->steps[comp->nbStep].value2 = value2; + comp->steps[comp->nbStep].value3 = NULL; + comp->steps[comp->nbStep].comp = NULL; + if (ctxt->ctxt != NULL) { + comp->steps[comp->nbStep].previousExtra = + xsltAllocateExtraCtxt(ctxt->ctxt); + comp->steps[comp->nbStep].indexExtra = + xsltAllocateExtraCtxt(ctxt->ctxt); + comp->steps[comp->nbStep].lenExtra = + xsltAllocateExtraCtxt(ctxt->ctxt); + } else { + comp->steps[comp->nbStep].previousExtra = + xsltAllocateExtra(ctxt->style); + comp->steps[comp->nbStep].indexExtra = + xsltAllocateExtra(ctxt->style); + comp->steps[comp->nbStep].lenExtra = + xsltAllocateExtra(ctxt->style); + } + if (op == XSLT_OP_PREDICATE) { + xmlXPathContextPtr xctxt; + + if (ctxt->style != NULL) + xctxt = xmlXPathNewContext(ctxt->style->doc); + else + xctxt = xmlXPathNewContext(NULL); +#ifdef XML_XPATH_NOVAR + if (novar != 0) + xctxt->flags = XML_XPATH_NOVAR; +#endif + if (ctxt->style != NULL) + xctxt->dict = ctxt->style->dict; + comp->steps[comp->nbStep].comp = xmlXPathCtxtCompile(xctxt, value); + xmlXPathFreeContext(xctxt); + if (comp->steps[comp->nbStep].comp == NULL) { + xsltTransformError(NULL, ctxt->style, ctxt->elem, + "Failed to compile predicate\n"); + if (ctxt->style != NULL) + ctxt->style->errors++; + } + } + comp->nbStep++; + return (0); +} + +/** + * xsltSwapTopCompMatch: + * @comp: the compiled match expression + * + * reverse the two top steps. + */ +static void +xsltSwapTopCompMatch(xsltCompMatchPtr comp) { + int i; + int j = comp->nbStep - 1; + + if (j > 0) { + register xmlChar *tmp; + register xsltOp op; + register xmlXPathCompExprPtr expr; + register int t; + i = j - 1; + tmp = comp->steps[i].value; + comp->steps[i].value = comp->steps[j].value; + comp->steps[j].value = tmp; + tmp = comp->steps[i].value2; + comp->steps[i].value2 = comp->steps[j].value2; + comp->steps[j].value2 = tmp; + tmp = comp->steps[i].value3; + comp->steps[i].value3 = comp->steps[j].value3; + comp->steps[j].value3 = tmp; + op = comp->steps[i].op; + comp->steps[i].op = comp->steps[j].op; + comp->steps[j].op = op; + expr = comp->steps[i].comp; + comp->steps[i].comp = comp->steps[j].comp; + comp->steps[j].comp = expr; + t = comp->steps[i].previousExtra; + comp->steps[i].previousExtra = comp->steps[j].previousExtra; + comp->steps[j].previousExtra = t; + t = comp->steps[i].indexExtra; + comp->steps[i].indexExtra = comp->steps[j].indexExtra; + comp->steps[j].indexExtra = t; + t = comp->steps[i].lenExtra; + comp->steps[i].lenExtra = comp->steps[j].lenExtra; + comp->steps[j].lenExtra = t; + } +} + +/** + * xsltReverseCompMatch: + * @ctxt: the parser context + * @comp: the compiled match expression + * + * reverse all the stack of expressions + */ +static void +xsltReverseCompMatch(xsltParserContextPtr ctxt, xsltCompMatchPtr comp) { + int i = 0; + int j = comp->nbStep - 1; + + while (j > i) { + register xmlChar *tmp; + register xsltOp op; + register xmlXPathCompExprPtr expr; + register int t; + + tmp = comp->steps[i].value; + comp->steps[i].value = comp->steps[j].value; + comp->steps[j].value = tmp; + tmp = comp->steps[i].value2; + comp->steps[i].value2 = comp->steps[j].value2; + comp->steps[j].value2 = tmp; + tmp = comp->steps[i].value3; + comp->steps[i].value3 = comp->steps[j].value3; + comp->steps[j].value3 = tmp; + op = comp->steps[i].op; + comp->steps[i].op = comp->steps[j].op; + comp->steps[j].op = op; + expr = comp->steps[i].comp; + comp->steps[i].comp = comp->steps[j].comp; + comp->steps[j].comp = expr; + t = comp->steps[i].previousExtra; + comp->steps[i].previousExtra = comp->steps[j].previousExtra; + comp->steps[j].previousExtra = t; + t = comp->steps[i].indexExtra; + comp->steps[i].indexExtra = comp->steps[j].indexExtra; + comp->steps[j].indexExtra = t; + t = comp->steps[i].lenExtra; + comp->steps[i].lenExtra = comp->steps[j].lenExtra; + comp->steps[j].lenExtra = t; + j--; + i++; + } + xsltCompMatchAdd(ctxt, comp, XSLT_OP_END, NULL, NULL, 0); + + /* + * Detect consecutive XSLT_OP_PREDICATE and predicates on ops which + * haven't been optimized yet indicating a direct matching should be done. + */ + for (i = 0;i < comp->nbStep - 1;i++) { + xsltOp op = comp->steps[i].op; + + if ((op != XSLT_OP_ELEM) && + (op != XSLT_OP_ALL) && + (comp->steps[i + 1].op == XSLT_OP_PREDICATE)) { + + comp->direct = 1; + if (comp->pattern[0] != '/') { + xmlChar *query; + + query = xmlStrdup((const xmlChar *)"//"); + query = xmlStrcat(query, comp->pattern); + + xmlFree((xmlChar *) comp->pattern); + comp->pattern = query; + } + break; + } + } +} + +/************************************************************************ + * * + * The interpreter for the precompiled patterns * + * * + ************************************************************************/ + +static int +xsltPatPushState(xsltTransformContextPtr ctxt, xsltStepStates *states, + int step, xmlNodePtr node) { + if ((states->states == NULL) || (states->maxstates <= 0)) { + states->maxstates = 4; + states->nbstates = 0; + states->states = xmlMalloc(4 * sizeof(xsltStepState)); + } + else if (states->maxstates <= states->nbstates) { + xsltStepState *tmp; + + tmp = (xsltStepStatePtr) xmlRealloc(states->states, + 2 * states->maxstates * sizeof(xsltStepState)); + if (tmp == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltPatPushState: memory re-allocation failure.\n"); + ctxt->state = XSLT_STATE_STOPPED; + return(-1); + } + states->states = tmp; + states->maxstates *= 2; + } + states->states[states->nbstates].step = step; + states->states[states->nbstates++].node = node; +#if 0 + fprintf(stderr, "Push: %d, %s\n", step, node->name); +#endif + return(0); +} + +/** + * xsltTestCompMatchDirect: + * @ctxt: a XSLT process context + * @comp: the precompiled pattern + * @node: a node + * @nsList: the namespaces in scope + * @nsNr: the number of namespaces in scope + * + * Test whether the node matches the pattern, do a direct evalutation + * and not a step by step evaluation. + * + * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure + */ +static int +xsltTestCompMatchDirect(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp, + xmlNodePtr node, xmlNsPtr *nsList, int nsNr) { + xsltStepOpPtr sel = NULL; + xmlDocPtr prevdoc; + xmlDocPtr doc; + xmlXPathObjectPtr list; + int ix, j; + int nocache = 0; + int isRVT; + + doc = node->doc; + if (XSLT_IS_RES_TREE_FRAG(doc)) + isRVT = 1; + else + isRVT = 0; + sel = &comp->steps[0]; /* store extra in first step arbitrarily */ + + prevdoc = (xmlDocPtr) + XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); + ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival); + list = (xmlXPathObjectPtr) + XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra); + + if ((list == NULL) || (prevdoc != doc)) { + xmlXPathObjectPtr newlist; + xmlNodePtr parent = node->parent; + xmlDocPtr olddoc; + xmlNodePtr oldnode; + int oldNsNr, oldContextSize, oldProximityPosition; + xmlNsPtr *oldNamespaces; + + oldnode = ctxt->xpathCtxt->node; + olddoc = ctxt->xpathCtxt->doc; + oldNsNr = ctxt->xpathCtxt->nsNr; + oldNamespaces = ctxt->xpathCtxt->namespaces; + oldContextSize = ctxt->xpathCtxt->contextSize; + oldProximityPosition = ctxt->xpathCtxt->proximityPosition; + ctxt->xpathCtxt->node = node; + ctxt->xpathCtxt->doc = doc; + ctxt->xpathCtxt->namespaces = nsList; + ctxt->xpathCtxt->nsNr = nsNr; + newlist = xmlXPathEval(comp->pattern, ctxt->xpathCtxt); + ctxt->xpathCtxt->node = oldnode; + ctxt->xpathCtxt->doc = olddoc; + ctxt->xpathCtxt->namespaces = oldNamespaces; + ctxt->xpathCtxt->nsNr = oldNsNr; + ctxt->xpathCtxt->contextSize = oldContextSize; + ctxt->xpathCtxt->proximityPosition = oldProximityPosition; + if (newlist == NULL) + return(-1); + if (newlist->type != XPATH_NODESET) { + xmlXPathFreeObject(newlist); + return(-1); + } + ix = 0; + + if ((parent == NULL) || (node->doc == NULL) || isRVT) + nocache = 1; + + if (nocache == 0) { + if (list != NULL) + xmlXPathFreeObject(list); + list = newlist; + + XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) = + (void *) list; + XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = + (void *) doc; + XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = + 0; + XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) = + (xmlFreeFunc) xmlXPathFreeObject; + } else + list = newlist; + } + if ((list->nodesetval == NULL) || + (list->nodesetval->nodeNr <= 0)) { + if (nocache == 1) + xmlXPathFreeObject(list); + return(0); + } + /* TODO: store the index and use it for the scan */ + if (ix == 0) { + for (j = 0;j < list->nodesetval->nodeNr;j++) { + if (list->nodesetval->nodeTab[j] == node) { + if (nocache == 1) + xmlXPathFreeObject(list); + return(1); + } + } + } else { + } + if (nocache == 1) + xmlXPathFreeObject(list); + return(0); +} + +/** + * xsltTestPredicateMatch: + * @ctxt: a XSLT process context + * @comp: the precompiled pattern + * @node: a node + * @step: the predicate step + * @sel: the previous step + * + * Test whether the node matches the predicate + * + * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure + */ +static int +xsltTestPredicateMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp, + xmlNodePtr node, xsltStepOpPtr step, + xsltStepOpPtr sel) { + xmlNodePtr oldNode; + xmlDocPtr doc; + int oldCS, oldCP; + int pos = 0, len = 0; + int isRVT; + int match; + + if (step->value == NULL) + return(0); + if (step->comp == NULL) + return(0); + + doc = node->doc; + if (XSLT_IS_RES_TREE_FRAG(doc)) + isRVT = 1; + else + isRVT = 0; + + /* + * Recompute contextSize and proximityPosition. + * + * TODO: Make this work for additional ops. Currently, only XSLT_OP_ELEM + * and XSLT_OP_ALL are supported. + */ + oldCS = ctxt->xpathCtxt->contextSize; + oldCP = ctxt->xpathCtxt->proximityPosition; + if ((sel != NULL) && + (sel->op == XSLT_OP_ELEM) && + (sel->value != NULL) && + (node->type == XML_ELEMENT_NODE) && + (node->parent != NULL)) { + xmlNodePtr previous; + int nocache = 0; + + previous = (xmlNodePtr) + XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); + if ((previous != NULL) && + (previous->parent == node->parent)) { + /* + * just walk back to adjust the index + */ + int indx = 0; + xmlNodePtr sibling = node; + + while (sibling != NULL) { + if (sibling == previous) + break; + if ((sibling->type == XML_ELEMENT_NODE) && + (previous->name != NULL) && + (sibling->name != NULL) && + (previous->name[0] == sibling->name[0]) && + (xmlStrEqual(previous->name, sibling->name))) + { + if ((sel->value2 == NULL) || + ((sibling->ns != NULL) && + (xmlStrEqual(sel->value2, sibling->ns->href)))) + indx++; + } + sibling = sibling->prev; + } + if (sibling == NULL) { + /* hum going backward in document order ... */ + indx = 0; + sibling = node; + while (sibling != NULL) { + if (sibling == previous) + break; + if ((sibling->type == XML_ELEMENT_NODE) && + (previous->name != NULL) && + (sibling->name != NULL) && + (previous->name[0] == sibling->name[0]) && + (xmlStrEqual(previous->name, sibling->name))) + { + if ((sel->value2 == NULL) || + ((sibling->ns != NULL) && + (xmlStrEqual(sel->value2, + sibling->ns->href)))) + { + indx--; + } + } + sibling = sibling->next; + } + } + if (sibling != NULL) { + pos = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) + indx; + /* + * If the node is in a Value Tree we need to + * save len, but cannot cache the node! + * (bugs 153137 and 158840) + */ + if (node->doc != NULL) { + len = XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival); + if (!isRVT) { + XSLT_RUNTIME_EXTRA(ctxt, + sel->previousExtra, ptr) = node; + XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos; + } + } + } else + pos = 0; + } else { + /* + * recompute the index + */ + xmlNodePtr parent = node->parent; + xmlNodePtr siblings = NULL; + + if (parent) siblings = parent->children; + + while (siblings != NULL) { + if (siblings->type == XML_ELEMENT_NODE) { + if (siblings == node) { + len++; + pos = len; + } else if ((node->name != NULL) && + (siblings->name != NULL) && + (node->name[0] == siblings->name[0]) && + (xmlStrEqual(node->name, siblings->name))) { + if ((sel->value2 == NULL) || + ((siblings->ns != NULL) && + (xmlStrEqual(sel->value2, siblings->ns->href)))) + len++; + } + } + siblings = siblings->next; + } + if ((parent == NULL) || (node->doc == NULL)) + nocache = 1; + else { + while (parent->parent != NULL) + parent = parent->parent; + if (((parent->type != XML_DOCUMENT_NODE) && + (parent->type != XML_HTML_DOCUMENT_NODE)) || + (parent != (xmlNodePtr) node->doc)) + nocache = 1; + } + } + if (pos != 0) { + ctxt->xpathCtxt->contextSize = len; + ctxt->xpathCtxt->proximityPosition = pos; + /* + * If the node is in a Value Tree we cannot + * cache it ! + */ + if ((!isRVT) && (node->doc != NULL) && + (nocache == 0)) { + XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node; + XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos; + XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = len; + } + } + } else if ((sel != NULL) && (sel->op == XSLT_OP_ALL) && + (node->type == XML_ELEMENT_NODE)) { + xmlNodePtr previous; + int nocache = 0; + + previous = (xmlNodePtr) + XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); + if ((previous != NULL) && + (previous->parent == node->parent)) { + /* + * just walk back to adjust the index + */ + int indx = 0; + xmlNodePtr sibling = node; + + while (sibling != NULL) { + if (sibling == previous) + break; + if (sibling->type == XML_ELEMENT_NODE) + indx++; + sibling = sibling->prev; + } + if (sibling == NULL) { + /* hum going backward in document order ... */ + indx = 0; + sibling = node; + while (sibling != NULL) { + if (sibling == previous) + break; + if (sibling->type == XML_ELEMENT_NODE) + indx--; + sibling = sibling->next; + } + } + if (sibling != NULL) { + pos = XSLT_RUNTIME_EXTRA(ctxt, + sel->indexExtra, ival) + indx; + /* + * If the node is in a Value Tree we cannot + * cache it ! + */ + if ((node->doc != NULL) && !isRVT) { + len = XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival); + XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node; + XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos; + } + } else + pos = 0; + } else { + /* + * recompute the index + */ + xmlNodePtr parent = node->parent; + xmlNodePtr siblings = NULL; + + if (parent) siblings = parent->children; + + while (siblings != NULL) { + if (siblings->type == XML_ELEMENT_NODE) { + len++; + if (siblings == node) { + pos = len; + } + } + siblings = siblings->next; + } + if ((parent == NULL) || (node->doc == NULL)) + nocache = 1; + else { + while (parent->parent != NULL) + parent = parent->parent; + if (((parent->type != XML_DOCUMENT_NODE) && + (parent->type != XML_HTML_DOCUMENT_NODE)) || + (parent != (xmlNodePtr) node->doc)) + nocache = 1; + } + } + if (pos != 0) { + ctxt->xpathCtxt->contextSize = len; + ctxt->xpathCtxt->proximityPosition = pos; + /* + * If the node is in a Value Tree we cannot + * cache it ! + */ + if ((node->doc != NULL) && (nocache == 0) && !isRVT) { + XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node; + XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos; + XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = len; + } + } + } + + oldNode = ctxt->node; + ctxt->node = node; + + match = xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList, comp->nsNr); + + if (pos != 0) { + ctxt->xpathCtxt->contextSize = oldCS; + ctxt->xpathCtxt->proximityPosition = oldCP; + } + ctxt->node = oldNode; + + return match; +} + +/** + * xsltTestCompMatch: + * @ctxt: a XSLT process context + * @comp: the precompiled pattern + * @node: a node + * @mode: the mode name or NULL + * @modeURI: the mode URI or NULL + * + * Test whether the node matches the pattern + * + * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure + */ +static int +xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp, + xmlNodePtr matchNode, const xmlChar *mode, + const xmlChar *modeURI) { + int i; + xmlNodePtr node = matchNode; + xsltStepOpPtr step, sel = NULL; + xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */ + + if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) { + xsltTransformError(ctxt, NULL, node, + "xsltTestCompMatch: null arg\n"); + return(-1); + } + if (mode != NULL) { + if (comp->mode == NULL) + return(0); + /* + * both mode strings must be interned on the stylesheet dictionary + */ + if (comp->mode != mode) + return(0); + } else { + if (comp->mode != NULL) + return(0); + } + if (modeURI != NULL) { + if (comp->modeURI == NULL) + return(0); + /* + * both modeURI strings must be interned on the stylesheet dictionary + */ + if (comp->modeURI != modeURI) + return(0); + } else { + if (comp->modeURI != NULL) + return(0); + } + + i = 0; +restart: + for (;i < comp->nbStep;i++) { + step = &comp->steps[i]; + if (step->op != XSLT_OP_PREDICATE) + sel = step; + switch (step->op) { + case XSLT_OP_END: + goto found; + case XSLT_OP_ROOT: + if ((node->type == XML_DOCUMENT_NODE) || +#ifdef LIBXML_DOCB_ENABLED + (node->type == XML_DOCB_DOCUMENT_NODE) || +#endif + (node->type == XML_HTML_DOCUMENT_NODE)) + continue; + if ((node->type == XML_ELEMENT_NODE) && (node->name[0] == ' ')) + continue; + goto rollback; + case XSLT_OP_ELEM: + if (node->type != XML_ELEMENT_NODE) + goto rollback; + if (step->value == NULL) + continue; + if (step->value[0] != node->name[0]) + goto rollback; + if (!xmlStrEqual(step->value, node->name)) + goto rollback; + + /* Namespace test */ + if (node->ns == NULL) { + if (step->value2 != NULL) + goto rollback; + } else if (node->ns->href != NULL) { + if (step->value2 == NULL) + goto rollback; + if (!xmlStrEqual(step->value2, node->ns->href)) + goto rollback; + } + continue; + case XSLT_OP_ATTR: + if (node->type != XML_ATTRIBUTE_NODE) + goto rollback; + if (step->value != NULL) { + if (step->value[0] != node->name[0]) + goto rollback; + if (!xmlStrEqual(step->value, node->name)) + goto rollback; + } + /* Namespace test */ + if (node->ns == NULL) { + if (step->value2 != NULL) + goto rollback; + } else if (step->value2 != NULL) { + if (!xmlStrEqual(step->value2, node->ns->href)) + goto rollback; + } + continue; + case XSLT_OP_PARENT: + if ((node->type == XML_DOCUMENT_NODE) || + (node->type == XML_HTML_DOCUMENT_NODE) || +#ifdef LIBXML_DOCB_ENABLED + (node->type == XML_DOCB_DOCUMENT_NODE) || +#endif + (node->type == XML_NAMESPACE_DECL)) + goto rollback; + node = node->parent; + if (node == NULL) + goto rollback; + if (step->value == NULL) + continue; + if (step->value[0] != node->name[0]) + goto rollback; + if (!xmlStrEqual(step->value, node->name)) + goto rollback; + /* Namespace test */ + if (node->ns == NULL) { + if (step->value2 != NULL) + goto rollback; + } else if (node->ns->href != NULL) { + if (step->value2 == NULL) + goto rollback; + if (!xmlStrEqual(step->value2, node->ns->href)) + goto rollback; + } + continue; + case XSLT_OP_ANCESTOR: + /* TODO: implement coalescing of ANCESTOR/NODE ops */ + if (step->value == NULL) { + step = &comp->steps[i+1]; + if (step->op == XSLT_OP_ROOT) + goto found; + /* added NS, ID and KEY as a result of bug 168208 */ + if ((step->op != XSLT_OP_ELEM) && + (step->op != XSLT_OP_ALL) && + (step->op != XSLT_OP_NS) && + (step->op != XSLT_OP_ID) && + (step->op != XSLT_OP_KEY)) + goto rollback; + } + if (node == NULL) + goto rollback; + if ((node->type == XML_DOCUMENT_NODE) || + (node->type == XML_HTML_DOCUMENT_NODE) || +#ifdef LIBXML_DOCB_ENABLED + (node->type == XML_DOCB_DOCUMENT_NODE) || +#endif + (node->type == XML_NAMESPACE_DECL)) + goto rollback; + node = node->parent; + if ((step->op != XSLT_OP_ELEM) && step->op != XSLT_OP_ALL) { + xsltPatPushState(ctxt, &states, i, node); + continue; + } + i++; + if (step->value == NULL) { + xsltPatPushState(ctxt, &states, i - 1, node); + continue; + } + while (node != NULL) { + if ((node->type == XML_ELEMENT_NODE) && + (step->value[0] == node->name[0]) && + (xmlStrEqual(step->value, node->name))) { + /* Namespace test */ + if (node->ns == NULL) { + if (step->value2 == NULL) + break; + } else if (node->ns->href != NULL) { + if ((step->value2 != NULL) && + (xmlStrEqual(step->value2, node->ns->href))) + break; + } + } + node = node->parent; + } + if (node == NULL) + goto rollback; + xsltPatPushState(ctxt, &states, i - 1, node); + continue; + case XSLT_OP_ID: { + /* TODO Handle IDs decently, must be done differently */ + xmlAttrPtr id; + + if (node->type != XML_ELEMENT_NODE) + goto rollback; + + id = xmlGetID(node->doc, step->value); + if ((id == NULL) || (id->parent != node)) + goto rollback; + break; + } + case XSLT_OP_KEY: { + xmlNodeSetPtr list; + int indx; + + list = xsltGetKey(ctxt, step->value, + step->value3, step->value2); + if (list == NULL) + goto rollback; + for (indx = 0;indx < list->nodeNr;indx++) + if (list->nodeTab[indx] == node) + break; + if (indx >= list->nodeNr) + goto rollback; + break; + } + case XSLT_OP_NS: + if (node->type != XML_ELEMENT_NODE) + goto rollback; + if (node->ns == NULL) { + if (step->value != NULL) + goto rollback; + } else if (node->ns->href != NULL) { + if (step->value == NULL) + goto rollback; + if (!xmlStrEqual(step->value, node->ns->href)) + goto rollback; + } + break; + case XSLT_OP_ALL: + if (node->type != XML_ELEMENT_NODE) + goto rollback; + break; + case XSLT_OP_PREDICATE: { + /* + * When there is cascading XSLT_OP_PREDICATE or a predicate + * after an op which hasn't been optimized yet, then use a + * direct computation approach. It's not done directly + * at the beginning of the routine to filter out as much + * as possible this costly computation. + */ + if (comp->direct) { + if (states.states != NULL) { + /* Free the rollback states */ + xmlFree(states.states); + } + return(xsltTestCompMatchDirect(ctxt, comp, matchNode, + comp->nsList, comp->nsNr)); + } + + if (!xsltTestPredicateMatch(ctxt, comp, node, step, sel)) + goto rollback; + + break; + } + case XSLT_OP_PI: + if (node->type != XML_PI_NODE) + goto rollback; + if (step->value != NULL) { + if (!xmlStrEqual(step->value, node->name)) + goto rollback; + } + break; + case XSLT_OP_COMMENT: + if (node->type != XML_COMMENT_NODE) + goto rollback; + break; + case XSLT_OP_TEXT: + if ((node->type != XML_TEXT_NODE) && + (node->type != XML_CDATA_SECTION_NODE)) + goto rollback; + break; + case XSLT_OP_NODE: + switch (node->type) { + case XML_ELEMENT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_TEXT_NODE: + break; + default: + goto rollback; + } + break; + } + } +found: + if (states.states != NULL) { + /* Free the rollback states */ + xmlFree(states.states); + } + return(1); +rollback: + /* got an error try to rollback */ + if (states.states == NULL) + return(0); + if (states.nbstates <= 0) { + xmlFree(states.states); + return(0); + } + states.nbstates--; + i = states.states[states.nbstates].step; + node = states.states[states.nbstates].node; +#if 0 + fprintf(stderr, "Pop: %d, %s\n", i, node->name); +#endif + goto restart; +} + +/** + * xsltTestCompMatchList: + * @ctxt: a XSLT process context + * @node: a node + * @comp: the precompiled pattern list + * + * Test whether the node matches one of the patterns in the list + * + * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure + */ +int +xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node, + xsltCompMatchPtr comp) { + int ret; + + if ((ctxt == NULL) || (node == NULL)) + return(-1); + while (comp != NULL) { + ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL); + if (ret == 1) + return(1); + comp = comp->next; + } + return(0); +} + +/************************************************************************ + * * + * Dedicated parser for templates * + * * + ************************************************************************/ + +#define CUR (*ctxt->cur) +#define SKIP(val) ctxt->cur += (val) +#define NXT(val) ctxt->cur[(val)] +#define CUR_PTR ctxt->cur + +#define SKIP_BLANKS \ + while (IS_BLANK_CH(CUR)) NEXT + +#define CURRENT (*ctxt->cur) +#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) + + +#define PUSH(op, val, val2, novar) \ + if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto error; + +#define SWAP() \ + xsltSwapTopCompMatch(ctxt->comp); + +#define XSLT_ERROR(X) \ + { xsltError(ctxt, __FILE__, __LINE__, X); \ + ctxt->error = (X); return; } + +#define XSLT_ERROR0(X) \ + { xsltError(ctxt, __FILE__, __LINE__, X); \ + ctxt->error = (X); return(0); } + +/** + * xsltScanLiteral: + * @ctxt: the XPath Parser context + * + * Parse an XPath Litteral: + * + * [29] Literal ::= '"' [^"]* '"' + * | "'" [^']* "'" + * + * Returns the Literal parsed or NULL + */ + +static xmlChar * +xsltScanLiteral(xsltParserContextPtr ctxt) { + const xmlChar *q, *cur; + xmlChar *ret = NULL; + int val, len; + + SKIP_BLANKS; + if (CUR == '"') { + NEXT; + cur = q = CUR_PTR; + val = xmlStringCurrentChar(NULL, cur, &len); + while ((IS_CHAR(val)) && (val != '"')) { + cur += len; + val = xmlStringCurrentChar(NULL, cur, &len); + } + if (!IS_CHAR(val)) { + ctxt->error = 1; + return(NULL); + } else { + ret = xmlStrndup(q, cur - q); + } + cur += len; + CUR_PTR = cur; + } else if (CUR == '\'') { + NEXT; + cur = q = CUR_PTR; + val = xmlStringCurrentChar(NULL, cur, &len); + while ((IS_CHAR(val)) && (val != '\'')) { + cur += len; + val = xmlStringCurrentChar(NULL, cur, &len); + } + if (!IS_CHAR(val)) { + ctxt->error = 1; + return(NULL); + } else { + ret = xmlStrndup(q, cur - q); + } + cur += len; + CUR_PTR = cur; + } else { + /* XP_ERROR(XPATH_START_LITERAL_ERROR); */ + ctxt->error = 1; + return(NULL); + } + return(ret); +} + +/** + * xsltScanNCName: + * @ctxt: the XPath Parser context + * + * Parses a non qualified name + * + * Returns the Name parsed or NULL + */ + +static xmlChar * +xsltScanNCName(xsltParserContextPtr ctxt) { + const xmlChar *q, *cur; + xmlChar *ret = NULL; + int val, len; + + SKIP_BLANKS; + + cur = q = CUR_PTR; + val = xmlStringCurrentChar(NULL, cur, &len); + if (!IS_LETTER(val) && (val != '_')) + return(NULL); + + while ((IS_LETTER(val)) || (IS_DIGIT(val)) || + (val == '.') || (val == '-') || + (val == '_') || + (IS_COMBINING(val)) || + (IS_EXTENDER(val))) { + cur += len; + val = xmlStringCurrentChar(NULL, cur, &len); + } + ret = xmlStrndup(q, cur - q); + CUR_PTR = cur; + return(ret); +} + +/* + * xsltCompileIdKeyPattern: + * @ctxt: the compilation context + * @name: a preparsed name + * @aid: whether id/key are allowed there + * @novar: flag to prohibit xslt var + * + * Compile the XSLT LocationIdKeyPattern + * [3] IdKeyPattern ::= 'id' '(' Literal ')' + * | 'key' '(' Literal ',' Literal ')' + * + * also handle NodeType and PI from: + * + * [7] NodeTest ::= NameTest + * | NodeType '(' ')' + * | 'processing-instruction' '(' Literal ')' + */ +static void +xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, + int aid, int novar, xsltAxis axis) { + xmlChar *lit = NULL; + xmlChar *lit2 = NULL; + + if (CUR != '(') { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : ( expected\n"); + ctxt->error = 1; + return; + } + if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) { + if (axis != 0) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : NodeTest expected\n"); + ctxt->error = 1; + return; + } + NEXT; + SKIP_BLANKS; + lit = xsltScanLiteral(ctxt); + if (ctxt->error) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : Literal expected\n"); + return; + } + SKIP_BLANKS; + if (CUR != ')') { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : ) expected\n"); + xmlFree(lit); + ctxt->error = 1; + return; + } + NEXT; + PUSH(XSLT_OP_ID, lit, NULL, novar); + lit = NULL; + } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) { + if (axis != 0) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : NodeTest expected\n"); + ctxt->error = 1; + return; + } + NEXT; + SKIP_BLANKS; + lit = xsltScanLiteral(ctxt); + if (ctxt->error) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : Literal expected\n"); + return; + } + SKIP_BLANKS; + if (CUR != ',') { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : , expected\n"); + xmlFree(lit); + ctxt->error = 1; + return; + } + NEXT; + SKIP_BLANKS; + lit2 = xsltScanLiteral(ctxt); + if (ctxt->error) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : Literal expected\n"); + xmlFree(lit); + return; + } + SKIP_BLANKS; + if (CUR != ')') { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : ) expected\n"); + xmlFree(lit); + xmlFree(lit2); + ctxt->error = 1; + return; + } + NEXT; + /* URGENT TODO: support namespace in keys */ + PUSH(XSLT_OP_KEY, lit, lit2, novar); + lit = NULL; + lit2 = NULL; + } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) { + NEXT; + SKIP_BLANKS; + if (CUR != ')') { + lit = xsltScanLiteral(ctxt); + if (ctxt->error) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : Literal expected\n"); + return; + } + SKIP_BLANKS; + if (CUR != ')') { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : ) expected\n"); + ctxt->error = 1; + return; + } + } + NEXT; + PUSH(XSLT_OP_PI, lit, NULL, novar); + lit = NULL; + } else if (xmlStrEqual(name, (const xmlChar *)"text")) { + NEXT; + SKIP_BLANKS; + if (CUR != ')') { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : ) expected\n"); + ctxt->error = 1; + return; + } + NEXT; + PUSH(XSLT_OP_TEXT, NULL, NULL, novar); + } else if (xmlStrEqual(name, (const xmlChar *)"comment")) { + NEXT; + SKIP_BLANKS; + if (CUR != ')') { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : ) expected\n"); + ctxt->error = 1; + return; + } + NEXT; + PUSH(XSLT_OP_COMMENT, NULL, NULL, novar); + } else if (xmlStrEqual(name, (const xmlChar *)"node")) { + NEXT; + SKIP_BLANKS; + if (CUR != ')') { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : ) expected\n"); + ctxt->error = 1; + return; + } + NEXT; + if (axis == AXIS_ATTRIBUTE) { + PUSH(XSLT_OP_ATTR, NULL, NULL, novar); + } + else { + PUSH(XSLT_OP_NODE, NULL, NULL, novar); + } + } else if (aid) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n"); + ctxt->error = 1; + return; + } else { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileIdKeyPattern : node type\n"); + ctxt->error = 1; + return; + } +error: + return; +} + +/** + * xsltCompileStepPattern: + * @ctxt: the compilation context + * @token: a posible precompiled name + * @novar: flag to prohibit xslt variables from pattern + * + * Compile the XSLT StepPattern and generates a precompiled + * form suitable for fast matching. + * + * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate* + * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier + * | ('child' | 'attribute') '::' + * from XPath + * [7] NodeTest ::= NameTest + * | NodeType '(' ')' + * | 'processing-instruction' '(' Literal ')' + * [8] Predicate ::= '[' PredicateExpr ']' + * [9] PredicateExpr ::= Expr + * [13] AbbreviatedAxisSpecifier ::= '@'? + * [37] NameTest ::= '*' | NCName ':' '*' | QName + */ + +static void +xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) { + xmlChar *name = NULL; + const xmlChar *URI = NULL; + xmlChar *URL = NULL; + int level; + xsltAxis axis = 0; + + SKIP_BLANKS; + if ((token == NULL) && (CUR == '@')) { + NEXT; + axis = AXIS_ATTRIBUTE; + } +parse_node_test: + if (token == NULL) + token = xsltScanNCName(ctxt); + if (token == NULL) { + if (CUR == '*') { + NEXT; + if (axis == AXIS_ATTRIBUTE) { + PUSH(XSLT_OP_ATTR, NULL, NULL, novar); + } + else { + PUSH(XSLT_OP_ALL, NULL, NULL, novar); + } + goto parse_predicate; + } else { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileStepPattern : Name expected\n"); + ctxt->error = 1; + goto error; + } + } + + + SKIP_BLANKS; + if (CUR == '(') { + xsltCompileIdKeyPattern(ctxt, token, 0, novar, axis); + xmlFree(token); + token = NULL; + if (ctxt->error) + goto error; + } else if (CUR == ':') { + NEXT; + if (CUR != ':') { + xmlChar *prefix = token; + xmlNsPtr ns; + + /* + * This is a namespace match + */ + token = xsltScanNCName(ctxt); + ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix); + if (ns == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileStepPattern : no namespace bound to prefix %s\n", + prefix); + xmlFree(prefix); + prefix=NULL; + ctxt->error = 1; + goto error; + } else { + URL = xmlStrdup(ns->href); + } + xmlFree(prefix); + prefix=NULL; + if (token == NULL) { + if (CUR == '*') { + NEXT; + if (axis == AXIS_ATTRIBUTE) { + PUSH(XSLT_OP_ATTR, NULL, URL, novar); + URL = NULL; + } + else { + PUSH(XSLT_OP_NS, URL, NULL, novar); + URL = NULL; + } + } else { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileStepPattern : Name expected\n"); + ctxt->error = 1; + goto error; + } + } else { + if (axis == AXIS_ATTRIBUTE) { + PUSH(XSLT_OP_ATTR, token, URL, novar); + token = NULL; + URL = NULL; + } + else { + PUSH(XSLT_OP_ELEM, token, URL, novar); + token = NULL; + URL = NULL; + } + } + } else { + if (axis != 0) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileStepPattern : NodeTest expected\n"); + ctxt->error = 1; + goto error; + } + NEXT; + if (xmlStrEqual(token, (const xmlChar *) "child")) { + axis = AXIS_CHILD; + } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) { + axis = AXIS_ATTRIBUTE; + } else { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileStepPattern : 'child' or 'attribute' expected\n"); + ctxt->error = 1; + goto error; + } + xmlFree(token); + token = NULL; + SKIP_BLANKS; + token = xsltScanNCName(ctxt); + goto parse_node_test; + } + } else { + URI = xsltGetQNameURI(ctxt->elem, &token); + if (token == NULL) { + ctxt->error = 1; + goto error; + } + if (URI != NULL) + URL = xmlStrdup(URI); + if (axis == AXIS_ATTRIBUTE) { + PUSH(XSLT_OP_ATTR, token, URL, novar); + token = NULL; + URL = NULL; + } + else { + PUSH(XSLT_OP_ELEM, token, URL, novar); + token = NULL; + URL = NULL; + } + } +parse_predicate: + SKIP_BLANKS; + level = 0; + while (CUR == '[') { + const xmlChar *q; + xmlChar *ret = NULL; + + level++; + NEXT; + q = CUR_PTR; + while (CUR != 0) { + /* Skip over nested predicates */ + if (CUR == '[') + level++; + else if (CUR == ']') { + level--; + if (level == 0) + break; + } else if (CUR == '"') { + NEXT; + while ((CUR != 0) && (CUR != '"')) + NEXT; + } else if (CUR == '\'') { + NEXT; + while ((CUR != 0) && (CUR != '\'')) + NEXT; + } + NEXT; + } + if (CUR == 0) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileStepPattern : ']' expected\n"); + ctxt->error = 1; + return; + } + ret = xmlStrndup(q, CUR_PTR - q); + PUSH(XSLT_OP_PREDICATE, ret, NULL, novar); + ret = NULL; + /* push the predicate lower than local test */ + SWAP(); + NEXT; + SKIP_BLANKS; + } + return; +error: + if (token != NULL) + xmlFree(token); + if (name != NULL) + xmlFree(name); +} + +/** + * xsltCompileRelativePathPattern: + * @comp: the compilation context + * @token: a posible precompiled name + * @novar: flag to prohibit xslt variables + * + * Compile the XSLT RelativePathPattern and generates a precompiled + * form suitable for fast matching. + * + * [4] RelativePathPattern ::= StepPattern + * | RelativePathPattern '/' StepPattern + * | RelativePathPattern '//' StepPattern + */ +static void +xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) { + xsltCompileStepPattern(ctxt, token, novar); + if (ctxt->error) + goto error; + SKIP_BLANKS; + while ((CUR != 0) && (CUR != '|')) { + if ((CUR == '/') && (NXT(1) == '/')) { + PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar); + NEXT; + NEXT; + SKIP_BLANKS; + xsltCompileStepPattern(ctxt, NULL, novar); + } else if (CUR == '/') { + PUSH(XSLT_OP_PARENT, NULL, NULL, novar); + NEXT; + SKIP_BLANKS; + if ((CUR != 0) && (CUR != '|')) { + xsltCompileRelativePathPattern(ctxt, NULL, novar); + } + } else { + ctxt->error = 1; + } + if (ctxt->error) + goto error; + SKIP_BLANKS; + } +error: + return; +} + +/** + * xsltCompileLocationPathPattern: + * @ctxt: the compilation context + * @novar: flag to prohibit xslt variables + * + * Compile the XSLT LocationPathPattern and generates a precompiled + * form suitable for fast matching. + * + * [2] LocationPathPattern ::= '/' RelativePathPattern? + * | IdKeyPattern (('/' | '//') RelativePathPattern)? + * | '//'? RelativePathPattern + */ +static void +xsltCompileLocationPathPattern(xsltParserContextPtr ctxt, int novar) { + SKIP_BLANKS; + if ((CUR == '/') && (NXT(1) == '/')) { + /* + * since we reverse the query + * a leading // can be safely ignored + */ + NEXT; + NEXT; + ctxt->comp->priority = 0.5; /* '//' means not 0 priority */ + xsltCompileRelativePathPattern(ctxt, NULL, novar); + } else if (CUR == '/') { + /* + * We need to find root as the parent + */ + NEXT; + SKIP_BLANKS; + PUSH(XSLT_OP_ROOT, NULL, NULL, novar); + if ((CUR != 0) && (CUR != '|')) { + PUSH(XSLT_OP_PARENT, NULL, NULL, novar); + xsltCompileRelativePathPattern(ctxt, NULL, novar); + } + } else if (CUR == '*') { + xsltCompileRelativePathPattern(ctxt, NULL, novar); + } else if (CUR == '@') { + xsltCompileRelativePathPattern(ctxt, NULL, novar); + } else { + xmlChar *name; + name = xsltScanNCName(ctxt); + if (name == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltCompileLocationPathPattern : Name expected\n"); + ctxt->error = 1; + return; + } + SKIP_BLANKS; + if ((CUR == '(') && !xmlXPathIsNodeType(name)) { + xsltCompileIdKeyPattern(ctxt, name, 1, novar, 0); + xmlFree(name); + name = NULL; + if ((CUR == '/') && (NXT(1) == '/')) { + PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar); + NEXT; + NEXT; + SKIP_BLANKS; + xsltCompileRelativePathPattern(ctxt, NULL, novar); + } else if (CUR == '/') { + PUSH(XSLT_OP_PARENT, NULL, NULL, novar); + NEXT; + SKIP_BLANKS; + xsltCompileRelativePathPattern(ctxt, NULL, novar); + } + return; + } + xsltCompileRelativePathPattern(ctxt, name, novar); + } +error: + return; +} + +/** + * xsltCompilePatternInternal: + * @pattern: an XSLT pattern + * @doc: the containing document + * @node: the containing element + * @style: the stylesheet + * @runtime: the transformation context, if done at run-time + * @novar: flag to prohibit xslt variables + * + * Compile the XSLT pattern and generates a list of precompiled form suitable + * for fast matching. + * + * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern + * + * Returns the generated pattern list or NULL in case of failure + */ + +static xsltCompMatchPtr +xsltCompilePatternInternal(const xmlChar *pattern, xmlDocPtr doc, + xmlNodePtr node, xsltStylesheetPtr style, + xsltTransformContextPtr runtime, int novar) { + xsltParserContextPtr ctxt = NULL; + xsltCompMatchPtr element, first = NULL, previous = NULL; + int current, start, end, level, j; + + if (pattern == NULL) { + xsltTransformError(NULL, NULL, node, + "xsltCompilePattern : NULL pattern\n"); + return(NULL); + } + + ctxt = xsltNewParserContext(style, runtime); + if (ctxt == NULL) + return(NULL); + ctxt->doc = doc; + ctxt->elem = node; + current = end = 0; + while (pattern[current] != 0) { + start = current; + while (IS_BLANK_CH(pattern[current])) + current++; + end = current; + level = 0; + while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) { + if (pattern[end] == '[') + level++; + else if (pattern[end] == ']') + level--; + else if (pattern[end] == '\'') { + end++; + while ((pattern[end] != 0) && (pattern[end] != '\'')) + end++; + } else if (pattern[end] == '"') { + end++; + while ((pattern[end] != 0) && (pattern[end] != '"')) + end++; + } + if (pattern[end] == 0) + break; + end++; + } + if (current == end) { + xsltTransformError(NULL, NULL, node, + "xsltCompilePattern : NULL pattern\n"); + goto error; + } + element = xsltNewCompMatch(); + if (element == NULL) { + goto error; + } + if (first == NULL) + first = element; + else if (previous != NULL) + previous->next = element; + previous = element; + + ctxt->comp = element; + ctxt->base = xmlStrndup(&pattern[start], end - start); + if (ctxt->base == NULL) + goto error; + ctxt->cur = &(ctxt->base)[current - start]; + element->pattern = ctxt->base; + element->nsList = xmlGetNsList(doc, node); + j = 0; + if (element->nsList != NULL) { + while (element->nsList[j] != NULL) + j++; + } + element->nsNr = j; + + +#ifdef WITH_XSLT_DEBUG_PATTERN + xsltGenericDebug(xsltGenericDebugContext, + "xsltCompilePattern : parsing '%s'\n", + element->pattern); +#endif + /* + Preset default priority to be zero. + This may be changed by xsltCompileLocationPathPattern. + */ + element->priority = 0; + xsltCompileLocationPathPattern(ctxt, novar); + if (ctxt->error) { + xsltTransformError(NULL, style, node, + "xsltCompilePattern : failed to compile '%s'\n", + element->pattern); + if (style != NULL) style->errors++; + goto error; + } + + /* + * Reverse for faster interpretation. + */ + xsltReverseCompMatch(ctxt, element); + + /* + * Set-up the priority + */ + if (element->priority == 0) { /* if not yet determined */ + if (((element->steps[0].op == XSLT_OP_ELEM) || + (element->steps[0].op == XSLT_OP_ATTR) || + (element->steps[0].op == XSLT_OP_PI)) && + (element->steps[0].value != NULL) && + (element->steps[1].op == XSLT_OP_END)) { + ; /* previously preset */ + } else if ((element->steps[0].op == XSLT_OP_ATTR) && + (element->steps[0].value2 != NULL) && + (element->steps[1].op == XSLT_OP_END)) { + element->priority = -0.25; + } else if ((element->steps[0].op == XSLT_OP_NS) && + (element->steps[0].value != NULL) && + (element->steps[1].op == XSLT_OP_END)) { + element->priority = -0.25; + } else if ((element->steps[0].op == XSLT_OP_ATTR) && + (element->steps[0].value == NULL) && + (element->steps[0].value2 == NULL) && + (element->steps[1].op == XSLT_OP_END)) { + element->priority = -0.5; + } else if (((element->steps[0].op == XSLT_OP_PI) || + (element->steps[0].op == XSLT_OP_TEXT) || + (element->steps[0].op == XSLT_OP_ALL) || + (element->steps[0].op == XSLT_OP_NODE) || + (element->steps[0].op == XSLT_OP_COMMENT)) && + (element->steps[1].op == XSLT_OP_END)) { + element->priority = -0.5; + } else { + element->priority = 0.5; + } + } +#ifdef WITH_XSLT_DEBUG_PATTERN + xsltGenericDebug(xsltGenericDebugContext, + "xsltCompilePattern : parsed %s, default priority %f\n", + element->pattern, element->priority); +#endif + if (pattern[end] == '|') + end++; + current = end; + } + if (end == 0) { + xsltTransformError(NULL, style, node, + "xsltCompilePattern : NULL pattern\n"); + if (style != NULL) style->errors++; + goto error; + } + + xsltFreeParserContext(ctxt); + return(first); + +error: + if (ctxt != NULL) + xsltFreeParserContext(ctxt); + if (first != NULL) + xsltFreeCompMatchList(first); + return(NULL); +} + +/** + * xsltCompilePattern: + * @pattern: an XSLT pattern + * @doc: the containing document + * @node: the containing element + * @style: the stylesheet + * @runtime: the transformation context, if done at run-time + * + * Compile the XSLT pattern and generates a list of precompiled form suitable + * for fast matching. + * + * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern + * + * Returns the generated pattern list or NULL in case of failure + */ + +xsltCompMatchPtr +xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc, + xmlNodePtr node, xsltStylesheetPtr style, + xsltTransformContextPtr runtime) { + return (xsltCompilePatternInternal(pattern, doc, node, style, runtime, 0)); +} + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +/** + * xsltAddTemplate: + * @style: an XSLT stylesheet + * @cur: an XSLT template + * @mode: the mode name or NULL + * @modeURI: the mode URI or NULL + * + * Register the XSLT pattern associated to @cur + * + * Returns -1 in case of error, 0 otherwise + */ +int +xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur, + const xmlChar *mode, const xmlChar *modeURI) { + xsltCompMatchPtr pat, list, next; + /* + * 'top' will point to style->xxxMatch ptr - declaring as 'void' + * avoids gcc 'type-punned pointer' warning. + */ + void **top = NULL; + const xmlChar *name = NULL; + float priority; /* the priority */ + + if ((style == NULL) || (cur == NULL)) + return(-1); + + /* Register named template */ + if (cur->name != NULL) { + if (style->namedTemplates == NULL) { + style->namedTemplates = xmlHashCreate(10); + if (style->namedTemplates == NULL) + return(-1); + } + else { + void *dup = xmlHashLookup2(style->namedTemplates, cur->name, + cur->nameURI); + if (dup != NULL) { + xsltTransformError(NULL, style, cur->elem, + "xsl:template: error duplicate name '%s'\n", + cur->name); + style->errors++; + return(-1); + } + } + + xmlHashAddEntry2(style->namedTemplates, cur->name, cur->nameURI, cur); + } + + if (cur->match == NULL) + return(0); + + priority = cur->priority; + pat = xsltCompilePatternInternal(cur->match, style->doc, cur->elem, + style, NULL, 1); + if (pat == NULL) + return(-1); + while (pat) { + next = pat->next; + pat->next = NULL; + name = NULL; + + pat->template = cur; + if (mode != NULL) + pat->mode = xmlDictLookup(style->dict, mode, -1); + if (modeURI != NULL) + pat->modeURI = xmlDictLookup(style->dict, modeURI, -1); + if (priority != XSLT_PAT_NO_PRIORITY) + pat->priority = priority; + + /* + * insert it in the hash table list corresponding to its lookup name + */ + switch (pat->steps[0].op) { + case XSLT_OP_ATTR: + if (pat->steps[0].value != NULL) + name = pat->steps[0].value; + else + top = &(style->attrMatch); + break; + case XSLT_OP_PARENT: + case XSLT_OP_ANCESTOR: + top = &(style->elemMatch); + break; + case XSLT_OP_ROOT: + top = &(style->rootMatch); + break; + case XSLT_OP_KEY: + top = &(style->keyMatch); + break; + case XSLT_OP_ID: + /* TODO optimize ID !!! */ + case XSLT_OP_NS: + case XSLT_OP_ALL: + top = &(style->elemMatch); + break; + case XSLT_OP_END: + case XSLT_OP_PREDICATE: + xsltTransformError(NULL, style, NULL, + "xsltAddTemplate: invalid compiled pattern\n"); + xsltFreeCompMatch(pat); + return(-1); + /* + * TODO: some flags at the top level about type based patterns + * would be faster than inclusion in the hash table. + */ + case XSLT_OP_PI: + if (pat->steps[0].value != NULL) + name = pat->steps[0].value; + else + top = &(style->piMatch); + break; + case XSLT_OP_COMMENT: + top = &(style->commentMatch); + break; + case XSLT_OP_TEXT: + top = &(style->textMatch); + break; + case XSLT_OP_ELEM: + case XSLT_OP_NODE: + if (pat->steps[0].value != NULL) + name = pat->steps[0].value; + else + top = &(style->elemMatch); + break; + } + if (name != NULL) { + if (style->templatesHash == NULL) { + style->templatesHash = xmlHashCreate(1024); + if (style->templatesHash == NULL) { + xsltFreeCompMatch(pat); + return(-1); + } + xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat); + } else { + list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash, + name, mode, modeURI); + if (list == NULL) { + xmlHashAddEntry3(style->templatesHash, name, + mode, modeURI, pat); + } else { + /* + * Note '<=' since one must choose among the matching + * template rules that are left, the one that occurs + * last in the stylesheet + */ + if (list->priority <= pat->priority) { + pat->next = list; + xmlHashUpdateEntry3(style->templatesHash, name, + mode, modeURI, pat, NULL); + } else { + while (list->next != NULL) { + if (list->next->priority <= pat->priority) + break; + list = list->next; + } + pat->next = list->next; + list->next = pat; + } + } + } + } else if (top != NULL) { + list = *top; + if (list == NULL) { + *top = pat; + pat->next = NULL; + } else if (list->priority <= pat->priority) { + pat->next = list; + *top = pat; + } else { + while (list->next != NULL) { + if (list->next->priority <= pat->priority) + break; + list = list->next; + } + pat->next = list->next; + list->next = pat; + } + } else { + xsltTransformError(NULL, style, NULL, + "xsltAddTemplate: invalid compiled pattern\n"); + xsltFreeCompMatch(pat); + return(-1); + } +#ifdef WITH_XSLT_DEBUG_PATTERN + if (mode) + xsltGenericDebug(xsltGenericDebugContext, + "added pattern : '%s' mode '%s' priority %f\n", + pat->pattern, pat->mode, pat->priority); + else + xsltGenericDebug(xsltGenericDebugContext, + "added pattern : '%s' priority %f\n", + pat->pattern, pat->priority); +#endif + + pat = next; + } + return(0); +} + +static int +xsltComputeAllKeys(xsltTransformContextPtr ctxt, xmlNodePtr contextNode) +{ + if ((ctxt == NULL) || (contextNode == NULL)) { + xsltTransformError(ctxt, NULL, ctxt->inst, + "Internal error in xsltComputeAllKeys(): " + "Bad arguments.\n"); + return(-1); + } + + if (ctxt->document == NULL) { + /* + * The document info will only be NULL if we have a RTF. + */ + if (contextNode->doc->_private != NULL) + goto doc_info_mismatch; + /* + * On-demand creation of the document info (needed for keys). + */ + ctxt->document = xsltNewDocument(ctxt, contextNode->doc); + if (ctxt->document == NULL) + return(-1); + } + return xsltInitAllDocKeys(ctxt); + +doc_info_mismatch: + xsltTransformError(ctxt, NULL, ctxt->inst, + "Internal error in xsltComputeAllKeys(): " + "The context's document info doesn't match the " + "document info of the current result tree.\n"); + ctxt->state = XSLT_STATE_STOPPED; + return(-1); +} + +/** + * xsltGetTemplate: + * @ctxt: a XSLT process context + * @node: the node being processed + * @style: the current style + * + * Finds the template applying to this node, if @style is non-NULL + * it means one needs to look for the next imported template in scope. + * + * Returns the xsltTemplatePtr or NULL if not found + */ +xsltTemplatePtr +xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, + xsltStylesheetPtr style) +{ + xsltStylesheetPtr curstyle; + xsltTemplatePtr ret = NULL; + const xmlChar *name = NULL; + xsltCompMatchPtr list = NULL; + float priority; + int keyed = 0; + + if ((ctxt == NULL) || (node == NULL)) + return(NULL); + + if (style == NULL) { + curstyle = ctxt->style; + } else { + curstyle = xsltNextImport(style); + } + + while ((curstyle != NULL) && (curstyle != style)) { + priority = XSLT_PAT_NO_PRIORITY; + /* TODO : handle IDs/keys here ! */ + if (curstyle->templatesHash != NULL) { + /* + * Use the top name as selector + */ + switch (node->type) { + case XML_ELEMENT_NODE: + if (node->name[0] == ' ') + break; + case XML_ATTRIBUTE_NODE: + case XML_PI_NODE: + name = node->name; + break; + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_COMMENT_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_DOCUMENT_TYPE_NODE: + case XML_DOCUMENT_FRAG_NODE: + case XML_NOTATION_NODE: + case XML_DTD_NODE: + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + case XML_ENTITY_DECL: + case XML_NAMESPACE_DECL: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + break; + default: + return(NULL); + + } + } + if (name != NULL) { + /* + * find the list of applicable expressions based on the name + */ + list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash, + name, ctxt->mode, ctxt->modeURI); + } else + list = NULL; + while (list != NULL) { + if (xsltTestCompMatch(ctxt, list, node, + ctxt->mode, ctxt->modeURI)) { + ret = list->template; + priority = list->priority; + break; + } + list = list->next; + } + list = NULL; + + /* + * find alternate generic matches + */ + switch (node->type) { + case XML_ELEMENT_NODE: + if (node->name[0] == ' ') + list = curstyle->rootMatch; + else + list = curstyle->elemMatch; + if (node->psvi != NULL) keyed = 1; + break; + case XML_ATTRIBUTE_NODE: { + xmlAttrPtr attr; + + list = curstyle->attrMatch; + attr = (xmlAttrPtr) node; + if (attr->psvi != NULL) keyed = 1; + break; + } + case XML_PI_NODE: + list = curstyle->piMatch; + if (node->psvi != NULL) keyed = 1; + break; + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: { + xmlDocPtr doc; + + list = curstyle->rootMatch; + doc = (xmlDocPtr) node; + if (doc->psvi != NULL) keyed = 1; + break; + } + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + list = curstyle->textMatch; + if (node->psvi != NULL) keyed = 1; + break; + case XML_COMMENT_NODE: + list = curstyle->commentMatch; + if (node->psvi != NULL) keyed = 1; + break; + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_DOCUMENT_TYPE_NODE: + case XML_DOCUMENT_FRAG_NODE: + case XML_NOTATION_NODE: + case XML_DTD_NODE: + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + case XML_ENTITY_DECL: + case XML_NAMESPACE_DECL: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + break; + default: + break; + } + while ((list != NULL) && + ((ret == NULL) || (list->priority > priority))) { + if (xsltTestCompMatch(ctxt, list, node, + ctxt->mode, ctxt->modeURI)) { + ret = list->template; + priority = list->priority; + break; + } + list = list->next; + } + /* + * Some of the tests for elements can also apply to documents + */ + if ((node->type == XML_DOCUMENT_NODE) || + (node->type == XML_HTML_DOCUMENT_NODE) || + (node->type == XML_TEXT_NODE)) { + list = curstyle->elemMatch; + while ((list != NULL) && + ((ret == NULL) || (list->priority > priority))) { + if (xsltTestCompMatch(ctxt, list, node, + ctxt->mode, ctxt->modeURI)) { + ret = list->template; + priority = list->priority; + break; + } + list = list->next; + } + } else if ((node->type == XML_PI_NODE) || + (node->type == XML_COMMENT_NODE)) { + list = curstyle->elemMatch; + while ((list != NULL) && + ((ret == NULL) || (list->priority > priority))) { + if (xsltTestCompMatch(ctxt, list, node, + ctxt->mode, ctxt->modeURI)) { + ret = list->template; + priority = list->priority; + break; + } + list = list->next; + } + } + +keyed_match: + if (keyed) { + list = curstyle->keyMatch; + while ((list != NULL) && + ((ret == NULL) || (list->priority > priority))) { + if (xsltTestCompMatch(ctxt, list, node, + ctxt->mode, ctxt->modeURI)) { + ret = list->template; + priority = list->priority; + break; + } + list = list->next; + } + } + else if (ctxt->hasTemplKeyPatterns && + ((ctxt->document == NULL) || + (ctxt->document->nbKeysComputed < ctxt->nbKeys))) + { + /* + * Compute all remaining keys for this document. + * + * REVISIT TODO: I think this could be further optimized. + */ + if (xsltComputeAllKeys(ctxt, node) == -1) + goto error; + + switch (node->type) { + case XML_ELEMENT_NODE: + if (node->psvi != NULL) keyed = 1; + break; + case XML_ATTRIBUTE_NODE: + if (((xmlAttrPtr) node)->psvi != NULL) keyed = 1; + break; + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_COMMENT_NODE: + case XML_PI_NODE: + if (node->psvi != NULL) keyed = 1; + break; + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + if (((xmlDocPtr) node)->psvi != NULL) keyed = 1; + break; + default: + break; + } + if (keyed) + goto keyed_match; + } + if (ret != NULL) + return(ret); + + /* + * Cycle on next curstylesheet import. + */ + curstyle = xsltNextImport(curstyle); + } + +error: + return(NULL); +} + +/** + * xsltCleanupTemplates: + * @style: an XSLT stylesheet + * + * Cleanup the state of the templates used by the stylesheet and + * the ones it imports. + */ +void +xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED) { +} + +/** + * xsltFreeTemplateHashes: + * @style: an XSLT stylesheet + * + * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism + */ +void +xsltFreeTemplateHashes(xsltStylesheetPtr style) { + if (style->templatesHash != NULL) + xmlHashFree((xmlHashTablePtr) style->templatesHash, + (xmlHashDeallocator) xsltFreeCompMatchList); + if (style->rootMatch != NULL) + xsltFreeCompMatchList(style->rootMatch); + if (style->keyMatch != NULL) + xsltFreeCompMatchList(style->keyMatch); + if (style->elemMatch != NULL) + xsltFreeCompMatchList(style->elemMatch); + if (style->attrMatch != NULL) + xsltFreeCompMatchList(style->attrMatch); + if (style->parentMatch != NULL) + xsltFreeCompMatchList(style->parentMatch); + if (style->textMatch != NULL) + xsltFreeCompMatchList(style->textMatch); + if (style->piMatch != NULL) + xsltFreeCompMatchList(style->piMatch); + if (style->commentMatch != NULL) + xsltFreeCompMatchList(style->commentMatch); + if (style->namedTemplates != NULL) + xmlHashFree(style->namedTemplates, NULL); +} + diff --git a/libxslt/pattern.h b/libxslt/pattern.h new file mode 100644 index 0000000..eb21be3 --- /dev/null +++ b/libxslt/pattern.h @@ -0,0 +1,81 @@ +/* + * Summary: interface for the pattern matching used in template matches. + * Description: the implementation of the lookup of the right template + * for a given node must be really fast in order to keep + * decent performances. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_PATTERN_H__ +#define __XML_XSLT_PATTERN_H__ + +#include "xsltInternals.h" +#include "xsltexports.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * xsltCompMatch: + * + * Data structure used for the implementation of patterns. + * It is kept private (in pattern.c). + */ +typedef struct _xsltCompMatch xsltCompMatch; +typedef xsltCompMatch *xsltCompMatchPtr; + +/* + * Pattern related interfaces. + */ + +XSLTPUBFUN xsltCompMatchPtr XSLTCALL + xsltCompilePattern (const xmlChar *pattern, + xmlDocPtr doc, + xmlNodePtr node, + xsltStylesheetPtr style, + xsltTransformContextPtr runtime); +XSLTPUBFUN void XSLTCALL + xsltFreeCompMatchList (xsltCompMatchPtr comp); +XSLTPUBFUN int XSLTCALL + xsltTestCompMatchList (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xsltCompMatchPtr comp); +XSLTPUBFUN void XSLTCALL + xsltNormalizeCompSteps (void *payload, + void *data, + const xmlChar *name); + +/* + * Template related interfaces. + */ +XSLTPUBFUN int XSLTCALL + xsltAddTemplate (xsltStylesheetPtr style, + xsltTemplatePtr cur, + const xmlChar *mode, + const xmlChar *modeURI); +XSLTPUBFUN xsltTemplatePtr XSLTCALL + xsltGetTemplate (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xsltStylesheetPtr style); +XSLTPUBFUN void XSLTCALL + xsltFreeTemplateHashes (xsltStylesheetPtr style); +XSLTPUBFUN void XSLTCALL + xsltCleanupTemplates (xsltStylesheetPtr style); + +#if 0 +int xsltMatchPattern (xsltTransformContextPtr ctxt, + xmlNodePtr node, + const xmlChar *pattern, + xmlDocPtr ctxtdoc, + xmlNodePtr ctxtnode); +#endif +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_PATTERN_H__ */ + diff --git a/libxslt/preproc.c b/libxslt/preproc.c new file mode 100644 index 0000000..9a7de53 --- /dev/null +++ b/libxslt/preproc.c @@ -0,0 +1,2376 @@ +/* + * preproc.c: Preprocessing of style operations + * + * References: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * Michael Kay "XSLT Programmer's Reference" pp 637-643 + * Writing Multiple Output Files + * + * XSLT-1.1 Working Draft + * http://www.w3.org/TR/xslt11#multiple-output + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> + +#include <libxml/xmlmemory.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/valid.h> +#include <libxml/hash.h> +#include <libxml/uri.h> +#include <libxml/encoding.h> +#include <libxml/xmlerror.h> +#include "xslt.h" +#include "xsltutils.h" +#include "xsltInternals.h" +#include "transform.h" +#include "templates.h" +#include "variables.h" +#include "numbersInternals.h" +#include "preproc.h" +#include "extra.h" +#include "imports.h" +#include "extensions.h" +#include "pattern.h" + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_PREPROC +#endif + +const xmlChar *xsltExtMarker = (const xmlChar *) "Extension Element"; + +/************************************************************************ + * * + * Grammar checks * + * * + ************************************************************************/ + +#ifdef XSLT_REFACTORED + /* + * Grammar checks are now performed in xslt.c. + */ +#else +/** + * xsltCheckTopLevelElement: + * @style: the XSLT stylesheet + * @inst: the XSLT instruction + * @err: raise an error or not + * + * Check that the instruction is instanciated as a top level element. + * + * Returns -1 in case of error, 0 if failed and 1 in case of success + */ +static int +xsltCheckTopLevelElement(xsltStylesheetPtr style, xmlNodePtr inst, int err) { + xmlNodePtr parent; + if ((style == NULL) || (inst == NULL) || (inst->ns == NULL)) + return(-1); + + parent = inst->parent; + if (parent == NULL) { + if (err) { + xsltTransformError(NULL, style, inst, + "internal problem: element has no parent\n"); + style->errors++; + } + return(0); + } + if ((parent->ns == NULL) || (parent->type != XML_ELEMENT_NODE) || + ((parent->ns != inst->ns) && + (!xmlStrEqual(parent->ns->href, inst->ns->href))) || + ((!xmlStrEqual(parent->name, BAD_CAST "stylesheet")) && + (!xmlStrEqual(parent->name, BAD_CAST "transform")))) { + if (err) { + xsltTransformError(NULL, style, inst, + "element %s only allowed as child of stylesheet\n", + inst->name); + style->errors++; + } + return(0); + } + return(1); +} + +/** + * xsltCheckInstructionElement: + * @style: the XSLT stylesheet + * @inst: the XSLT instruction + * + * Check that the instruction is instanciated as an instruction element. + */ +static void +xsltCheckInstructionElement(xsltStylesheetPtr style, xmlNodePtr inst) { + xmlNodePtr parent; + int has_ext; + + if ((style == NULL) || (inst == NULL) || (inst->ns == NULL) || + (style->literal_result)) + return; + + has_ext = (style->extInfos != NULL); + + parent = inst->parent; + if (parent == NULL) { + xsltTransformError(NULL, style, inst, + "internal problem: element has no parent\n"); + style->errors++; + return; + } + while ((parent != NULL) && (parent->type != XML_DOCUMENT_NODE)) { + if (((parent->ns == inst->ns) || + ((parent->ns != NULL) && + (xmlStrEqual(parent->ns->href, inst->ns->href)))) && + ((xmlStrEqual(parent->name, BAD_CAST "template")) || + (xmlStrEqual(parent->name, BAD_CAST "param")) || + (xmlStrEqual(parent->name, BAD_CAST "attribute")) || + (xmlStrEqual(parent->name, BAD_CAST "variable")))) { + return; + } + + /* + * if we are within an extension element all bets are off + * about the semantic there e.g. xsl:param within func:function + */ + if ((has_ext) && (parent->ns != NULL) && + (xmlHashLookup(style->extInfos, parent->ns->href) != NULL)) + return; + + parent = parent->parent; + } + xsltTransformError(NULL, style, inst, + "element %s only allowed within a template, variable or param\n", + inst->name); + style->errors++; +} + +/** + * xsltCheckParentElement: + * @style: the XSLT stylesheet + * @inst: the XSLT instruction + * @allow1: allowed parent1 + * @allow2: allowed parent2 + * + * Check that the instruction is instanciated as the childre of one of the + * possible parents. + */ +static void +xsltCheckParentElement(xsltStylesheetPtr style, xmlNodePtr inst, + const xmlChar *allow1, const xmlChar *allow2) { + xmlNodePtr parent; + + if ((style == NULL) || (inst == NULL) || (inst->ns == NULL) || + (style->literal_result)) + return; + + parent = inst->parent; + if (parent == NULL) { + xsltTransformError(NULL, style, inst, + "internal problem: element has no parent\n"); + style->errors++; + return; + } + if (((parent->ns == inst->ns) || + ((parent->ns != NULL) && + (xmlStrEqual(parent->ns->href, inst->ns->href)))) && + ((xmlStrEqual(parent->name, allow1)) || + (xmlStrEqual(parent->name, allow2)))) { + return; + } + + if (style->extInfos != NULL) { + while ((parent != NULL) && (parent->type != XML_DOCUMENT_NODE)) { + /* + * if we are within an extension element all bets are off + * about the semantic there e.g. xsl:param within func:function + */ + if ((parent->ns != NULL) && + (xmlHashLookup(style->extInfos, parent->ns->href) != NULL)) + return; + + parent = parent->parent; + } + } + xsltTransformError(NULL, style, inst, + "element %s is not allowed within that context\n", + inst->name); + style->errors++; +} +#endif + +/************************************************************************ + * * + * handling of precomputed data * + * * + ************************************************************************/ + +/** + * xsltNewStylePreComp: + * @style: the XSLT stylesheet + * @type: the construct type + * + * Create a new XSLT Style precomputed block + * + * Returns the newly allocated specialized structure + * or NULL in case of error + */ +static xsltStylePreCompPtr +xsltNewStylePreComp(xsltStylesheetPtr style, xsltStyleType type) { + xsltStylePreCompPtr cur; +#ifdef XSLT_REFACTORED + size_t size; +#endif + + if (style == NULL) + return(NULL); + +#ifdef XSLT_REFACTORED + /* + * URGENT TODO: Use specialized factory functions in order + * to avoid this ugliness. + */ + switch (type) { + case XSLT_FUNC_COPY: + size = sizeof(xsltStyleItemCopy); break; + case XSLT_FUNC_SORT: + size = sizeof(xsltStyleItemSort); break; + case XSLT_FUNC_TEXT: + size = sizeof(xsltStyleItemText); break; + case XSLT_FUNC_ELEMENT: + size = sizeof(xsltStyleItemElement); break; + case XSLT_FUNC_ATTRIBUTE: + size = sizeof(xsltStyleItemAttribute); break; + case XSLT_FUNC_COMMENT: + size = sizeof(xsltStyleItemComment); break; + case XSLT_FUNC_PI: + size = sizeof(xsltStyleItemPI); break; + case XSLT_FUNC_COPYOF: + size = sizeof(xsltStyleItemCopyOf); break; + case XSLT_FUNC_VALUEOF: + size = sizeof(xsltStyleItemValueOf); break;; + case XSLT_FUNC_NUMBER: + size = sizeof(xsltStyleItemNumber); break; + case XSLT_FUNC_APPLYIMPORTS: + size = sizeof(xsltStyleItemApplyImports); break; + case XSLT_FUNC_CALLTEMPLATE: + size = sizeof(xsltStyleItemCallTemplate); break; + case XSLT_FUNC_APPLYTEMPLATES: + size = sizeof(xsltStyleItemApplyTemplates); break; + case XSLT_FUNC_CHOOSE: + size = sizeof(xsltStyleItemChoose); break; + case XSLT_FUNC_IF: + size = sizeof(xsltStyleItemIf); break; + case XSLT_FUNC_FOREACH: + size = sizeof(xsltStyleItemForEach); break; + case XSLT_FUNC_DOCUMENT: + size = sizeof(xsltStyleItemDocument); break; + case XSLT_FUNC_WITHPARAM: + size = sizeof(xsltStyleItemWithParam); break; + case XSLT_FUNC_PARAM: + size = sizeof(xsltStyleItemParam); break; + case XSLT_FUNC_VARIABLE: + size = sizeof(xsltStyleItemVariable); break; + case XSLT_FUNC_WHEN: + size = sizeof(xsltStyleItemWhen); break; + case XSLT_FUNC_OTHERWISE: + size = sizeof(xsltStyleItemOtherwise); break; + default: + xsltTransformError(NULL, style, NULL, + "xsltNewStylePreComp : invalid type %d\n", type); + style->errors++; + return(NULL); + } + /* + * Create the structure. + */ + cur = (xsltStylePreCompPtr) xmlMalloc(size); + if (cur == NULL) { + xsltTransformError(NULL, style, NULL, + "xsltNewStylePreComp : malloc failed\n"); + style->errors++; + return(NULL); + } + memset(cur, 0, size); + +#else /* XSLT_REFACTORED */ + /* + * Old behaviour. + */ + cur = (xsltStylePreCompPtr) xmlMalloc(sizeof(xsltStylePreComp)); + if (cur == NULL) { + xsltTransformError(NULL, style, NULL, + "xsltNewStylePreComp : malloc failed\n"); + style->errors++; + return(NULL); + } + memset(cur, 0, sizeof(xsltStylePreComp)); +#endif /* XSLT_REFACTORED */ + + /* + * URGENT TODO: Better to move this to spezialized factory functions. + */ + cur->type = type; + switch (cur->type) { + case XSLT_FUNC_COPY: + cur->func = (xsltTransformFunction) xsltCopy;break; + case XSLT_FUNC_SORT: + cur->func = (xsltTransformFunction) xsltSort;break; + case XSLT_FUNC_TEXT: + cur->func = (xsltTransformFunction) xsltText;break; + case XSLT_FUNC_ELEMENT: + cur->func = (xsltTransformFunction) xsltElement;break; + case XSLT_FUNC_ATTRIBUTE: + cur->func = (xsltTransformFunction) xsltAttribute;break; + case XSLT_FUNC_COMMENT: + cur->func = (xsltTransformFunction) xsltComment;break; + case XSLT_FUNC_PI: + cur->func = (xsltTransformFunction) xsltProcessingInstruction; + break; + case XSLT_FUNC_COPYOF: + cur->func = (xsltTransformFunction) xsltCopyOf;break; + case XSLT_FUNC_VALUEOF: + cur->func = (xsltTransformFunction) xsltValueOf;break; + case XSLT_FUNC_NUMBER: + cur->func = (xsltTransformFunction) xsltNumber;break; + case XSLT_FUNC_APPLYIMPORTS: + cur->func = (xsltTransformFunction) xsltApplyImports;break; + case XSLT_FUNC_CALLTEMPLATE: + cur->func = (xsltTransformFunction) xsltCallTemplate;break; + case XSLT_FUNC_APPLYTEMPLATES: + cur->func = (xsltTransformFunction) xsltApplyTemplates;break; + case XSLT_FUNC_CHOOSE: + cur->func = (xsltTransformFunction) xsltChoose;break; + case XSLT_FUNC_IF: + cur->func = (xsltTransformFunction) xsltIf;break; + case XSLT_FUNC_FOREACH: + cur->func = (xsltTransformFunction) xsltForEach;break; + case XSLT_FUNC_DOCUMENT: + cur->func = (xsltTransformFunction) xsltDocumentElem;break; + case XSLT_FUNC_WITHPARAM: + case XSLT_FUNC_PARAM: + case XSLT_FUNC_VARIABLE: + case XSLT_FUNC_WHEN: + break; + default: + if (cur->func == NULL) { + xsltTransformError(NULL, style, NULL, + "xsltNewStylePreComp : no function for type %d\n", type); + style->errors++; + } + } + cur->next = style->preComps; + style->preComps = (xsltElemPreCompPtr) cur; + + return(cur); +} + +/** + * xsltFreeStylePreComp: + * @comp: an XSLT Style precomputed block + * + * Free up the memory allocated by @comp + */ +static void +xsltFreeStylePreComp(xsltStylePreCompPtr comp) { + if (comp == NULL) + return; +#ifdef XSLT_REFACTORED + /* + * URGENT TODO: Implement destructors. + */ + switch (comp->type) { + case XSLT_FUNC_LITERAL_RESULT_ELEMENT: + break; + case XSLT_FUNC_COPY: + break; + case XSLT_FUNC_SORT: { + xsltStyleItemSortPtr item = (xsltStyleItemSortPtr) comp; + if (item->locale != (xsltLocale)0) + xsltFreeLocale(item->locale); + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_TEXT: + break; + case XSLT_FUNC_ELEMENT: + break; + case XSLT_FUNC_ATTRIBUTE: + break; + case XSLT_FUNC_COMMENT: + break; + case XSLT_FUNC_PI: + break; + case XSLT_FUNC_COPYOF: { + xsltStyleItemCopyOfPtr item = (xsltStyleItemCopyOfPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_VALUEOF: { + xsltStyleItemValueOfPtr item = (xsltStyleItemValueOfPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_NUMBER: { + xsltStyleItemNumberPtr item = (xsltStyleItemNumberPtr) comp; + if (item->numdata.countPat != NULL) + xsltFreeCompMatchList(item->numdata.countPat); + if (item->numdata.fromPat != NULL) + xsltFreeCompMatchList(item->numdata.fromPat); + } + break; + case XSLT_FUNC_APPLYIMPORTS: + break; + case XSLT_FUNC_CALLTEMPLATE: + break; + case XSLT_FUNC_APPLYTEMPLATES: { + xsltStyleItemApplyTemplatesPtr item = + (xsltStyleItemApplyTemplatesPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_CHOOSE: + break; + case XSLT_FUNC_IF: { + xsltStyleItemIfPtr item = (xsltStyleItemIfPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_FOREACH: { + xsltStyleItemForEachPtr item = + (xsltStyleItemForEachPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_DOCUMENT: + break; + case XSLT_FUNC_WITHPARAM: { + xsltStyleItemWithParamPtr item = + (xsltStyleItemWithParamPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_PARAM: { + xsltStyleItemParamPtr item = + (xsltStyleItemParamPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_VARIABLE: { + xsltStyleItemVariablePtr item = + (xsltStyleItemVariablePtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_WHEN: { + xsltStyleItemWhenPtr item = + (xsltStyleItemWhenPtr) comp; + if (item->comp != NULL) + xmlXPathFreeCompExpr(item->comp); + } + break; + case XSLT_FUNC_OTHERWISE: + case XSLT_FUNC_FALLBACK: + case XSLT_FUNC_MESSAGE: + case XSLT_FUNC_INCLUDE: + case XSLT_FUNC_ATTRSET: + + break; + default: + /* TODO: Raise error. */ + break; + } +#else + if (comp->locale != (xsltLocale)0) + xsltFreeLocale(comp->locale); + if (comp->comp != NULL) + xmlXPathFreeCompExpr(comp->comp); + if (comp->numdata.countPat != NULL) + xsltFreeCompMatchList(comp->numdata.countPat); + if (comp->numdata.fromPat != NULL) + xsltFreeCompMatchList(comp->numdata.fromPat); + if (comp->nsList != NULL) + xmlFree(comp->nsList); +#endif + + xmlFree(comp); +} + + +/************************************************************************ + * * + * XSLT-1.1 extensions * + * * + ************************************************************************/ + +/** + * xsltDocumentComp: + * @style: the XSLT stylesheet + * @inst: the instruction in the stylesheet + * @function: unused + * + * Pre process an XSLT-1.1 document element + * + * Returns a precompiled data structure for the element + */ +xsltElemPreCompPtr +xsltDocumentComp(xsltStylesheetPtr style, xmlNodePtr inst, + xsltTransformFunction function ATTRIBUTE_UNUSED) { +#ifdef XSLT_REFACTORED + xsltStyleItemDocumentPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + const xmlChar *filename = NULL; + + /* + * As of 2006-03-30, this function is currently defined in Libxslt + * to be used for: + * (in libxslt/extra.c) + * "output" in XSLT_SAXON_NAMESPACE + * "write" XSLT_XALAN_NAMESPACE + * "document" XSLT_XT_NAMESPACE + * "document" XSLT_NAMESPACE (from the abandoned old working + * draft of XSLT 1.1) + * (in libexslt/common.c) + * "document" in EXSLT_COMMON_NAMESPACE + */ +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemDocumentPtr) + xsltNewStylePreComp(style, XSLT_FUNC_DOCUMENT); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_DOCUMENT); +#endif + + if (comp == NULL) + return (NULL); + comp->inst = inst; + comp->ver11 = 0; + + if (xmlStrEqual(inst->name, (const xmlChar *) "output")) { +#ifdef WITH_XSLT_DEBUG_EXTRA + xsltGenericDebug(xsltGenericDebugContext, + "Found saxon:output extension\n"); +#endif + /* + * The element "output" is in the namespace XSLT_SAXON_NAMESPACE + * (http://icl.com/saxon) + * The @file is in no namespace; it is an AVT. + * (http://www.computerwizards.com/saxon/doc/extensions.html#saxon:output) + * + * TODO: Do we need not to check the namespace here? + */ + filename = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"file", + NULL, &comp->has_filename); + } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) { +#ifdef WITH_XSLT_DEBUG_EXTRA + xsltGenericDebug(xsltGenericDebugContext, + "Found xalan:write extension\n"); +#endif + /* the filename need to be interpreted */ + /* + * TODO: Is "filename need to be interpreted" meant to be a todo? + * Where will be the filename of xalan:write be processed? + * + * TODO: Do we need not to check the namespace here? + * The extension ns is "http://xml.apache.org/xalan/redirect". + * See http://xml.apache.org/xalan-j/extensionslib.html. + */ + } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) { + if (inst->ns != NULL) { + if (xmlStrEqual(inst->ns->href, XSLT_NAMESPACE)) { + /* + * Mark the instruction as being of + * XSLT version 1.1 (abandoned). + */ + comp->ver11 = 1; +#ifdef WITH_XSLT_DEBUG_EXTRA + xsltGenericDebug(xsltGenericDebugContext, + "Found xslt11:document construct\n"); +#endif + } else { + if (xmlStrEqual(inst->ns->href, + (const xmlChar *)"http://exslt.org/common")) { + /* EXSLT. */ +#ifdef WITH_XSLT_DEBUG_EXTRA + xsltGenericDebug(xsltGenericDebugContext, + "Found exslt:document extension\n"); +#endif + } else if (xmlStrEqual(inst->ns->href, XSLT_XT_NAMESPACE)) { + /* James Clark's XT. */ +#ifdef WITH_XSLT_DEBUG_EXTRA + xsltGenericDebug(xsltGenericDebugContext, + "Found xt:document extension\n"); +#endif + } + } + } + /* + * The element "document" is used in conjunction with the + * following namespaces: + * + * 1) XSLT_NAMESPACE (http://www.w3.org/1999/XSL/Transform version 1.1) + * <!ELEMENT xsl:document %template;> + * <!ATTLIST xsl:document + * href %avt; #REQUIRED + * @href is an AVT + * IMPORTANT: xsl:document was in the abandoned XSLT 1.1 draft, + * it was removed and isn't available in XSLT 1.1 anymore. + * In XSLT 2.0 it was renamed to xsl:result-document. + * + * All other attributes are identical to the attributes + * on xsl:output + * + * 2) EXSLT_COMMON_NAMESPACE (http://exslt.org/common) + * <exsl:document + * href = { uri-reference } + * TODO: is @href is an AVT? + * + * 3) XSLT_XT_NAMESPACE (http://www.jclark.com/xt) + * Example: <xt:document method="xml" href="myFile.xml"> + * TODO: is @href is an AVT? + * + * In all cases @href is in no namespace. + */ + filename = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"href", NULL, &comp->has_filename); + } + if (!comp->has_filename) { + goto error; + } + comp->filename = filename; + +error: + return ((xsltElemPreCompPtr) comp); +} + +/************************************************************************ + * * + * Most of the XSLT-1.0 transformations * + * * + ************************************************************************/ + +/** + * xsltSortComp: + * @style: the XSLT stylesheet + * @inst: the xslt sort node + * + * Process the xslt sort node on the source node + */ +static void +xsltSortComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemSortPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemSortPtr) xsltNewStylePreComp(style, XSLT_FUNC_SORT); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_SORT); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + comp->stype = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"data-type", + NULL, &comp->has_stype); + if (comp->stype != NULL) { + if (xmlStrEqual(comp->stype, (const xmlChar *) "text")) + comp->number = 0; + else if (xmlStrEqual(comp->stype, (const xmlChar *) "number")) + comp->number = 1; + else { + xsltTransformError(NULL, style, inst, + "xsltSortComp: no support for data-type = %s\n", comp->stype); + comp->number = 0; /* use default */ + if (style != NULL) style->warnings++; + } + } + comp->order = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"order", + NULL, &comp->has_order); + if (comp->order != NULL) { + if (xmlStrEqual(comp->order, (const xmlChar *) "ascending")) + comp->descending = 0; + else if (xmlStrEqual(comp->order, (const xmlChar *) "descending")) + comp->descending = 1; + else { + xsltTransformError(NULL, style, inst, + "xsltSortComp: invalid value %s for order\n", comp->order); + comp->descending = 0; /* use default */ + if (style != NULL) style->warnings++; + } + } + comp->case_order = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"case-order", + NULL, &comp->has_use); + if (comp->case_order != NULL) { + if (xmlStrEqual(comp->case_order, (const xmlChar *) "upper-first")) + comp->lower_first = 0; + else if (xmlStrEqual(comp->case_order, (const xmlChar *) "lower-first")) + comp->lower_first = 1; + else { + xsltTransformError(NULL, style, inst, + "xsltSortComp: invalid value %s for order\n", comp->order); + comp->lower_first = 0; /* use default */ + if (style != NULL) style->warnings++; + } + } + + comp->lang = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"lang", + NULL, &comp->has_lang); + if (comp->lang != NULL) { + comp->locale = xsltNewLocale(comp->lang); + } + else { + comp->locale = (xsltLocale)0; + } + + comp->select = xsltGetCNsProp(style, inst,(const xmlChar *)"select", XSLT_NAMESPACE); + if (comp->select == NULL) { + /* + * The default value of the select attribute is ., which will + * cause the string-value of the current node to be used as + * the sort key. + */ + comp->select = xmlDictLookup(style->dict, BAD_CAST ".", 1); + } + comp->comp = xsltXPathCompile(style, comp->select); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsltSortComp: could not compile select expression '%s'\n", + comp->select); + if (style != NULL) style->errors++; + } + if (inst->children != NULL) { + xsltTransformError(NULL, style, inst, + "xsl:sort : is not empty\n"); + if (style != NULL) style->errors++; + } +} + +/** + * xsltCopyComp: + * @style: the XSLT stylesheet + * @inst: the xslt copy node + * + * Process the xslt copy node on the source node + */ +static void +xsltCopyComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemCopyPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemCopyPtr) xsltNewStylePreComp(style, XSLT_FUNC_COPY); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_COPY); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + + comp->use = xsltGetCNsProp(style, inst, (const xmlChar *)"use-attribute-sets", + XSLT_NAMESPACE); + if (comp->use == NULL) + comp->has_use = 0; + else + comp->has_use = 1; +} + +#ifdef XSLT_REFACTORED + /* Enable if ever needed for xsl:text. */ +#else +/** + * xsltTextComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt text node + * + * TODO: This function is obsolete, since xsl:text won't + * be compiled, but removed from the tree. + * + * Process the xslt text node on the source node + */ +static void +xsltTextComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemTextPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + const xmlChar *prop; + + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemTextPtr) xsltNewStylePreComp(style, XSLT_FUNC_TEXT); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_TEXT); +#endif + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + comp->noescape = 0; + + prop = xsltGetCNsProp(style, inst, + (const xmlChar *)"disable-output-escaping", + XSLT_NAMESPACE); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *)"yes")) { + comp->noescape = 1; + } else if (!xmlStrEqual(prop, + (const xmlChar *)"no")){ + xsltTransformError(NULL, style, inst, + "xsl:text: disable-output-escaping allows only yes or no\n"); + if (style != NULL) style->warnings++; + } + } +} +#endif /* else of XSLT_REFACTORED */ + +/** + * xsltElementComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt element node + * + * Process the xslt element node on the source node + */ +static void +xsltElementComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemElementPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + /* + * <xsl:element + * name = { qname } + * namespace = { uri-reference } + * use-attribute-sets = qnames> + * <!-- Content: template --> + * </xsl:element> + */ + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemElementPtr) xsltNewStylePreComp(style, XSLT_FUNC_ELEMENT); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_ELEMENT); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + /* + * Attribute "name". + */ + /* + * TODO: Precompile the AVT. See bug #344894. + */ + comp->name = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"name", NULL, &comp->has_name); + if (! comp->has_name) { + xsltTransformError(NULL, style, inst, + "xsl:element: The attribute 'name' is missing.\n"); + style->errors++; + goto error; + } + /* + * Attribute "namespace". + */ + /* + * TODO: Precompile the AVT. See bug #344894. + */ + comp->ns = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"namespace", NULL, &comp->has_ns); + + if (comp->name != NULL) { + if (xmlValidateQName(comp->name, 0)) { + xsltTransformError(NULL, style, inst, + "xsl:element: The value '%s' of the attribute 'name' is " + "not a valid QName.\n", comp->name); + style->errors++; + } else { + const xmlChar *prefix = NULL, *name; + + name = xsltSplitQName(style->dict, comp->name, &prefix); + if (comp->has_ns == 0) { + xmlNsPtr ns; + + /* + * SPEC XSLT 1.0: + * "If the namespace attribute is not present, then the QName is + * expanded into an expanded-name using the namespace declarations + * in effect for the xsl:element element, including any default + * namespace declaration. + */ + ns = xmlSearchNs(inst->doc, inst, prefix); + if (ns != NULL) { + comp->ns = xmlDictLookup(style->dict, ns->href, -1); + comp->has_ns = 1; +#ifdef XSLT_REFACTORED + comp->nsPrefix = prefix; + comp->name = name; +#else + (void)name; /* Suppress unused variable warning. */ +#endif + } else if (prefix != NULL) { + xsltTransformError(NULL, style, inst, + "xsl:element: The prefixed QName '%s' " + "has no namespace binding in scope in the " + "stylesheet; this is an error, since the namespace was " + "not specified by the instruction itself.\n", comp->name); + style->errors++; + } + } + if ((prefix != NULL) && + (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3))) + { + /* + * Mark is to be skipped. + */ + comp->has_name = 0; + } + } + } + /* + * Attribute "use-attribute-sets", + */ + comp->use = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"use-attribute-sets", + NULL, &comp->has_use); + +error: + return; +} + +/** + * xsltAttributeComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt attribute node + * + * Process the xslt attribute node on the source node + */ +static void +xsltAttributeComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemAttributePtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + /* + * <xsl:attribute + * name = { qname } + * namespace = { uri-reference }> + * <!-- Content: template --> + * </xsl:attribute> + */ + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemAttributePtr) xsltNewStylePreComp(style, + XSLT_FUNC_ATTRIBUTE); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_ATTRIBUTE); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + /* + * Attribute "name". + */ + /* + * TODO: Precompile the AVT. See bug #344894. + */ + comp->name = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"name", + NULL, &comp->has_name); + if (! comp->has_name) { + xsltTransformError(NULL, style, inst, + "XSLT-attribute: The attribute 'name' is missing.\n"); + style->errors++; + return; + } + /* + * Attribute "namespace". + */ + /* + * TODO: Precompile the AVT. See bug #344894. + */ + comp->ns = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"namespace", + NULL, &comp->has_ns); + + if (comp->name != NULL) { + if (xmlValidateQName(comp->name, 0)) { + xsltTransformError(NULL, style, inst, + "xsl:attribute: The value '%s' of the attribute 'name' is " + "not a valid QName.\n", comp->name); + style->errors++; + } else if (xmlStrEqual(comp->name, BAD_CAST "xmlns")) { + xsltTransformError(NULL, style, inst, + "xsl:attribute: The attribute name 'xmlns' is not allowed.\n"); + style->errors++; + } else { + const xmlChar *prefix = NULL, *name; + + name = xsltSplitQName(style->dict, comp->name, &prefix); + if (prefix != NULL) { + if (comp->has_ns == 0) { + xmlNsPtr ns; + + /* + * SPEC XSLT 1.0: + * "If the namespace attribute is not present, then the + * QName is expanded into an expanded-name using the + * namespace declarations in effect for the xsl:element + * element, including any default namespace declaration. + */ + ns = xmlSearchNs(inst->doc, inst, prefix); + if (ns != NULL) { + comp->ns = xmlDictLookup(style->dict, ns->href, -1); + comp->has_ns = 1; +#ifdef XSLT_REFACTORED + comp->nsPrefix = prefix; + comp->name = name; +#else + (void)name; /* Suppress unused variable warning. */ +#endif + } else { + xsltTransformError(NULL, style, inst, + "xsl:attribute: The prefixed QName '%s' " + "has no namespace binding in scope in the " + "stylesheet; this is an error, since the " + "namespace was not specified by the instruction " + "itself.\n", comp->name); + style->errors++; + } + } + } + } + } +} + +/** + * xsltCommentComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt comment node + * + * Process the xslt comment node on the source node + */ +static void +xsltCommentComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemCommentPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemCommentPtr) xsltNewStylePreComp(style, XSLT_FUNC_COMMENT); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_COMMENT); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; +} + +/** + * xsltProcessingInstructionComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt processing-instruction node + * + * Process the xslt processing-instruction node on the source node + */ +static void +xsltProcessingInstructionComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemPIPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemPIPtr) xsltNewStylePreComp(style, XSLT_FUNC_PI); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_PI); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + comp->name = xsltEvalStaticAttrValueTemplate(style, inst, + (const xmlChar *)"name", + XSLT_NAMESPACE, &comp->has_name); +} + +/** + * xsltCopyOfComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt copy-of node + * + * Process the xslt copy-of node on the source node + */ +static void +xsltCopyOfComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemCopyOfPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemCopyOfPtr) xsltNewStylePreComp(style, XSLT_FUNC_COPYOF); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_COPYOF); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", + XSLT_NAMESPACE); + if (comp->select == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:copy-of : select is missing\n"); + if (style != NULL) style->errors++; + return; + } + comp->comp = xsltXPathCompile(style, comp->select); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:copy-of : could not compile select expression '%s'\n", + comp->select); + if (style != NULL) style->errors++; + } +} + +/** + * xsltValueOfComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt value-of node + * + * Process the xslt value-of node on the source node + */ +static void +xsltValueOfComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemValueOfPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + const xmlChar *prop; + + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemValueOfPtr) xsltNewStylePreComp(style, XSLT_FUNC_VALUEOF); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_VALUEOF); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + prop = xsltGetCNsProp(style, inst, + (const xmlChar *)"disable-output-escaping", + XSLT_NAMESPACE); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *)"yes")) { + comp->noescape = 1; + } else if (!xmlStrEqual(prop, + (const xmlChar *)"no")){ + xsltTransformError(NULL, style, inst, +"xsl:value-of : disable-output-escaping allows only yes or no\n"); + if (style != NULL) style->warnings++; + } + } + comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", + XSLT_NAMESPACE); + if (comp->select == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:value-of : select is missing\n"); + if (style != NULL) style->errors++; + return; + } + comp->comp = xsltXPathCompile(style, comp->select); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:value-of : could not compile select expression '%s'\n", + comp->select); + if (style != NULL) style->errors++; + } +} + +static void +xsltGetQNameProperty(xsltStylesheetPtr style, xmlNodePtr inst, + const xmlChar *propName, + int mandatory, + int *hasProp, const xmlChar **nsName, + const xmlChar** localName) +{ + const xmlChar *prop; + + if (nsName) + *nsName = NULL; + if (localName) + *localName = NULL; + if (hasProp) + *hasProp = 0; + + prop = xsltGetCNsProp(style, inst, propName, XSLT_NAMESPACE); + if (prop == NULL) { + if (mandatory) { + xsltTransformError(NULL, style, inst, + "The attribute '%s' is missing.\n", propName); + style->errors++; + return; + } + } else { + const xmlChar *URI; + + if (xmlValidateQName(prop, 0)) { + xsltTransformError(NULL, style, inst, + "The value '%s' of the attribute " + "'%s' is not a valid QName.\n", prop, propName); + style->errors++; + return; + } else { + /* + * @prop will be in the string dict afterwards, @URI not. + */ + URI = xsltGetQNameURI2(style, inst, &prop); + if (prop == NULL) { + style->errors++; + } else { + if (localName) + *localName = prop; + if (hasProp) + *hasProp = 1; + if (URI != NULL) { + /* + * Fixes bug #308441: Put the ns-name in the dict + * in order to pointer compare names during XPath's + * variable lookup. + */ + if (nsName) + *nsName = xmlDictLookup(style->dict, URI, -1); + /* comp->has_ns = 1; */ + } + } + } + } + return; +} + +/** + * xsltWithParamComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt with-param node + * + * Process the xslt with-param node on the source node + * Allowed parents: xsl:call-template, xsl:apply-templates. + * <xsl:with-param + * name = qname + * select = expression> + * <!-- Content: template --> + * </xsl:with-param> + */ +static void +xsltWithParamComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemWithParamPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemWithParamPtr) xsltNewStylePreComp(style, XSLT_FUNC_WITHPARAM); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_WITHPARAM); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + /* + * Attribute "name". + */ + xsltGetQNameProperty(style, inst, BAD_CAST "name", + 1, &(comp->has_name), &(comp->ns), &(comp->name)); + if (comp->ns) + comp->has_ns = 1; + /* + * Attribute "select". + */ + comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", + XSLT_NAMESPACE); + if (comp->select != NULL) { + comp->comp = xsltXPathCompile(style, comp->select); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "XSLT-with-param: Failed to compile select " + "expression '%s'\n", comp->select); + style->errors++; + } + if (inst->children != NULL) { + xsltTransformError(NULL, style, inst, + "XSLT-with-param: The content should be empty since " + "the attribute select is present.\n"); + style->warnings++; + } + } +} + +/** + * xsltNumberComp: + * @style: an XSLT compiled stylesheet + * @cur: the xslt number node + * + * Process the xslt number node on the source node + */ +static void +xsltNumberComp(xsltStylesheetPtr style, xmlNodePtr cur) { +#ifdef XSLT_REFACTORED + xsltStyleItemNumberPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + const xmlChar *prop; + + if ((style == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemNumberPtr) xsltNewStylePreComp(style, XSLT_FUNC_NUMBER); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_NUMBER); +#endif + + if (comp == NULL) + return; + cur->psvi = comp; + + comp->numdata.doc = cur->doc; + comp->numdata.node = cur; + comp->numdata.value = xsltGetCNsProp(style, cur, (const xmlChar *)"value", + XSLT_NAMESPACE); + + prop = xsltEvalStaticAttrValueTemplate(style, cur, + (const xmlChar *)"format", + XSLT_NAMESPACE, &comp->numdata.has_format); + if (comp->numdata.has_format == 0) { + comp->numdata.format = xmlDictLookup(style->dict, BAD_CAST "" , 0); + } else { + comp->numdata.format = prop; + } + + comp->numdata.count = xsltGetCNsProp(style, cur, (const xmlChar *)"count", + XSLT_NAMESPACE); + comp->numdata.from = xsltGetCNsProp(style, cur, (const xmlChar *)"from", + XSLT_NAMESPACE); + + prop = xsltGetCNsProp(style, cur, (const xmlChar *)"count", XSLT_NAMESPACE); + if (prop != NULL) { + comp->numdata.countPat = xsltCompilePattern(prop, cur->doc, cur, style, + NULL); + } + + prop = xsltGetCNsProp(style, cur, (const xmlChar *)"from", XSLT_NAMESPACE); + if (prop != NULL) { + comp->numdata.fromPat = xsltCompilePattern(prop, cur->doc, cur, style, + NULL); + } + + prop = xsltGetCNsProp(style, cur, (const xmlChar *)"level", XSLT_NAMESPACE); + if (prop != NULL) { + if (xmlStrEqual(prop, BAD_CAST("single")) || + xmlStrEqual(prop, BAD_CAST("multiple")) || + xmlStrEqual(prop, BAD_CAST("any"))) { + comp->numdata.level = prop; + } else { + xsltTransformError(NULL, style, cur, + "xsl:number : invalid value %s for level\n", prop); + if (style != NULL) style->warnings++; + } + } + + prop = xsltGetCNsProp(style, cur, (const xmlChar *)"lang", XSLT_NAMESPACE); + if (prop != NULL) { + xsltTransformError(NULL, style, cur, + "xsl:number : lang attribute not implemented\n"); + XSLT_TODO; /* xsl:number lang attribute */ + } + + prop = xsltGetCNsProp(style, cur, (const xmlChar *)"letter-value", XSLT_NAMESPACE); + if (prop != NULL) { + if (xmlStrEqual(prop, BAD_CAST("alphabetic"))) { + xsltTransformError(NULL, style, cur, + "xsl:number : letter-value 'alphabetic' not implemented\n"); + if (style != NULL) style->warnings++; + XSLT_TODO; /* xsl:number letter-value attribute alphabetic */ + } else if (xmlStrEqual(prop, BAD_CAST("traditional"))) { + xsltTransformError(NULL, style, cur, + "xsl:number : letter-value 'traditional' not implemented\n"); + if (style != NULL) style->warnings++; + XSLT_TODO; /* xsl:number letter-value attribute traditional */ + } else { + xsltTransformError(NULL, style, cur, + "xsl:number : invalid value %s for letter-value\n", prop); + if (style != NULL) style->warnings++; + } + } + + prop = xsltGetCNsProp(style, cur, (const xmlChar *)"grouping-separator", + XSLT_NAMESPACE); + if (prop != NULL) { + comp->numdata.groupingCharacterLen = xmlStrlen(prop); + comp->numdata.groupingCharacter = + xsltGetUTF8Char(prop, &(comp->numdata.groupingCharacterLen)); + } + + prop = xsltGetCNsProp(style, cur, (const xmlChar *)"grouping-size", XSLT_NAMESPACE); + if (prop != NULL) { + sscanf((char *)prop, "%d", &comp->numdata.digitsPerGroup); + } else { + comp->numdata.groupingCharacter = 0; + } + + /* Set default values */ + if (comp->numdata.value == NULL) { + if (comp->numdata.level == NULL) { + comp->numdata.level = xmlDictLookup(style->dict, + BAD_CAST"single", 6); + } + } + +} + +/** + * xsltApplyImportsComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt apply-imports node + * + * Process the xslt apply-imports node on the source node + */ +static void +xsltApplyImportsComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemApplyImportsPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemApplyImportsPtr) xsltNewStylePreComp(style, XSLT_FUNC_APPLYIMPORTS); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_APPLYIMPORTS); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; +} + +/** + * xsltCallTemplateComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt call-template node + * + * Process the xslt call-template node on the source node + */ +static void +xsltCallTemplateComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemCallTemplatePtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemCallTemplatePtr) + xsltNewStylePreComp(style, XSLT_FUNC_CALLTEMPLATE); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_CALLTEMPLATE); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + /* + * Attribute "name". + */ + xsltGetQNameProperty(style, inst, BAD_CAST "name", + 1, &(comp->has_name), &(comp->ns), &(comp->name)); + if (comp->ns) + comp->has_ns = 1; +} + +/** + * xsltApplyTemplatesComp: + * @style: an XSLT compiled stylesheet + * @inst: the apply-templates node + * + * Process the apply-templates node on the source node + */ +static void +xsltApplyTemplatesComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemApplyTemplatesPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemApplyTemplatesPtr) + xsltNewStylePreComp(style, XSLT_FUNC_APPLYTEMPLATES); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_APPLYTEMPLATES); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + /* + * Attribute "mode". + */ + xsltGetQNameProperty(style, inst, BAD_CAST "mode", + 0, NULL, &(comp->modeURI), &(comp->mode)); + /* + * Attribute "select". + */ + comp->select = xsltGetCNsProp(style, inst, BAD_CAST "select", + XSLT_NAMESPACE); + if (comp->select != NULL) { + comp->comp = xsltXPathCompile(style, comp->select); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "XSLT-apply-templates: could not compile select " + "expression '%s'\n", comp->select); + style->errors++; + } + } + /* TODO: handle (or skip) the xsl:sort and xsl:with-param */ +} + +/** + * xsltChooseComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt choose node + * + * Process the xslt choose node on the source node + */ +static void +xsltChooseComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemChoosePtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemChoosePtr) + xsltNewStylePreComp(style, XSLT_FUNC_CHOOSE); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_CHOOSE); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; +} + +/** + * xsltIfComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt if node + * + * Process the xslt if node on the source node + */ +static void +xsltIfComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemIfPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemIfPtr) + xsltNewStylePreComp(style, XSLT_FUNC_IF); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_IF); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + comp->test = xsltGetCNsProp(style, inst, (const xmlChar *)"test", XSLT_NAMESPACE); + if (comp->test == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:if : test is not defined\n"); + if (style != NULL) style->errors++; + return; + } + comp->comp = xsltXPathCompile(style, comp->test); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:if : could not compile test expression '%s'\n", + comp->test); + if (style != NULL) style->errors++; + } +} + +/** + * xsltWhenComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt if node + * + * Process the xslt if node on the source node + */ +static void +xsltWhenComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemWhenPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemWhenPtr) + xsltNewStylePreComp(style, XSLT_FUNC_WHEN); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_WHEN); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + comp->test = xsltGetCNsProp(style, inst, (const xmlChar *)"test", XSLT_NAMESPACE); + if (comp->test == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:when : test is not defined\n"); + if (style != NULL) style->errors++; + return; + } + comp->comp = xsltXPathCompile(style, comp->test); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:when : could not compile test expression '%s'\n", + comp->test); + if (style != NULL) style->errors++; + } +} + +/** + * xsltForEachComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt for-each node + * + * Process the xslt for-each node on the source node + */ +static void +xsltForEachComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemForEachPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemForEachPtr) + xsltNewStylePreComp(style, XSLT_FUNC_FOREACH); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_FOREACH); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", + XSLT_NAMESPACE); + if (comp->select == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:for-each : select is missing\n"); + if (style != NULL) style->errors++; + } else { + comp->comp = xsltXPathCompile(style, comp->select); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "xsl:for-each : could not compile select expression '%s'\n", + comp->select); + if (style != NULL) style->errors++; + } + } + /* TODO: handle and skip the xsl:sort */ +} + +/** + * xsltVariableComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt variable node + * + * Process the xslt variable node on the source node + */ +static void +xsltVariableComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemVariablePtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemVariablePtr) + xsltNewStylePreComp(style, XSLT_FUNC_VARIABLE); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_VARIABLE); +#endif + + if (comp == NULL) + return; + + inst->psvi = comp; + comp->inst = inst; + /* + * The full template resolution can be done statically + */ + + /* + * Attribute "name". + */ + xsltGetQNameProperty(style, inst, BAD_CAST "name", + 1, &(comp->has_name), &(comp->ns), &(comp->name)); + if (comp->ns) + comp->has_ns = 1; + /* + * Attribute "select". + */ + comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", + XSLT_NAMESPACE); + if (comp->select != NULL) { +#ifndef XSLT_REFACTORED + xmlNodePtr cur; +#endif + comp->comp = xsltXPathCompile(style, comp->select); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "XSLT-variable: Failed to compile the XPath expression '%s'.\n", + comp->select); + style->errors++; + } +#ifdef XSLT_REFACTORED + if (inst->children != NULL) { + xsltTransformError(NULL, style, inst, + "XSLT-variable: There must be no child nodes, since the " + "attribute 'select' was specified.\n"); + style->errors++; + } +#else + for (cur = inst->children; cur != NULL; cur = cur->next) { + if (cur->type != XML_COMMENT_NODE && + (cur->type != XML_TEXT_NODE || !xsltIsBlank(cur->content))) + { + xsltTransformError(NULL, style, inst, + "XSLT-variable: There must be no child nodes, since the " + "attribute 'select' was specified.\n"); + style->errors++; + } + } +#endif + } +} + +/** + * xsltParamComp: + * @style: an XSLT compiled stylesheet + * @inst: the xslt param node + * + * Process the xslt param node on the source node + */ +static void +xsltParamComp(xsltStylesheetPtr style, xmlNodePtr inst) { +#ifdef XSLT_REFACTORED + xsltStyleItemParamPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((style == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + comp = (xsltStyleItemParamPtr) + xsltNewStylePreComp(style, XSLT_FUNC_PARAM); +#else + comp = xsltNewStylePreComp(style, XSLT_FUNC_PARAM); +#endif + + if (comp == NULL) + return; + inst->psvi = comp; + comp->inst = inst; + + /* + * Attribute "name". + */ + xsltGetQNameProperty(style, inst, BAD_CAST "name", + 1, &(comp->has_name), &(comp->ns), &(comp->name)); + if (comp->ns) + comp->has_ns = 1; + /* + * Attribute "select". + */ + comp->select = xsltGetCNsProp(style, inst, (const xmlChar *)"select", + XSLT_NAMESPACE); + if (comp->select != NULL) { + comp->comp = xsltXPathCompile(style, comp->select); + if (comp->comp == NULL) { + xsltTransformError(NULL, style, inst, + "XSLT-param: could not compile select expression '%s'.\n", + comp->select); + style->errors++; + } + if (inst->children != NULL) { + xsltTransformError(NULL, style, inst, + "XSLT-param: The content should be empty since the " + "attribute 'select' is present.\n"); + style->warnings++; + } + } +} + +/************************************************************************ + * * + * Generic interface * + * * + ************************************************************************/ + +/** + * xsltFreeStylePreComps: + * @style: an XSLT transformation context + * + * Free up the memory allocated by all precomputed blocks + */ +void +xsltFreeStylePreComps(xsltStylesheetPtr style) { + xsltElemPreCompPtr cur, next; + + if (style == NULL) + return; + + cur = style->preComps; + while (cur != NULL) { + next = cur->next; + if (cur->type == XSLT_FUNC_EXTENSION) + cur->free(cur); + else + xsltFreeStylePreComp((xsltStylePreCompPtr) cur); + cur = next; + } +} + +#ifdef XSLT_REFACTORED + +/** + * xsltStylePreCompute: + * @style: the XSLT stylesheet + * @node: the element in the XSLT namespace + * + * Precompute an XSLT element. + * This expects the type of the element to be already + * set in style->compCtxt->inode->type; + */ +void +xsltStylePreCompute(xsltStylesheetPtr style, xmlNodePtr node) { + /* + * The xsltXSLTElemMarker marker was set beforehand by + * the parsing mechanism for all elements in the XSLT namespace. + */ + if (style == NULL) { + if ((node != NULL) && (node->type == XML_ELEMENT_NODE)) + node->psvi = NULL; + return; + } + if (node == NULL) + return; + if (! IS_XSLT_ELEM_FAST(node)) + return; + + node->psvi = NULL; + if (XSLT_CCTXT(style)->inode->type != 0) { + switch (XSLT_CCTXT(style)->inode->type) { + case XSLT_FUNC_APPLYTEMPLATES: + xsltApplyTemplatesComp(style, node); + break; + case XSLT_FUNC_WITHPARAM: + xsltWithParamComp(style, node); + break; + case XSLT_FUNC_VALUEOF: + xsltValueOfComp(style, node); + break; + case XSLT_FUNC_COPY: + xsltCopyComp(style, node); + break; + case XSLT_FUNC_COPYOF: + xsltCopyOfComp(style, node); + break; + case XSLT_FUNC_IF: + xsltIfComp(style, node); + break; + case XSLT_FUNC_CHOOSE: + xsltChooseComp(style, node); + break; + case XSLT_FUNC_WHEN: + xsltWhenComp(style, node); + break; + case XSLT_FUNC_OTHERWISE: + /* NOP yet */ + return; + case XSLT_FUNC_FOREACH: + xsltForEachComp(style, node); + break; + case XSLT_FUNC_APPLYIMPORTS: + xsltApplyImportsComp(style, node); + break; + case XSLT_FUNC_ATTRIBUTE: + xsltAttributeComp(style, node); + break; + case XSLT_FUNC_ELEMENT: + xsltElementComp(style, node); + break; + case XSLT_FUNC_SORT: + xsltSortComp(style, node); + break; + case XSLT_FUNC_COMMENT: + xsltCommentComp(style, node); + break; + case XSLT_FUNC_NUMBER: + xsltNumberComp(style, node); + break; + case XSLT_FUNC_PI: + xsltProcessingInstructionComp(style, node); + break; + case XSLT_FUNC_CALLTEMPLATE: + xsltCallTemplateComp(style, node); + break; + case XSLT_FUNC_PARAM: + xsltParamComp(style, node); + break; + case XSLT_FUNC_VARIABLE: + xsltVariableComp(style, node); + break; + case XSLT_FUNC_FALLBACK: + /* NOP yet */ + return; + case XSLT_FUNC_DOCUMENT: + /* The extra one */ + node->psvi = (void *) xsltDocumentComp(style, node, + (xsltTransformFunction) xsltDocumentElem); + break; + case XSLT_FUNC_MESSAGE: + /* NOP yet */ + return; + default: + /* + * NOTE that xsl:text, xsl:template, xsl:stylesheet, + * xsl:transform, xsl:import, xsl:include are not expected + * to be handed over to this function. + */ + xsltTransformError(NULL, style, node, + "Internal error: (xsltStylePreCompute) cannot handle " + "the XSLT element '%s'.\n", node->name); + style->errors++; + return; + } + } else { + /* + * Fallback to string comparison. + */ + if (IS_XSLT_NAME(node, "apply-templates")) { + xsltApplyTemplatesComp(style, node); + } else if (IS_XSLT_NAME(node, "with-param")) { + xsltWithParamComp(style, node); + } else if (IS_XSLT_NAME(node, "value-of")) { + xsltValueOfComp(style, node); + } else if (IS_XSLT_NAME(node, "copy")) { + xsltCopyComp(style, node); + } else if (IS_XSLT_NAME(node, "copy-of")) { + xsltCopyOfComp(style, node); + } else if (IS_XSLT_NAME(node, "if")) { + xsltIfComp(style, node); + } else if (IS_XSLT_NAME(node, "choose")) { + xsltChooseComp(style, node); + } else if (IS_XSLT_NAME(node, "when")) { + xsltWhenComp(style, node); + } else if (IS_XSLT_NAME(node, "otherwise")) { + /* NOP yet */ + return; + } else if (IS_XSLT_NAME(node, "for-each")) { + xsltForEachComp(style, node); + } else if (IS_XSLT_NAME(node, "apply-imports")) { + xsltApplyImportsComp(style, node); + } else if (IS_XSLT_NAME(node, "attribute")) { + xsltAttributeComp(style, node); + } else if (IS_XSLT_NAME(node, "element")) { + xsltElementComp(style, node); + } else if (IS_XSLT_NAME(node, "sort")) { + xsltSortComp(style, node); + } else if (IS_XSLT_NAME(node, "comment")) { + xsltCommentComp(style, node); + } else if (IS_XSLT_NAME(node, "number")) { + xsltNumberComp(style, node); + } else if (IS_XSLT_NAME(node, "processing-instruction")) { + xsltProcessingInstructionComp(style, node); + } else if (IS_XSLT_NAME(node, "call-template")) { + xsltCallTemplateComp(style, node); + } else if (IS_XSLT_NAME(node, "param")) { + xsltParamComp(style, node); + } else if (IS_XSLT_NAME(node, "variable")) { + xsltVariableComp(style, node); + } else if (IS_XSLT_NAME(node, "fallback")) { + /* NOP yet */ + return; + } else if (IS_XSLT_NAME(node, "document")) { + /* The extra one */ + node->psvi = (void *) xsltDocumentComp(style, node, + (xsltTransformFunction) xsltDocumentElem); + } else if (IS_XSLT_NAME(node, "output")) { + /* Top-level */ + return; + } else if (IS_XSLT_NAME(node, "preserve-space")) { + /* Top-level */ + return; + } else if (IS_XSLT_NAME(node, "strip-space")) { + /* Top-level */ + return; + } else if (IS_XSLT_NAME(node, "key")) { + /* Top-level */ + return; + } else if (IS_XSLT_NAME(node, "message")) { + return; + } else if (IS_XSLT_NAME(node, "attribute-set")) { + /* Top-level */ + return; + } else if (IS_XSLT_NAME(node, "namespace-alias")) { + /* Top-level */ + return; + } else if (IS_XSLT_NAME(node, "decimal-format")) { + /* Top-level */ + return; + } else if (IS_XSLT_NAME(node, "include")) { + /* Top-level */ + } else { + /* + * NOTE that xsl:text, xsl:template, xsl:stylesheet, + * xsl:transform, xsl:import, xsl:include are not expected + * to be handed over to this function. + */ + xsltTransformError(NULL, style, node, + "Internal error: (xsltStylePreCompute) cannot handle " + "the XSLT element '%s'.\n", node->name); + style->errors++; + return; + } + } + /* + * Assign the current list of in-scope namespaces to the + * item. This is needed for XPath expressions. + */ + if (node->psvi != NULL) { + ((xsltStylePreCompPtr) node->psvi)->inScopeNs = + XSLT_CCTXT(style)->inode->inScopeNs; + } +} + +#else + +/** + * xsltStylePreCompute: + * @style: the XSLT stylesheet + * @inst: the instruction in the stylesheet + * + * Precompute an XSLT stylesheet element + */ +void +xsltStylePreCompute(xsltStylesheetPtr style, xmlNodePtr inst) { + /* + * URGENT TODO: Normally inst->psvi Should never be reserved here, + * BUT: since if we include the same stylesheet from + * multiple imports, then the stylesheet will be parsed + * again. We simply must not try to compute the stylesheet again. + * TODO: Get to the point where we don't need to query the + * namespace- and local-name of the node, but can evaluate this + * using cctxt->style->inode->category; + */ + if ((inst == NULL) || (inst->type != XML_ELEMENT_NODE) || + (inst->psvi != NULL)) + return; + + if (IS_XSLT_ELEM(inst)) { + xsltStylePreCompPtr cur; + + if (IS_XSLT_NAME(inst, "apply-templates")) { + xsltCheckInstructionElement(style, inst); + xsltApplyTemplatesComp(style, inst); + } else if (IS_XSLT_NAME(inst, "with-param")) { + xsltCheckParentElement(style, inst, BAD_CAST "apply-templates", + BAD_CAST "call-template"); + xsltWithParamComp(style, inst); + } else if (IS_XSLT_NAME(inst, "value-of")) { + xsltCheckInstructionElement(style, inst); + xsltValueOfComp(style, inst); + } else if (IS_XSLT_NAME(inst, "copy")) { + xsltCheckInstructionElement(style, inst); + xsltCopyComp(style, inst); + } else if (IS_XSLT_NAME(inst, "copy-of")) { + xsltCheckInstructionElement(style, inst); + xsltCopyOfComp(style, inst); + } else if (IS_XSLT_NAME(inst, "if")) { + xsltCheckInstructionElement(style, inst); + xsltIfComp(style, inst); + } else if (IS_XSLT_NAME(inst, "when")) { + xsltCheckParentElement(style, inst, BAD_CAST "choose", NULL); + xsltWhenComp(style, inst); + } else if (IS_XSLT_NAME(inst, "choose")) { + xsltCheckInstructionElement(style, inst); + xsltChooseComp(style, inst); + } else if (IS_XSLT_NAME(inst, "for-each")) { + xsltCheckInstructionElement(style, inst); + xsltForEachComp(style, inst); + } else if (IS_XSLT_NAME(inst, "apply-imports")) { + xsltCheckInstructionElement(style, inst); + xsltApplyImportsComp(style, inst); + } else if (IS_XSLT_NAME(inst, "attribute")) { + xmlNodePtr parent = inst->parent; + + if ((parent == NULL) || + (parent->type != XML_ELEMENT_NODE) || (parent->ns == NULL) || + ((parent->ns != inst->ns) && + (!xmlStrEqual(parent->ns->href, inst->ns->href))) || + (!xmlStrEqual(parent->name, BAD_CAST "attribute-set"))) { + xsltCheckInstructionElement(style, inst); + } + xsltAttributeComp(style, inst); + } else if (IS_XSLT_NAME(inst, "element")) { + xsltCheckInstructionElement(style, inst); + xsltElementComp(style, inst); + } else if (IS_XSLT_NAME(inst, "text")) { + xsltCheckInstructionElement(style, inst); + xsltTextComp(style, inst); + } else if (IS_XSLT_NAME(inst, "sort")) { + xsltCheckParentElement(style, inst, BAD_CAST "apply-templates", + BAD_CAST "for-each"); + xsltSortComp(style, inst); + } else if (IS_XSLT_NAME(inst, "comment")) { + xsltCheckInstructionElement(style, inst); + xsltCommentComp(style, inst); + } else if (IS_XSLT_NAME(inst, "number")) { + xsltCheckInstructionElement(style, inst); + xsltNumberComp(style, inst); + } else if (IS_XSLT_NAME(inst, "processing-instruction")) { + xsltCheckInstructionElement(style, inst); + xsltProcessingInstructionComp(style, inst); + } else if (IS_XSLT_NAME(inst, "call-template")) { + xsltCheckInstructionElement(style, inst); + xsltCallTemplateComp(style, inst); + } else if (IS_XSLT_NAME(inst, "param")) { + if (xsltCheckTopLevelElement(style, inst, 0) == 0) + xsltCheckInstructionElement(style, inst); + xsltParamComp(style, inst); + } else if (IS_XSLT_NAME(inst, "variable")) { + if (xsltCheckTopLevelElement(style, inst, 0) == 0) + xsltCheckInstructionElement(style, inst); + xsltVariableComp(style, inst); + } else if (IS_XSLT_NAME(inst, "otherwise")) { + xsltCheckParentElement(style, inst, BAD_CAST "choose", NULL); + xsltCheckInstructionElement(style, inst); + return; + } else if (IS_XSLT_NAME(inst, "template")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "output")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "preserve-space")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "strip-space")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if ((IS_XSLT_NAME(inst, "stylesheet")) || + (IS_XSLT_NAME(inst, "transform"))) { + xmlNodePtr parent = inst->parent; + + if ((parent == NULL) || (parent->type != XML_DOCUMENT_NODE)) { + xsltTransformError(NULL, style, inst, + "element %s only allowed only as root element\n", + inst->name); + style->errors++; + } + return; + } else if (IS_XSLT_NAME(inst, "key")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "message")) { + xsltCheckInstructionElement(style, inst); + return; + } else if (IS_XSLT_NAME(inst, "attribute-set")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "namespace-alias")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "include")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "import")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "decimal-format")) { + xsltCheckTopLevelElement(style, inst, 1); + return; + } else if (IS_XSLT_NAME(inst, "fallback")) { + xsltCheckInstructionElement(style, inst); + return; + } else if (IS_XSLT_NAME(inst, "document")) { + xsltCheckInstructionElement(style, inst); + inst->psvi = (void *) xsltDocumentComp(style, inst, + (xsltTransformFunction) xsltDocumentElem); + } else if ((style == NULL) || (style->forwards_compatible == 0)) { + xsltTransformError(NULL, style, inst, + "xsltStylePreCompute: unknown xsl:%s\n", inst->name); + if (style != NULL) style->warnings++; + } + + cur = (xsltStylePreCompPtr) inst->psvi; + /* + * A ns-list is build for every XSLT item in the + * node-tree. This is needed for XPath expressions. + */ + if (cur != NULL) { + int i = 0; + + cur->nsList = xmlGetNsList(inst->doc, inst); + if (cur->nsList != NULL) { + while (cur->nsList[i] != NULL) + i++; + } + cur->nsNr = i; + } + } else { + inst->psvi = + (void *) xsltPreComputeExtModuleElement(style, inst); + + /* + * Unknown element, maybe registered at the context + * level. Mark it for later recognition. + */ + if (inst->psvi == NULL) + inst->psvi = (void *) xsltExtMarker; + } +} +#endif /* XSLT_REFACTORED */ diff --git a/libxslt/preproc.h b/libxslt/preproc.h new file mode 100644 index 0000000..caf464a --- /dev/null +++ b/libxslt/preproc.h @@ -0,0 +1,43 @@ +/* + * Summary: precomputing stylesheets + * Description: this is the compilation phase, where most of the + * stylesheet is "compiled" into faster to use data. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_PRECOMP_H__ +#define __XML_XSLT_PRECOMP_H__ + +#include <libxml/tree.h> +#include "xsltexports.h" +#include "xsltInternals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Interfaces + */ +extern const xmlChar *xsltExtMarker; + +XSLTPUBFUN xsltElemPreCompPtr XSLTCALL + xsltDocumentComp (xsltStylesheetPtr style, + xmlNodePtr inst, + xsltTransformFunction function); + +XSLTPUBFUN void XSLTCALL + xsltStylePreCompute (xsltStylesheetPtr style, + xmlNodePtr inst); +XSLTPUBFUN void XSLTCALL + xsltFreeStylePreComps (xsltStylesheetPtr style); + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_PRECOMP_H__ */ + diff --git a/libxslt/security.c b/libxslt/security.c new file mode 100644 index 0000000..965175f --- /dev/null +++ b/libxslt/security.c @@ -0,0 +1,480 @@ +/* + * security.c: Implementation of the XSLT security framework + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#ifdef HAVE_MATH_H +#include <math.h> +#endif +#ifdef HAVE_FLOAT_H +#include <float.h> +#endif +#ifdef HAVE_IEEEFP_H +#include <ieeefp.h> +#endif +#ifdef HAVE_NAN_H +#include <nan.h> +#endif +#ifdef HAVE_CTYPE_H +#include <ctype.h> +#endif + +#if defined(WIN32) && !defined(__CYGWIN__) +#include <windows.h> +#ifndef INVALID_FILE_ATTRIBUTES +#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) +#endif +#endif + +#ifndef HAVE_STAT +# ifdef HAVE__STAT + /* MS C library seems to define stat and _stat. The definition + * is identical. Still, mapping them to each other causes a warning. */ +# ifndef _MSC_VER +# define stat(x,y) _stat(x,y) +# endif +# define HAVE_STAT +# endif +#endif + +#include <libxml/xmlmemory.h> +#include <libxml/tree.h> +#include <libxml/uri.h> +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "extensions.h" +#include "security.h" + + +struct _xsltSecurityPrefs { + xsltSecurityCheck readFile; + xsltSecurityCheck createFile; + xsltSecurityCheck createDir; + xsltSecurityCheck readNet; + xsltSecurityCheck writeNet; +}; + +static xsltSecurityPrefsPtr xsltDefaultSecurityPrefs = NULL; + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +/** + * xsltNewSecurityPrefs: + * + * Create a new security preference block + * + * Returns a pointer to the new block or NULL in case of error + */ +xsltSecurityPrefsPtr +xsltNewSecurityPrefs(void) { + xsltSecurityPrefsPtr ret; + + xsltInitGlobals(); + + ret = (xsltSecurityPrefsPtr) xmlMalloc(sizeof(xsltSecurityPrefs)); + if (ret == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewSecurityPrefs : malloc failed\n"); + return(NULL); + } + memset(ret, 0, sizeof(xsltSecurityPrefs)); + return(ret); +} + +/** + * xsltFreeSecurityPrefs: + * @sec: the security block to free + * + * Free up a security preference block + */ +void +xsltFreeSecurityPrefs(xsltSecurityPrefsPtr sec) { + if (sec == NULL) + return; + xmlFree(sec); +} + +/** + * xsltSetSecurityPrefs: + * @sec: the security block to update + * @option: the option to update + * @func: the user callback to use for this option + * + * Update the security option to use the new callback checking function + * + * Returns -1 in case of error, 0 otherwise + */ +int +xsltSetSecurityPrefs(xsltSecurityPrefsPtr sec, xsltSecurityOption option, + xsltSecurityCheck func) { + xsltInitGlobals(); + if (sec == NULL) + return(-1); + switch (option) { + case XSLT_SECPREF_READ_FILE: + sec->readFile = func; return(0); + case XSLT_SECPREF_WRITE_FILE: + sec->createFile = func; return(0); + case XSLT_SECPREF_CREATE_DIRECTORY: + sec->createDir = func; return(0); + case XSLT_SECPREF_READ_NETWORK: + sec->readNet = func; return(0); + case XSLT_SECPREF_WRITE_NETWORK: + sec->writeNet = func; return(0); + } + return(-1); +} + +/** + * xsltGetSecurityPrefs: + * @sec: the security block to update + * @option: the option to lookup + * + * Lookup the security option to get the callback checking function + * + * Returns NULL if not found, the function otherwise + */ +xsltSecurityCheck +xsltGetSecurityPrefs(xsltSecurityPrefsPtr sec, xsltSecurityOption option) { + if (sec == NULL) + return(NULL); + switch (option) { + case XSLT_SECPREF_READ_FILE: + return(sec->readFile); + case XSLT_SECPREF_WRITE_FILE: + return(sec->createFile); + case XSLT_SECPREF_CREATE_DIRECTORY: + return(sec->createDir); + case XSLT_SECPREF_READ_NETWORK: + return(sec->readNet); + case XSLT_SECPREF_WRITE_NETWORK: + return(sec->writeNet); + } + return(NULL); +} + +/** + * xsltSetDefaultSecurityPrefs: + * @sec: the security block to use + * + * Set the default security preference application-wide + */ +void +xsltSetDefaultSecurityPrefs(xsltSecurityPrefsPtr sec) { + + xsltDefaultSecurityPrefs = sec; +} + +/** + * xsltGetDefaultSecurityPrefs: + * + * Get the default security preference application-wide + * + * Returns the current xsltSecurityPrefsPtr in use or NULL if none + */ +xsltSecurityPrefsPtr +xsltGetDefaultSecurityPrefs(void) { + return(xsltDefaultSecurityPrefs); +} + +/** + * xsltSetCtxtSecurityPrefs: + * @sec: the security block to use + * @ctxt: an XSLT transformation context + * + * Set the security preference for a specific transformation + * + * Returns -1 in case of error, 0 otherwise + */ +int +xsltSetCtxtSecurityPrefs(xsltSecurityPrefsPtr sec, + xsltTransformContextPtr ctxt) { + if (ctxt == NULL) + return(-1); + ctxt->sec = (void *) sec; + return(0); +} + + +/** + * xsltSecurityAllow: + * @sec: the security block to use + * @ctxt: an XSLT transformation context + * @value: unused + * + * Function used to always allow an operation + * + * Returns 1 always + */ +int +xsltSecurityAllow(xsltSecurityPrefsPtr sec ATTRIBUTE_UNUSED, + xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, + const char *value ATTRIBUTE_UNUSED) { + return(1); +} + +/** + * xsltSecurityForbid: + * @sec: the security block to use + * @ctxt: an XSLT transformation context + * @value: unused + * + * Function used to always forbid an operation + * + * Returns 0 always + */ +int +xsltSecurityForbid(xsltSecurityPrefsPtr sec ATTRIBUTE_UNUSED, + xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, + const char *value ATTRIBUTE_UNUSED) { + return(0); +} + +/************************************************************************ + * * + * Internal interfaces * + * * + ************************************************************************/ + +/** + * xsltCheckFilename + * @path: the path to check + * + * function checks to see if @path is a valid source + * (file, socket...) for XML. + * + * TODO: remove at some point !!! + * Local copy of xmlCheckFilename to avoid a hard dependency on + * a new version of libxml2 + * + * if stat is not available on the target machine, + * returns 1. if stat fails, returns 0 (if calling + * stat on the filename fails, it can't be right). + * if stat succeeds and the file is a directory, + * returns 2. otherwise returns 1. + */ + +static int +xsltCheckFilename (const char *path) +{ +#ifdef HAVE_STAT + struct stat stat_buffer; +#if defined(WIN32) && !defined(__CYGWIN__) + DWORD dwAttrs; + + dwAttrs = GetFileAttributes(path); + if (dwAttrs != INVALID_FILE_ATTRIBUTES) { + if (dwAttrs & FILE_ATTRIBUTE_DIRECTORY) { + return 2; + } + } +#endif + + if (stat(path, &stat_buffer) == -1) + return 0; + +#ifdef S_ISDIR + if (S_ISDIR(stat_buffer.st_mode)) { + return 2; + } +#endif +#endif + return 1; +} + +static int +xsltCheckWritePath(xsltSecurityPrefsPtr sec, + xsltTransformContextPtr ctxt, + const char *path) +{ + int ret; + xsltSecurityCheck check; + char *directory; + + check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_WRITE_FILE); + if (check != NULL) { + ret = check(sec, ctxt, path); + if (ret == 0) { + xsltTransformError(ctxt, NULL, NULL, + "File write for %s refused\n", path); + return(0); + } + } + + directory = xmlParserGetDirectory (path); + + if (directory != NULL) { + ret = xsltCheckFilename(directory); + if (ret == 0) { + /* + * The directory doesn't exist check for creation + */ + check = xsltGetSecurityPrefs(sec, + XSLT_SECPREF_CREATE_DIRECTORY); + if (check != NULL) { + ret = check(sec, ctxt, directory); + if (ret == 0) { + xsltTransformError(ctxt, NULL, NULL, + "Directory creation for %s refused\n", + path); + xmlFree(directory); + return(0); + } + } + ret = xsltCheckWritePath(sec, ctxt, directory); + if (ret == 1) + ret = mkdir(directory, 0755); + } + xmlFree(directory); + if (ret < 0) + return(ret); + } + + return(1); +} + +/** + * xsltCheckWrite: + * @sec: the security options + * @ctxt: an XSLT transformation context + * @URL: the resource to be written + * + * Check if the resource is allowed to be written, if necessary makes + * some preliminary work like creating directories + * + * Return 1 if write is allowed, 0 if not and -1 in case or error. + */ +int +xsltCheckWrite(xsltSecurityPrefsPtr sec, + xsltTransformContextPtr ctxt, const xmlChar *URL) { + int ret; + xmlURIPtr uri; + xsltSecurityCheck check; + + uri = xmlParseURI((const char *)URL); + if (uri == NULL) { + uri = xmlCreateURI(); + if (uri == NULL) { + xsltTransformError(ctxt, NULL, NULL, + "xsltCheckWrite: out of memory for %s\n", URL); + return(-1); + } + uri->path = (char *)xmlStrdup(URL); + } + if ((uri->scheme == NULL) || + (xmlStrEqual(BAD_CAST uri->scheme, BAD_CAST "file"))) { + +#if defined(WIN32) && !defined(__CYGWIN__) + if ((uri->path)&&(uri->path[0]=='/')&& + (uri->path[1]!='\0')&&(uri->path[2]==':')) + ret = xsltCheckWritePath(sec, ctxt, uri->path+1); + else +#endif + + /* + * Check if we are allowed to write this file + */ + ret = xsltCheckWritePath(sec, ctxt, uri->path); + if (ret <= 0) { + xmlFreeURI(uri); + return(ret); + } + } else { + /* + * Check if we are allowed to write this network resource + */ + check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_WRITE_NETWORK); + if (check != NULL) { + ret = check(sec, ctxt, (const char *)URL); + if (ret == 0) { + xsltTransformError(ctxt, NULL, NULL, + "File write for %s refused\n", URL); + xmlFreeURI(uri); + return(0); + } + } + } + xmlFreeURI(uri); + return(1); +} + + +/** + * xsltCheckRead: + * @sec: the security options + * @ctxt: an XSLT transformation context + * @URL: the resource to be read + * + * Check if the resource is allowed to be read + * + * Return 1 if read is allowed, 0 if not and -1 in case or error. + */ +int +xsltCheckRead(xsltSecurityPrefsPtr sec, + xsltTransformContextPtr ctxt, const xmlChar *URL) { + int ret; + xmlURIPtr uri; + xsltSecurityCheck check; + + uri = xmlParseURI((const char *)URL); + if (uri == NULL) { + xsltTransformError(ctxt, NULL, NULL, + "xsltCheckRead: URL parsing failed for %s\n", + URL); + return(-1); + } + if ((uri->scheme == NULL) || + (xmlStrEqual(BAD_CAST uri->scheme, BAD_CAST "file"))) { + + /* + * Check if we are allowed to read this file + */ + check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_READ_FILE); + if (check != NULL) { + ret = check(sec, ctxt, uri->path); + if (ret == 0) { + xsltTransformError(ctxt, NULL, NULL, + "Local file read for %s refused\n", URL); + xmlFreeURI(uri); + return(0); + } + } + } else { + /* + * Check if we are allowed to write this network resource + */ + check = xsltGetSecurityPrefs(sec, XSLT_SECPREF_READ_NETWORK); + if (check != NULL) { + ret = check(sec, ctxt, (const char *)URL); + if (ret == 0) { + xsltTransformError(ctxt, NULL, NULL, + "Network file read for %s refused\n", URL); + xmlFreeURI(uri); + return(0); + } + } + } + xmlFreeURI(uri); + return(1); +} + diff --git a/libxslt/security.h b/libxslt/security.h new file mode 100644 index 0000000..bab5c8c --- /dev/null +++ b/libxslt/security.h @@ -0,0 +1,104 @@ +/* + * Summary: interface for the libxslt security framework + * Description: the libxslt security framework allow to restrict + * the access to new resources (file or URL) from + * the stylesheet at runtime. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_SECURITY_H__ +#define __XML_XSLT_SECURITY_H__ + +#include <libxml/tree.h> +#include "xsltexports.h" +#include "xsltInternals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * xsltSecurityPref: + * + * structure to indicate the preferences for security in the XSLT + * transformation. + */ +typedef struct _xsltSecurityPrefs xsltSecurityPrefs; +typedef xsltSecurityPrefs *xsltSecurityPrefsPtr; + +/** + * xsltSecurityOption: + * + * the set of option that can be configured + */ +typedef enum { + XSLT_SECPREF_READ_FILE = 1, + XSLT_SECPREF_WRITE_FILE, + XSLT_SECPREF_CREATE_DIRECTORY, + XSLT_SECPREF_READ_NETWORK, + XSLT_SECPREF_WRITE_NETWORK +} xsltSecurityOption; + +/** + * xsltSecurityCheck: + * + * User provided function to check the value of a string like a file + * path or an URL ... + */ +typedef int (*xsltSecurityCheck) (xsltSecurityPrefsPtr sec, + xsltTransformContextPtr ctxt, + const char *value); + +/* + * Module interfaces + */ +XSLTPUBFUN xsltSecurityPrefsPtr XSLTCALL + xsltNewSecurityPrefs (void); +XSLTPUBFUN void XSLTCALL + xsltFreeSecurityPrefs (xsltSecurityPrefsPtr sec); +XSLTPUBFUN int XSLTCALL + xsltSetSecurityPrefs (xsltSecurityPrefsPtr sec, + xsltSecurityOption option, + xsltSecurityCheck func); +XSLTPUBFUN xsltSecurityCheck XSLTCALL + xsltGetSecurityPrefs (xsltSecurityPrefsPtr sec, + xsltSecurityOption option); + +XSLTPUBFUN void XSLTCALL + xsltSetDefaultSecurityPrefs (xsltSecurityPrefsPtr sec); +XSLTPUBFUN xsltSecurityPrefsPtr XSLTCALL + xsltGetDefaultSecurityPrefs (void); + +XSLTPUBFUN int XSLTCALL + xsltSetCtxtSecurityPrefs (xsltSecurityPrefsPtr sec, + xsltTransformContextPtr ctxt); + +XSLTPUBFUN int XSLTCALL + xsltSecurityAllow (xsltSecurityPrefsPtr sec, + xsltTransformContextPtr ctxt, + const char *value); +XSLTPUBFUN int XSLTCALL + xsltSecurityForbid (xsltSecurityPrefsPtr sec, + xsltTransformContextPtr ctxt, + const char *value); +/* + * internal interfaces + */ +XSLTPUBFUN int XSLTCALL + xsltCheckWrite (xsltSecurityPrefsPtr sec, + xsltTransformContextPtr ctxt, + const xmlChar *URL); +XSLTPUBFUN int XSLTCALL + xsltCheckRead (xsltSecurityPrefsPtr sec, + xsltTransformContextPtr ctxt, + const xmlChar *URL); + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_SECURITY_H__ */ + diff --git a/libxslt/templates.c b/libxslt/templates.c new file mode 100644 index 0000000..02193f7 --- /dev/null +++ b/libxslt/templates.c @@ -0,0 +1,843 @@ +/* + * templates.c: Implementation of the template processing + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> + +#include <libxml/xmlmemory.h> +#include <libxml/globals.h> +#include <libxml/xmlerror.h> +#include <libxml/tree.h> +#include <libxml/dict.h> +#include <libxml/xpathInternals.h> +#include <libxml/parserInternals.h> +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "variables.h" +#include "functions.h" +#include "templates.h" +#include "transform.h" +#include "namespaces.h" +#include "attributes.h" + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_TEMPLATES +#endif + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +/** + * xsltEvalXPathPredicate: + * @ctxt: the XSLT transformation context + * @comp: the XPath compiled expression + * @nsList: the namespaces in scope + * @nsNr: the number of namespaces in scope + * + * Process the expression using XPath and evaluate the result as + * an XPath predicate + * + * Returns 1 is the predicate was true, 0 otherwise + */ +int +xsltEvalXPathPredicate(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, + xmlNsPtr *nsList, int nsNr) { + int ret; + xmlXPathObjectPtr res; + int oldNsNr; + xmlNsPtr *oldNamespaces; + xmlNodePtr oldInst; + int oldProximityPosition, oldContextSize; + + oldContextSize = ctxt->xpathCtxt->contextSize; + oldProximityPosition = ctxt->xpathCtxt->proximityPosition; + oldNsNr = ctxt->xpathCtxt->nsNr; + oldNamespaces = ctxt->xpathCtxt->namespaces; + oldInst = ctxt->inst; + + ctxt->xpathCtxt->node = ctxt->node; + ctxt->xpathCtxt->namespaces = nsList; + ctxt->xpathCtxt->nsNr = nsNr; + + res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); + + if (res != NULL) { + ret = xmlXPathEvalPredicate(ctxt->xpathCtxt, res); + xmlXPathFreeObject(res); +#ifdef WITH_XSLT_DEBUG_TEMPLATES + XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltEvalXPathPredicate: returns %d\n", ret)); +#endif + } else { +#ifdef WITH_XSLT_DEBUG_TEMPLATES + XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltEvalXPathPredicate: failed\n")); +#endif + ctxt->state = XSLT_STATE_STOPPED; + ret = 0; + } + ctxt->xpathCtxt->nsNr = oldNsNr; + + ctxt->xpathCtxt->namespaces = oldNamespaces; + ctxt->inst = oldInst; + ctxt->xpathCtxt->contextSize = oldContextSize; + ctxt->xpathCtxt->proximityPosition = oldProximityPosition; + + return(ret); +} + +/** + * xsltEvalXPathStringNs: + * @ctxt: the XSLT transformation context + * @comp: the compiled XPath expression + * @nsNr: the number of namespaces in the list + * @nsList: the list of in-scope namespaces to use + * + * Process the expression using XPath, allowing to pass a namespace mapping + * context and get a string + * + * Returns the computed string value or NULL, must be deallocated by the + * caller. + */ +xmlChar * +xsltEvalXPathStringNs(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, + int nsNr, xmlNsPtr *nsList) { + xmlChar *ret = NULL; + xmlXPathObjectPtr res; + xmlNodePtr oldInst; + xmlNodePtr oldNode; + int oldPos, oldSize; + int oldNsNr; + xmlNsPtr *oldNamespaces; + + oldInst = ctxt->inst; + oldNode = ctxt->node; + oldPos = ctxt->xpathCtxt->proximityPosition; + oldSize = ctxt->xpathCtxt->contextSize; + oldNsNr = ctxt->xpathCtxt->nsNr; + oldNamespaces = ctxt->xpathCtxt->namespaces; + + ctxt->xpathCtxt->node = ctxt->node; + /* TODO: do we need to propagate the namespaces here ? */ + ctxt->xpathCtxt->namespaces = nsList; + ctxt->xpathCtxt->nsNr = nsNr; + res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); + if (res != NULL) { + if (res->type != XPATH_STRING) + res = xmlXPathConvertString(res); + if (res->type == XPATH_STRING) { + ret = res->stringval; + res->stringval = NULL; + } else { + xsltTransformError(ctxt, NULL, NULL, + "xpath : string() function didn't return a String\n"); + } + xmlXPathFreeObject(res); + } else { + ctxt->state = XSLT_STATE_STOPPED; + } +#ifdef WITH_XSLT_DEBUG_TEMPLATES + XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltEvalXPathString: returns %s\n", ret)); +#endif + ctxt->inst = oldInst; + ctxt->node = oldNode; + ctxt->xpathCtxt->contextSize = oldSize; + ctxt->xpathCtxt->proximityPosition = oldPos; + ctxt->xpathCtxt->nsNr = oldNsNr; + ctxt->xpathCtxt->namespaces = oldNamespaces; + return(ret); +} + +/** + * xsltEvalXPathString: + * @ctxt: the XSLT transformation context + * @comp: the compiled XPath expression + * + * Process the expression using XPath and get a string + * + * Returns the computed string value or NULL, must be deallocated by the + * caller. + */ +xmlChar * +xsltEvalXPathString(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp) { + return(xsltEvalXPathStringNs(ctxt, comp, 0, NULL)); +} + +/** + * xsltEvalTemplateString: + * @ctxt: the XSLT transformation context + * @contextNode: the current node in the source tree + * @inst: the XSLT instruction (xsl:comment, xsl:processing-instruction) + * + * Processes the sequence constructor of the given instruction on + * @contextNode and converts the resulting tree to a string. + * This is needed by e.g. xsl:comment and xsl:processing-instruction. + * + * Returns the computed string value or NULL; it's up to the caller to + * free the result. + */ +xmlChar * +xsltEvalTemplateString(xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, + xmlNodePtr inst) +{ + xmlNodePtr oldInsert, insert = NULL; + xmlChar *ret; + + if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL) || + (inst->type != XML_ELEMENT_NODE)) + return(NULL); + + if (inst->children == NULL) + return(NULL); + + /* + * This creates a temporary element-node to add the resulting + * text content to. + * OPTIMIZE TODO: Keep such an element-node in the transformation + * context to avoid creating it every time. + */ + insert = xmlNewDocNode(ctxt->output, NULL, + (const xmlChar *)"fake", NULL); + if (insert == NULL) { + xsltTransformError(ctxt, NULL, contextNode, + "Failed to create temporary node\n"); + return(NULL); + } + oldInsert = ctxt->insert; + ctxt->insert = insert; + /* + * OPTIMIZE TODO: if inst->children consists only of text-nodes. + */ + xsltApplyOneTemplate(ctxt, contextNode, inst->children, NULL, NULL); + + ctxt->insert = oldInsert; + + ret = xmlNodeGetContent(insert); + if (insert != NULL) + xmlFreeNode(insert); + return(ret); +} + +/** + * xsltAttrTemplateValueProcessNode: + * @ctxt: the XSLT transformation context + * @str: the attribute template node value + * @inst: the instruction (or LRE) in the stylesheet holding the + * attribute with an AVT + * + * Process the given string, allowing to pass a namespace mapping + * context and return the new string value. + * + * Called by: + * - xsltAttrTemplateValueProcess() (templates.c) + * - xsltEvalAttrValueTemplate() (templates.c) + * + * QUESTION: Why is this function public? It is not used outside + * of templates.c. + * + * Returns the computed string value or NULL, must be deallocated by the + * caller. + */ +xmlChar * +xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt, + const xmlChar *str, xmlNodePtr inst) +{ + xmlChar *ret = NULL; + const xmlChar *cur; + xmlChar *expr, *val; + xmlNsPtr *nsList = NULL; + int nsNr = 0; + + if (str == NULL) return(NULL); + if (*str == 0) + return(xmlStrndup((xmlChar *)"", 0)); + + cur = str; + while (*cur != 0) { + if (*cur == '{') { + if (*(cur+1) == '{') { /* escaped '{' */ + cur++; + ret = xmlStrncat(ret, str, cur - str); + cur++; + str = cur; + continue; + } + ret = xmlStrncat(ret, str, cur - str); + str = cur; + cur++; + while ((*cur != 0) && (*cur != '}')) { + /* Need to check for literal (bug539741) */ + if ((*cur == '\'') || (*cur == '"')) { + char delim = *(cur++); + while ((*cur != 0) && (*cur != delim)) + cur++; + if (*cur != 0) + cur++; /* skip the ending delimiter */ + } else + cur++; + } + if (*cur == 0) { + xsltTransformError(ctxt, NULL, inst, + "xsltAttrTemplateValueProcessNode: unmatched '{'\n"); + ret = xmlStrncat(ret, str, cur - str); + return(ret); + } + str++; + expr = xmlStrndup(str, cur - str); + if (expr == NULL) + return(ret); + else if (*expr == '{') { + ret = xmlStrcat(ret, expr); + xmlFree(expr); + } else { + xmlXPathCompExprPtr comp; + /* + * TODO: keep precompiled form around + */ + if ((nsList == NULL) && (inst != NULL)) { + int i = 0; + + nsList = xmlGetNsList(inst->doc, inst); + if (nsList != NULL) { + while (nsList[i] != NULL) + i++; + nsNr = i; + } + } + comp = xmlXPathCompile(expr); + val = xsltEvalXPathStringNs(ctxt, comp, nsNr, nsList); + xmlXPathFreeCompExpr(comp); + xmlFree(expr); + if (val != NULL) { + ret = xmlStrcat(ret, val); + xmlFree(val); + } + } + cur++; + str = cur; + } else if (*cur == '}') { + cur++; + if (*cur == '}') { /* escaped '}' */ + ret = xmlStrncat(ret, str, cur - str); + cur++; + str = cur; + continue; + } else { + xsltTransformError(ctxt, NULL, inst, + "xsltAttrTemplateValueProcessNode: unmatched '}'\n"); + } + } else + cur++; + } + if (cur != str) { + ret = xmlStrncat(ret, str, cur - str); + } + + if (nsList != NULL) + xmlFree(nsList); + + return(ret); +} + +/** + * xsltAttrTemplateValueProcess: + * @ctxt: the XSLT transformation context + * @str: the attribute template node value + * + * Process the given node and return the new string value. + * + * Returns the computed string value or NULL, must be deallocated by the + * caller. + */ +xmlChar * +xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt, const xmlChar *str) { + return(xsltAttrTemplateValueProcessNode(ctxt, str, NULL)); +} + +/** + * xsltEvalAttrValueTemplate: + * @ctxt: the XSLT transformation context + * @inst: the instruction (or LRE) in the stylesheet holding the + * attribute with an AVT + * @name: the attribute QName + * @ns: the attribute namespace URI + * + * Evaluate a attribute value template, i.e. the attribute value can + * contain expressions contained in curly braces ({}) and those are + * substituted by they computed value. + * + * Returns the computed string value or NULL, must be deallocated by the + * caller. + */ +xmlChar * +xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst, + const xmlChar *name, const xmlChar *ns) +{ + xmlChar *ret; + xmlChar *expr; + + if ((ctxt == NULL) || (inst == NULL) || (name == NULL) || + (inst->type != XML_ELEMENT_NODE)) + return(NULL); + + expr = xsltGetNsProp(inst, name, ns); + if (expr == NULL) + return(NULL); + + /* + * TODO: though now {} is detected ahead, it would still be good to + * optimize both functions to keep the splitted value if the + * attribute content and the XPath precompiled expressions around + */ + + ret = xsltAttrTemplateValueProcessNode(ctxt, expr, inst); +#ifdef WITH_XSLT_DEBUG_TEMPLATES + XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret)); +#endif + if (expr != NULL) + xmlFree(expr); + return(ret); +} + +/** + * xsltEvalStaticAttrValueTemplate: + * @style: the XSLT stylesheet + * @inst: the instruction (or LRE) in the stylesheet holding the + * attribute with an AVT + * @name: the attribute Name + * @ns: the attribute namespace URI + * @found: indicator whether the attribute is present + * + * Check if an attribute value template has a static value, i.e. the + * attribute value does not contain expressions contained in curly braces ({}) + * + * Returns the static string value or NULL, must be deallocated by the + * caller. + */ +const xmlChar * +xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr inst, + const xmlChar *name, const xmlChar *ns, int *found) { + const xmlChar *ret; + xmlChar *expr; + + if ((style == NULL) || (inst == NULL) || (name == NULL) || + (inst->type != XML_ELEMENT_NODE)) + return(NULL); + + expr = xsltGetNsProp(inst, name, ns); + if (expr == NULL) { + *found = 0; + return(NULL); + } + *found = 1; + + ret = xmlStrchr(expr, '{'); + if (ret != NULL) { + xmlFree(expr); + return(NULL); + } + ret = xmlDictLookup(style->dict, expr, -1); + xmlFree(expr); + return(ret); +} + +/** + * xsltAttrTemplateProcess: + * @ctxt: the XSLT transformation context + * @target: the element where the attribute will be grafted + * @attr: the attribute node of a literal result element + * + * Process one attribute of a Literal Result Element (in the stylesheet). + * Evaluates Attribute Value Templates and copies the attribute over to + * the result element. + * This does *not* process attribute sets (xsl:use-attribute-set). + * + * + * Returns the generated attribute node. + */ +xmlAttrPtr +xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target, + xmlAttrPtr attr) +{ + const xmlChar *value; + xmlAttrPtr ret; + + if ((ctxt == NULL) || (attr == NULL) || (target == NULL) || + (target->type != XML_ELEMENT_NODE)) + return(NULL); + + if (attr->type != XML_ATTRIBUTE_NODE) + return(NULL); + + /* + * Skip all XSLT attributes. + */ +#ifdef XSLT_REFACTORED + if (attr->psvi == xsltXSLTAttrMarker) + return(NULL); +#else + if ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) + return(NULL); +#endif + /* + * Get the value. + */ + if (attr->children != NULL) { + if ((attr->children->type != XML_TEXT_NODE) || + (attr->children->next != NULL)) + { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: The children of an attribute node of a " + "literal result element are not in the expected form.\n"); + return(NULL); + } + value = attr->children->content; + if (value == NULL) + value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); + } else + value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); + /* + * Overwrite duplicates. + */ + ret = target->properties; + while (ret != NULL) { + if (((attr->ns != NULL) == (ret->ns != NULL)) && + xmlStrEqual(ret->name, attr->name) && + ((attr->ns == NULL) || xmlStrEqual(ret->ns->href, attr->ns->href))) + { + break; + } + ret = ret->next; + } + if (ret != NULL) { + /* free the existing value */ + xmlFreeNodeList(ret->children); + ret->children = ret->last = NULL; + /* + * Adjust ns-prefix if needed. + */ + if ((ret->ns != NULL) && + (! xmlStrEqual(ret->ns->prefix, attr->ns->prefix))) + { + ret->ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target); + } + } else { + /* create a new attribute */ + if (attr->ns != NULL) + ret = xmlNewNsProp(target, + xsltGetNamespace(ctxt, attr->parent, attr->ns, target), + attr->name, NULL); + else + ret = xmlNewNsProp(target, NULL, attr->name, NULL); + } + /* + * Set the value. + */ + if (ret != NULL) { + xmlNodePtr text; + + text = xmlNewText(NULL); + if (text != NULL) { + ret->last = ret->children = text; + text->parent = (xmlNodePtr) ret; + text->doc = ret->doc; + + if (attr->psvi != NULL) { + /* + * Evaluate the Attribute Value Template. + */ + xmlChar *val; + val = xsltEvalAVT(ctxt, attr->psvi, attr->parent); + if (val == NULL) { + /* + * TODO: Damn, we need an easy mechanism to report + * qualified names! + */ + if (attr->ns) { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to evaluate the AVT " + "of attribute '{%s}%s'.\n", + attr->ns->href, attr->name); + } else { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to evaluate the AVT " + "of attribute '%s'.\n", + attr->name); + } + text->content = xmlStrdup(BAD_CAST ""); + } else { + text->content = val; + } + } else if ((ctxt->internalized) && (target != NULL) && + (target->doc != NULL) && + (target->doc->dict == ctxt->dict) && + xmlDictOwns(ctxt->dict, value)) { + text->content = (xmlChar *) value; + } else { + text->content = xmlStrdup(value); + } + } + } else { + if (attr->ns) { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to create attribute '{%s}%s'.\n", + attr->ns->href, attr->name); + } else { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to create attribute '%s'.\n", + attr->name); + } + } + return(ret); +} + + +/** + * xsltAttrListTemplateProcess: + * @ctxt: the XSLT transformation context + * @target: the element where the attributes will be grafted + * @attrs: the first attribute + * + * Processes all attributes of a Literal Result Element. + * Attribute references are applied via xsl:use-attribute-set + * attributes. + * Copies all non XSLT-attributes over to the @target element + * and evaluates Attribute Value Templates. + * + * Called by xsltApplySequenceConstructor() (transform.c). + * + * Returns a new list of attribute nodes, or NULL in case of error. + * (Don't assign the result to @target->properties; if + * the result is NULL, you'll get memory leaks, since the + * attributes will be disattached.) + */ +xmlAttrPtr +xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt, + xmlNodePtr target, xmlAttrPtr attrs) +{ + xmlAttrPtr attr, copy, last; + xmlNodePtr oldInsert, text; + xmlNsPtr origNs = NULL, copyNs = NULL; + const xmlChar *value; + xmlChar *valueAVT; + + if ((ctxt == NULL) || (target == NULL) || (attrs == NULL) || + (target->type != XML_ELEMENT_NODE)) + return(NULL); + + oldInsert = ctxt->insert; + ctxt->insert = target; + + /* + * Instantiate LRE-attributes. + */ + if (target->properties) { + last = target->properties; + while (last->next != NULL) + last = last->next; + } else { + last = NULL; + } + attr = attrs; + do { + /* + * Skip XSLT attributes. + */ +#ifdef XSLT_REFACTORED + if (attr->psvi == xsltXSLTAttrMarker) { + goto next_attribute; + } +#else + if ((attr->ns != NULL) && + xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) + { + goto next_attribute; + } +#endif + /* + * Get the value. + */ + if (attr->children != NULL) { + if ((attr->children->type != XML_TEXT_NODE) || + (attr->children->next != NULL)) + { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: The children of an attribute node of a " + "literal result element are not in the expected form.\n"); + goto error; + } + value = attr->children->content; + if (value == NULL) + value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); + } else + value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); + + /* + * Create a new attribute. + */ + copy = xmlNewDocProp(target->doc, attr->name, NULL); + if (copy == NULL) { + if (attr->ns) { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to create attribute '{%s}%s'.\n", + attr->ns->href, attr->name); + } else { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to create attribute '%s'.\n", + attr->name); + } + goto error; + } + /* + * Attach it to the target element. + */ + copy->parent = target; + if (last == NULL) { + target->properties = copy; + last = copy; + } else { + last->next = copy; + copy->prev = last; + last = copy; + } + /* + * Set the namespace. Avoid lookups of same namespaces. + */ + if (attr->ns != origNs) { + origNs = attr->ns; + if (attr->ns != NULL) { +#ifdef XSLT_REFACTORED + copyNs = xsltGetSpecialNamespace(ctxt, attr->parent, + attr->ns->href, attr->ns->prefix, target); +#else + copyNs = xsltGetNamespace(ctxt, attr->parent, + attr->ns, target); +#endif + if (copyNs == NULL) + goto error; + } else + copyNs = NULL; + } + copy->ns = copyNs; + + /* + * Set the value. + */ + text = xmlNewText(NULL); + if (text != NULL) { + copy->last = copy->children = text; + text->parent = (xmlNodePtr) copy; + text->doc = copy->doc; + + if (attr->psvi != NULL) { + /* + * Evaluate the Attribute Value Template. + */ + valueAVT = xsltEvalAVT(ctxt, attr->psvi, attr->parent); + if (valueAVT == NULL) { + /* + * TODO: Damn, we need an easy mechanism to report + * qualified names! + */ + if (attr->ns) { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to evaluate the AVT " + "of attribute '{%s}%s'.\n", + attr->ns->href, attr->name); + } else { + xsltTransformError(ctxt, NULL, attr->parent, + "Internal error: Failed to evaluate the AVT " + "of attribute '%s'.\n", + attr->name); + } + text->content = xmlStrdup(BAD_CAST ""); + goto error; + } else { + text->content = valueAVT; + } + } else if ((ctxt->internalized) && + (target->doc != NULL) && + (target->doc->dict == ctxt->dict) && + xmlDictOwns(ctxt->dict, value)) + { + text->content = (xmlChar *) value; + } else { + text->content = xmlStrdup(value); + } + if ((copy != NULL) && (text != NULL) && + (xmlIsID(copy->doc, copy->parent, copy))) + xmlAddID(NULL, copy->doc, text->content, copy); + } + +next_attribute: + attr = attr->next; + } while (attr != NULL); + + /* + * Apply attribute-sets. + * The creation of such attributes will not overwrite any existing + * attribute. + */ + attr = attrs; + do { +#ifdef XSLT_REFACTORED + if ((attr->psvi == xsltXSLTAttrMarker) && + xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets")) + { + xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); + } +#else + if ((attr->ns != NULL) && + xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets") && + xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) + { + xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); + } +#endif + attr = attr->next; + } while (attr != NULL); + + ctxt->insert = oldInsert; + return(target->properties); + +error: + ctxt->insert = oldInsert; + return(NULL); +} + + +/** + * xsltTemplateProcess: + * @ctxt: the XSLT transformation context + * @node: the attribute template node + * + * Obsolete. Don't use it. + * + * Returns NULL. + */ +xmlNodePtr * +xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) { + if (node == NULL) + return(NULL); + + return(0); +} + + diff --git a/libxslt/templates.h b/libxslt/templates.h new file mode 100644 index 0000000..84a9de4 --- /dev/null +++ b/libxslt/templates.h @@ -0,0 +1,77 @@ +/* + * Summary: interface for the template processing + * Description: This set of routine encapsulates XPath calls + * and Attribute Value Templates evaluation. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_TEMPLATES_H__ +#define __XML_XSLT_TEMPLATES_H__ + +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> +#include "xsltexports.h" +#include "xsltInternals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +XSLTPUBFUN int XSLTCALL + xsltEvalXPathPredicate (xsltTransformContextPtr ctxt, + xmlXPathCompExprPtr comp, + xmlNsPtr *nsList, + int nsNr); +XSLTPUBFUN xmlChar * XSLTCALL + xsltEvalTemplateString (xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, + xmlNodePtr inst); +XSLTPUBFUN xmlChar * XSLTCALL + xsltEvalAttrValueTemplate (xsltTransformContextPtr ctxt, + xmlNodePtr node, + const xmlChar *name, + const xmlChar *ns); +XSLTPUBFUN const xmlChar * XSLTCALL + xsltEvalStaticAttrValueTemplate (xsltStylesheetPtr style, + xmlNodePtr node, + const xmlChar *name, + const xmlChar *ns, + int *found); + +/* TODO: this is obviously broken ... the namespaces should be passed too ! */ +XSLTPUBFUN xmlChar * XSLTCALL + xsltEvalXPathString (xsltTransformContextPtr ctxt, + xmlXPathCompExprPtr comp); +XSLTPUBFUN xmlChar * XSLTCALL + xsltEvalXPathStringNs (xsltTransformContextPtr ctxt, + xmlXPathCompExprPtr comp, + int nsNr, + xmlNsPtr *nsList); + +XSLTPUBFUN xmlNodePtr * XSLTCALL + xsltTemplateProcess (xsltTransformContextPtr ctxt, + xmlNodePtr node); +XSLTPUBFUN xmlAttrPtr XSLTCALL + xsltAttrListTemplateProcess (xsltTransformContextPtr ctxt, + xmlNodePtr target, + xmlAttrPtr cur); +XSLTPUBFUN xmlAttrPtr XSLTCALL + xsltAttrTemplateProcess (xsltTransformContextPtr ctxt, + xmlNodePtr target, + xmlAttrPtr attr); +XSLTPUBFUN xmlChar * XSLTCALL + xsltAttrTemplateValueProcess (xsltTransformContextPtr ctxt, + const xmlChar* attr); +XSLTPUBFUN xmlChar * XSLTCALL + xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt, + const xmlChar* str, + xmlNodePtr node); +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_TEMPLATES_H__ */ + diff --git a/libxslt/transform.c b/libxslt/transform.c new file mode 100644 index 0000000..b3fce80 --- /dev/null +++ b/libxslt/transform.c @@ -0,0 +1,6449 @@ +/* + * transform.c: Implementation of the XSL Transformation 1.0 engine + * transform part, i.e. applying a Stylesheet to a document + * + * References: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * Michael Kay "XSLT Programmer's Reference" pp 637-643 + * Writing Multiple Output Files + * + * XSLT-1.1 Working Draft + * http://www.w3.org/TR/xslt11#multiple-output + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> +#include <stdio.h> + +#include <libxml/xmlmemory.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/valid.h> +#include <libxml/hash.h> +#include <libxml/encoding.h> +#include <libxml/xmlerror.h> +#include <libxml/xpath.h> +#include <libxml/parserInternals.h> +#include <libxml/xpathInternals.h> +#include <libxml/HTMLtree.h> +#include <libxml/debugXML.h> +#include <libxml/uri.h> +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "pattern.h" +#include "transform.h" +#include "variables.h" +#include "numbersInternals.h" +#include "namespaces.h" +#include "attributes.h" +#include "templates.h" +#include "imports.h" +#include "keys.h" +#include "documents.h" +#include "extensions.h" +#include "extra.h" +#include "preproc.h" +#include "security.h" + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_EXTRA +#define WITH_XSLT_DEBUG_PROCESS +#define WITH_XSLT_DEBUG_VARIABLE +#endif + +#define XSLT_GENERATE_HTML_DOCTYPE +#ifdef XSLT_GENERATE_HTML_DOCTYPE +static int xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID, + const xmlChar **systemID); +#endif + +int xsltMaxDepth = 3000; +int xsltMaxVars = 15000; + +/* + * Useful macros + */ + +#ifndef FALSE +# define FALSE (0 == 1) +# define TRUE (!FALSE) +#endif + +#define IS_BLANK_NODE(n) \ + (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content))) + + +/* +* Forward declarations +*/ + +static xmlNsPtr +xsltCopyNamespaceListInternal(xmlNodePtr node, xmlNsPtr cur); + +static xmlNodePtr +xsltCopyTreeInternal(xsltTransformContextPtr ctxt, + xmlNodePtr invocNode, + xmlNodePtr node, + xmlNodePtr insert, int isLRE, int topElemVisited); + +static void +xsltApplySequenceConstructor(xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, xmlNodePtr list, + xsltTemplatePtr templ); + +static void +xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, + xmlNodePtr list, + xsltTemplatePtr templ, + xsltStackElemPtr withParams); + +/** + * templPush: + * @ctxt: the transformation context + * @value: the template to push on the stack + * + * Push a template on the stack + * + * Returns the new index in the stack or 0 in case of error + */ +static int +templPush(xsltTransformContextPtr ctxt, xsltTemplatePtr value) +{ + if (ctxt->templMax == 0) { + ctxt->templMax = 4; + ctxt->templTab = + (xsltTemplatePtr *) xmlMalloc(ctxt->templMax * + sizeof(ctxt->templTab[0])); + if (ctxt->templTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); + return (0); + } + } + else if (ctxt->templNr >= ctxt->templMax) { + ctxt->templMax *= 2; + ctxt->templTab = + (xsltTemplatePtr *) xmlRealloc(ctxt->templTab, + ctxt->templMax * + sizeof(ctxt->templTab[0])); + if (ctxt->templTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); + return (0); + } + } + ctxt->templTab[ctxt->templNr] = value; + ctxt->templ = value; + return (ctxt->templNr++); +} +/** + * templPop: + * @ctxt: the transformation context + * + * Pop a template value from the stack + * + * Returns the stored template value + */ +static xsltTemplatePtr +templPop(xsltTransformContextPtr ctxt) +{ + xsltTemplatePtr ret; + + if (ctxt->templNr <= 0) + return (0); + ctxt->templNr--; + if (ctxt->templNr > 0) + ctxt->templ = ctxt->templTab[ctxt->templNr - 1]; + else + ctxt->templ = (xsltTemplatePtr) 0; + ret = ctxt->templTab[ctxt->templNr]; + ctxt->templTab[ctxt->templNr] = 0; + return (ret); +} + +/** + * xsltLocalVariablePop: + * @ctxt: the transformation context + * @limitNr: number of variables which should remain + * @level: the depth in the xsl:template's tree + * + * Pops all variable values at the given @depth from the stack. + * + * Returns the stored variable value + * **NOTE:** + * This is an internal routine and should not be called by users! + */ +void +xsltLocalVariablePop(xsltTransformContextPtr ctxt, int limitNr, int level) +{ + xsltStackElemPtr variable; + + if (ctxt->varsNr <= 0) + return; + + do { + if (ctxt->varsNr <= limitNr) + break; + variable = ctxt->varsTab[ctxt->varsNr - 1]; + if (variable->level <= level) + break; + if (variable->level >= 0) + xsltFreeStackElemList(variable); + ctxt->varsNr--; + } while (ctxt->varsNr != 0); + if (ctxt->varsNr > 0) + ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1]; + else + ctxt->vars = NULL; +} + +/** + * xsltTemplateParamsCleanup: + * + * Removes xsl:param and xsl:with-param items from the + * variable-stack. Only xsl:with-param items are not freed. + */ +static void +xsltTemplateParamsCleanup(xsltTransformContextPtr ctxt) +{ + xsltStackElemPtr param; + + for (; ctxt->varsNr > ctxt->varsBase; ctxt->varsNr--) { + param = ctxt->varsTab[ctxt->varsNr -1]; + /* + * Free xsl:param items. + * xsl:with-param items will have a level of -1 or -2. + */ + if (param->level >= 0) { + xsltFreeStackElemList(param); + } + } + if (ctxt->varsNr > 0) + ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1]; + else + ctxt->vars = NULL; +} + +/** + * profPush: + * @ctxt: the transformation context + * @value: the profiling value to push on the stack + * + * Push a profiling value on the stack + * + * Returns the new index in the stack or 0 in case of error + */ +static int +profPush(xsltTransformContextPtr ctxt, long value) +{ + if (ctxt->profMax == 0) { + ctxt->profMax = 4; + ctxt->profTab = + (long *) xmlMalloc(ctxt->profMax * sizeof(ctxt->profTab[0])); + if (ctxt->profTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); + return (0); + } + } + else if (ctxt->profNr >= ctxt->profMax) { + ctxt->profMax *= 2; + ctxt->profTab = + (long *) xmlRealloc(ctxt->profTab, + ctxt->profMax * sizeof(ctxt->profTab[0])); + if (ctxt->profTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); + return (0); + } + } + ctxt->profTab[ctxt->profNr] = value; + ctxt->prof = value; + return (ctxt->profNr++); +} +/** + * profPop: + * @ctxt: the transformation context + * + * Pop a profiling value from the stack + * + * Returns the stored profiling value + */ +static long +profPop(xsltTransformContextPtr ctxt) +{ + long ret; + + if (ctxt->profNr <= 0) + return (0); + ctxt->profNr--; + if (ctxt->profNr > 0) + ctxt->prof = ctxt->profTab[ctxt->profNr - 1]; + else + ctxt->prof = (long) 0; + ret = ctxt->profTab[ctxt->profNr]; + ctxt->profTab[ctxt->profNr] = 0; + return (ret); +} + +static void +profCallgraphAdd(xsltTemplatePtr templ, xsltTemplatePtr parent) +{ + int i; + + if (templ->templMax == 0) { + templ->templMax = 4; + templ->templCalledTab = + (xsltTemplatePtr *) xmlMalloc(templ->templMax * + sizeof(templ->templCalledTab[0])); + templ->templCountTab = + (int *) xmlMalloc(templ->templMax * + sizeof(templ->templCountTab[0])); + if (templ->templCalledTab == NULL || templ->templCountTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); + return; + } + } + else if (templ->templNr >= templ->templMax) { + templ->templMax *= 2; + templ->templCalledTab = + (xsltTemplatePtr *) xmlRealloc(templ->templCalledTab, + templ->templMax * + sizeof(templ->templCalledTab[0])); + templ->templCountTab = + (int *) xmlRealloc(templ->templCountTab, + templ->templMax * + sizeof(templ->templCountTab[0])); + if (templ->templCalledTab == NULL || templ->templCountTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); + return; + } + } + + for (i = 0; i < templ->templNr; i++) { + if (templ->templCalledTab[i] == parent) { + templ->templCountTab[i]++; + break; + } + } + if (i == templ->templNr) { + /* not found, add new one */ + templ->templCalledTab[templ->templNr] = parent; + templ->templCountTab[templ->templNr] = 1; + templ->templNr++; + } +} + +/** + * xsltPreCompEval: + * @ctxt: transform context + * @node: context node + * @comp: precompiled expression + * + * Evaluate a precompiled XPath expression. + */ +static xmlXPathObjectPtr +xsltPreCompEval(xsltTransformContextPtr ctxt, xmlNodePtr node, + xsltStylePreCompPtr comp) { + xmlXPathObjectPtr res; + xmlXPathContextPtr xpctxt; + xmlNodePtr oldXPContextNode; + xmlNsPtr *oldXPNamespaces; + int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; + + xpctxt = ctxt->xpathCtxt; + oldXPContextNode = xpctxt->node; + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPContextSize = xpctxt->contextSize; + oldXPNsNr = xpctxt->nsNr; + oldXPNamespaces = xpctxt->namespaces; + + xpctxt->node = node; +#ifdef XSLT_REFACTORED + if (comp->inScopeNs != NULL) { + xpctxt->namespaces = comp->inScopeNs->list; + xpctxt->nsNr = comp->inScopeNs->xpathNumber; + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } +#else + xpctxt->namespaces = comp->nsList; + xpctxt->nsNr = comp->nsNr; +#endif + + res = xmlXPathCompiledEval(comp->comp, xpctxt); + + xpctxt->node = oldXPContextNode; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->contextSize = oldXPContextSize; + xpctxt->nsNr = oldXPNsNr; + xpctxt->namespaces = oldXPNamespaces; + + return(res); +} + +/** + * xsltPreCompEvalToBoolean: + * @ctxt: transform context + * @node: context node + * @comp: precompiled expression + * + * Evaluate a precompiled XPath expression as boolean. + */ +static int +xsltPreCompEvalToBoolean(xsltTransformContextPtr ctxt, xmlNodePtr node, + xsltStylePreCompPtr comp) { + int res; + xmlXPathContextPtr xpctxt; + xmlNodePtr oldXPContextNode; + xmlNsPtr *oldXPNamespaces; + int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; + + xpctxt = ctxt->xpathCtxt; + oldXPContextNode = xpctxt->node; + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPContextSize = xpctxt->contextSize; + oldXPNsNr = xpctxt->nsNr; + oldXPNamespaces = xpctxt->namespaces; + + xpctxt->node = node; +#ifdef XSLT_REFACTORED + if (comp->inScopeNs != NULL) { + xpctxt->namespaces = comp->inScopeNs->list; + xpctxt->nsNr = comp->inScopeNs->xpathNumber; + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } +#else + xpctxt->namespaces = comp->nsList; + xpctxt->nsNr = comp->nsNr; +#endif + + res = xmlXPathCompiledEvalToBoolean(comp->comp, xpctxt); + + xpctxt->node = oldXPContextNode; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->contextSize = oldXPContextSize; + xpctxt->nsNr = oldXPNsNr; + xpctxt->namespaces = oldXPNamespaces; + + return(res); +} + +/************************************************************************ + * * + * XInclude default settings * + * * + ************************************************************************/ + +static int xsltDoXIncludeDefault = 0; + +/** + * xsltSetXIncludeDefault: + * @xinclude: whether to do XInclude processing + * + * Set whether XInclude should be processed on document being loaded by default + */ +void +xsltSetXIncludeDefault(int xinclude) { + xsltDoXIncludeDefault = (xinclude != 0); +} + +/** + * xsltGetXIncludeDefault: + * + * Provides the default state for XInclude processing + * + * Returns 0 if there is no processing 1 otherwise + */ +int +xsltGetXIncludeDefault(void) { + return(xsltDoXIncludeDefault); +} + +unsigned long xsltDefaultTrace = (unsigned long) XSLT_TRACE_ALL; + +/** + * xsltDebugSetDefaultTrace: + * @val: tracing level mask + * + * Set the default debug tracing level mask + */ +void xsltDebugSetDefaultTrace(xsltDebugTraceCodes val) { + xsltDefaultTrace = val; +} + +/** + * xsltDebugGetDefaultTrace: + * + * Get the current default debug tracing level mask + * + * Returns the current default debug tracing level mask + */ +xsltDebugTraceCodes xsltDebugGetDefaultTrace() { + return xsltDefaultTrace; +} + +/************************************************************************ + * * + * Handling of Transformation Contexts * + * * + ************************************************************************/ + +static xsltTransformCachePtr +xsltTransformCacheCreate(void) +{ + xsltTransformCachePtr ret; + + ret = (xsltTransformCachePtr) xmlMalloc(sizeof(xsltTransformCache)); + if (ret == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltTransformCacheCreate : malloc failed\n"); + return(NULL); + } + memset(ret, 0, sizeof(xsltTransformCache)); + return(ret); +} + +static void +xsltTransformCacheFree(xsltTransformCachePtr cache) +{ + if (cache == NULL) + return; + /* + * Free tree fragments. + */ + if (cache->RVT) { + xmlDocPtr tmp, cur = cache->RVT; + while (cur) { + tmp = cur; + cur = (xmlDocPtr) cur->next; + if (tmp->_private != NULL) { + /* + * Tree the document info. + */ + xsltFreeDocumentKeys((xsltDocumentPtr) tmp->_private); + xmlFree(tmp->_private); + } + xmlFreeDoc(tmp); + } + } + /* + * Free vars/params. + */ + if (cache->stackItems) { + xsltStackElemPtr tmp, cur = cache->stackItems; + while (cur) { + tmp = cur; + cur = cur->next; + /* + * REVISIT TODO: Should be call a destruction-function + * instead? + */ + xmlFree(tmp); + } + } + xmlFree(cache); +} + +/** + * xsltNewTransformContext: + * @style: a parsed XSLT stylesheet + * @doc: the input document + * + * Create a new XSLT TransformContext + * + * Returns the newly allocated xsltTransformContextPtr or NULL in case of error + */ +xsltTransformContextPtr +xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) { + xsltTransformContextPtr cur; + xsltDocumentPtr docu; + int i; + + xsltInitGlobals(); + + cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, (xmlNodePtr)doc, + "xsltNewTransformContext : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltTransformContext)); + + cur->cache = xsltTransformCacheCreate(); + if (cur->cache == NULL) + goto internal_err; + /* + * setup of the dictionary must be done early as some of the + * processing later like key handling may need it. + */ + cur->dict = xmlDictCreateSub(style->dict); + cur->internalized = ((style->internalized) && (cur->dict != NULL)); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "Creating sub-dictionary from stylesheet for transformation\n"); +#endif + + /* + * initialize the template stack + */ + cur->templTab = (xsltTemplatePtr *) + xmlMalloc(10 * sizeof(xsltTemplatePtr)); + if (cur->templTab == NULL) { + xsltTransformError(NULL, NULL, (xmlNodePtr) doc, + "xsltNewTransformContext: out of memory\n"); + goto internal_err; + } + cur->templNr = 0; + cur->templMax = 5; + cur->templ = NULL; + cur->maxTemplateDepth = xsltMaxDepth; + + /* + * initialize the variables stack + */ + cur->varsTab = (xsltStackElemPtr *) + xmlMalloc(10 * sizeof(xsltStackElemPtr)); + if (cur->varsTab == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltNewTransformContext: out of memory\n"); + goto internal_err; + } + cur->varsNr = 0; + cur->varsMax = 10; + cur->vars = NULL; + cur->varsBase = 0; + cur->maxTemplateVars = xsltMaxVars; + + /* + * the profiling stack is not initialized by default + */ + cur->profTab = NULL; + cur->profNr = 0; + cur->profMax = 0; + cur->prof = 0; + + cur->style = style; + xmlXPathInit(); + cur->xpathCtxt = xmlXPathNewContext(doc); + if (cur->xpathCtxt == NULL) { + xsltTransformError(NULL, NULL, (xmlNodePtr) doc, + "xsltNewTransformContext : xmlXPathNewContext failed\n"); + goto internal_err; + } + /* + * Create an XPath cache. + */ + if (xmlXPathContextSetCache(cur->xpathCtxt, 1, -1, 0) == -1) + goto internal_err; + /* + * Initialize the extras array + */ + if (style->extrasNr != 0) { + cur->extrasMax = style->extrasNr + 20; + cur->extras = (xsltRuntimeExtraPtr) + xmlMalloc(cur->extrasMax * sizeof(xsltRuntimeExtra)); + if (cur->extras == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltNewTransformContext: out of memory\n"); + goto internal_err; + } + cur->extrasNr = style->extrasNr; + for (i = 0;i < cur->extrasMax;i++) { + cur->extras[i].info = NULL; + cur->extras[i].deallocate = NULL; + cur->extras[i].val.ptr = NULL; + } + } else { + cur->extras = NULL; + cur->extrasNr = 0; + cur->extrasMax = 0; + } + + XSLT_REGISTER_VARIABLE_LOOKUP(cur); + XSLT_REGISTER_FUNCTION_LOOKUP(cur); + cur->xpathCtxt->nsHash = style->nsHash; + /* + * Initialize the registered external modules + */ + xsltInitCtxtExts(cur); + /* + * Setup document element ordering for later efficiencies + * (bug 133289) + */ + if (xslDebugStatus == XSLT_DEBUG_NONE) + xmlXPathOrderDocElems(doc); + /* + * Must set parserOptions before calling xsltNewDocument + * (bug 164530) + */ + cur->parserOptions = XSLT_PARSE_OPTIONS; + docu = xsltNewDocument(cur, doc); + if (docu == NULL) { + xsltTransformError(cur, NULL, (xmlNodePtr)doc, + "xsltNewTransformContext : xsltNewDocument failed\n"); + goto internal_err; + } + docu->main = 1; + cur->document = docu; + cur->inst = NULL; + cur->outputFile = NULL; + cur->sec = xsltGetDefaultSecurityPrefs(); + cur->debugStatus = xslDebugStatus; + cur->traceCode = (unsigned long*) &xsltDefaultTrace; + cur->xinclude = xsltGetXIncludeDefault(); + cur->keyInitLevel = 0; + + return(cur); + +internal_err: + if (cur != NULL) + xsltFreeTransformContext(cur); + return(NULL); +} + +/** + * xsltFreeTransformContext: + * @ctxt: an XSLT parser context + * + * Free up the memory allocated by @ctxt + */ +void +xsltFreeTransformContext(xsltTransformContextPtr ctxt) { + if (ctxt == NULL) + return; + + /* + * Shutdown the extension modules associated to the stylesheet + * used if needed. + */ + xsltShutdownCtxtExts(ctxt); + + if (ctxt->xpathCtxt != NULL) { + ctxt->xpathCtxt->nsHash = NULL; + xmlXPathFreeContext(ctxt->xpathCtxt); + } + if (ctxt->templTab != NULL) + xmlFree(ctxt->templTab); + if (ctxt->varsTab != NULL) + xmlFree(ctxt->varsTab); + if (ctxt->profTab != NULL) + xmlFree(ctxt->profTab); + if ((ctxt->extrasNr > 0) && (ctxt->extras != NULL)) { + int i; + + for (i = 0;i < ctxt->extrasNr;i++) { + if ((ctxt->extras[i].deallocate != NULL) && + (ctxt->extras[i].info != NULL)) + ctxt->extras[i].deallocate(ctxt->extras[i].info); + } + xmlFree(ctxt->extras); + } + xsltFreeGlobalVariables(ctxt); + xsltFreeDocuments(ctxt); + xsltFreeCtxtExts(ctxt); + xsltFreeRVTs(ctxt); + xsltTransformCacheFree(ctxt->cache); + xmlDictFree(ctxt->dict); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "freeing transformation dictionary\n"); +#endif + memset(ctxt, -1, sizeof(xsltTransformContext)); + xmlFree(ctxt); +} + +/************************************************************************ + * * + * Copy of Nodes in an XSLT fashion * + * * + ************************************************************************/ + +xmlNodePtr xsltCopyTree(xsltTransformContextPtr ctxt, + xmlNodePtr node, xmlNodePtr insert, int literal); + +/** + * xsltAddChild: + * @parent: the parent node + * @cur: the child node + * + * Wrapper version of xmlAddChild with a more consistent behaviour on + * error. One expect the use to be child = xsltAddChild(parent, child); + * and the routine will take care of not leaking on errors or node merge + * + * Returns the child is successfully attached or NULL if merged or freed + */ +static xmlNodePtr +xsltAddChild(xmlNodePtr parent, xmlNodePtr cur) { + xmlNodePtr ret; + + if (cur == NULL) + return(NULL); + if (parent == NULL) { + xmlFreeNode(cur); + return(NULL); + } + ret = xmlAddChild(parent, cur); + + return(ret); +} + +/** + * xsltAddTextString: + * @ctxt: a XSLT process context + * @target: the text node where the text will be attached + * @string: the text string + * @len: the string length in byte + * + * Extend the current text node with the new string, it handles coalescing + * + * Returns: the text node + */ +static xmlNodePtr +xsltAddTextString(xsltTransformContextPtr ctxt, xmlNodePtr target, + const xmlChar *string, int len) { + /* + * optimization + */ + if ((len <= 0) || (string == NULL) || (target == NULL)) + return(target); + + if (ctxt->lasttext == target->content) { + + if (ctxt->lasttuse + len >= ctxt->lasttsize) { + xmlChar *newbuf; + int size; + + size = ctxt->lasttsize + len + 100; + size *= 2; + newbuf = (xmlChar *) xmlRealloc(target->content,size); + if (newbuf == NULL) { + xsltTransformError(ctxt, NULL, target, + "xsltCopyText: text allocation failed\n"); + return(NULL); + } + ctxt->lasttsize = size; + ctxt->lasttext = newbuf; + target->content = newbuf; + } + memcpy(&(target->content[ctxt->lasttuse]), string, len); + ctxt->lasttuse += len; + target->content[ctxt->lasttuse] = 0; + } else { + xmlNodeAddContent(target, string); + ctxt->lasttext = target->content; + len = xmlStrlen(target->content); + ctxt->lasttsize = len; + ctxt->lasttuse = len; + } + return(target); +} + +/** + * xsltCopyTextString: + * @ctxt: a XSLT process context + * @target: the element where the text will be attached + * @string: the text string + * @noescape: should disable-escaping be activated for this text node. + * + * Adds @string to a newly created or an existent text node child of + * @target. + * + * Returns: the text node, where the text content of @cur is copied to. + * NULL in case of API or internal errors. + */ +xmlNodePtr +xsltCopyTextString(xsltTransformContextPtr ctxt, xmlNodePtr target, + const xmlChar *string, int noescape) +{ + xmlNodePtr copy; + int len; + + if (string == NULL) + return(NULL); + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopyTextString: copy text %s\n", + string)); +#endif + + /* + * Play safe and reset the merging mechanism for every new + * target node. + */ + if ((target == NULL) || (target->children == NULL)) { + ctxt->lasttext = NULL; + } + + /* handle coalescing of text nodes here */ + len = xmlStrlen(string); + if ((ctxt->type == XSLT_OUTPUT_XML) && + (ctxt->style->cdataSection != NULL) && + (target != NULL) && + (target->type == XML_ELEMENT_NODE) && + (((target->ns == NULL) && + (xmlHashLookup2(ctxt->style->cdataSection, + target->name, NULL) != NULL)) || + ((target->ns != NULL) && + (xmlHashLookup2(ctxt->style->cdataSection, + target->name, target->ns->href) != NULL)))) + { + /* + * Process "cdata-section-elements". + */ + if ((target->last != NULL) && + (target->last->type == XML_CDATA_SECTION_NODE)) + { + return(xsltAddTextString(ctxt, target->last, string, len)); + } + copy = xmlNewCDataBlock(ctxt->output, string, len); + } else if (noescape) { + /* + * Process "disable-output-escaping". + */ + if ((target != NULL) && (target->last != NULL) && + (target->last->type == XML_TEXT_NODE) && + (target->last->name == xmlStringTextNoenc)) + { + return(xsltAddTextString(ctxt, target->last, string, len)); + } + copy = xmlNewTextLen(string, len); + if (copy != NULL) + copy->name = xmlStringTextNoenc; + } else { + /* + * Default processing. + */ + if ((target != NULL) && (target->last != NULL) && + (target->last->type == XML_TEXT_NODE) && + (target->last->name == xmlStringText)) { + return(xsltAddTextString(ctxt, target->last, string, len)); + } + copy = xmlNewTextLen(string, len); + } + if (copy != NULL && target != NULL) + copy = xsltAddChild(target, copy); + if (copy != NULL) { + ctxt->lasttext = copy->content; + ctxt->lasttsize = len; + ctxt->lasttuse = len; + } else { + xsltTransformError(ctxt, NULL, target, + "xsltCopyTextString: text copy failed\n"); + ctxt->lasttext = NULL; + } + return(copy); +} + +/** + * xsltCopyText: + * @ctxt: a XSLT process context + * @target: the element where the text will be attached + * @cur: the text or CDATA node + * @interned: the string is in the target doc dictionary + * + * Copy the text content of @cur and append it to @target's children. + * + * Returns: the text node, where the text content of @cur is copied to. + * NULL in case of API or internal errors. + */ +static xmlNodePtr +xsltCopyText(xsltTransformContextPtr ctxt, xmlNodePtr target, + xmlNodePtr cur, int interned) +{ + xmlNodePtr copy; + + if ((cur->type != XML_TEXT_NODE) && + (cur->type != XML_CDATA_SECTION_NODE)) + return(NULL); + if (cur->content == NULL) + return(NULL); + +#ifdef WITH_XSLT_DEBUG_PROCESS + if (cur->type == XML_CDATA_SECTION_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopyText: copy CDATA text %s\n", + cur->content)); + } else if (cur->name == xmlStringTextNoenc) { + XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopyText: copy unescaped text %s\n", + cur->content)); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopyText: copy text %s\n", + cur->content)); + } +#endif + + /* + * Play save and reset the merging mechanism for every new + * target node. + */ + if ((target == NULL) || (target->children == NULL)) { + ctxt->lasttext = NULL; + } + + if ((ctxt->style->cdataSection != NULL) && + (ctxt->type == XSLT_OUTPUT_XML) && + (target != NULL) && + (target->type == XML_ELEMENT_NODE) && + (((target->ns == NULL) && + (xmlHashLookup2(ctxt->style->cdataSection, + target->name, NULL) != NULL)) || + ((target->ns != NULL) && + (xmlHashLookup2(ctxt->style->cdataSection, + target->name, target->ns->href) != NULL)))) + { + /* + * Process "cdata-section-elements". + */ + /* + * OPTIMIZE TODO: xsltCopyText() is also used for attribute content. + */ + /* + * TODO: Since this doesn't merge adjacent CDATA-section nodes, + * we'll get: <![CDATA[x]]><!CDATA[y]]>. + * TODO: Reported in #321505. + */ + if ((target->last != NULL) && + (target->last->type == XML_CDATA_SECTION_NODE)) + { + /* + * Append to existing CDATA-section node. + */ + copy = xsltAddTextString(ctxt, target->last, cur->content, + xmlStrlen(cur->content)); + goto exit; + } else { + unsigned int len; + + len = xmlStrlen(cur->content); + copy = xmlNewCDataBlock(ctxt->output, cur->content, len); + if (copy == NULL) + goto exit; + ctxt->lasttext = copy->content; + ctxt->lasttsize = len; + ctxt->lasttuse = len; + } + } else if ((target != NULL) && + (target->last != NULL) && + /* both escaped or both non-escaped text-nodes */ + (((target->last->type == XML_TEXT_NODE) && + (target->last->name == cur->name)) || + /* non-escaped text nodes and CDATA-section nodes */ + (((target->last->type == XML_CDATA_SECTION_NODE) && + (cur->name == xmlStringTextNoenc))))) + { + /* + * we are appending to an existing text node + */ + copy = xsltAddTextString(ctxt, target->last, cur->content, + xmlStrlen(cur->content)); + goto exit; + } else if ((interned) && (target != NULL) && + (target->doc != NULL) && + (target->doc->dict == ctxt->dict)) + { + /* + * TODO: DO we want to use this also for "text" output? + */ + copy = xmlNewTextLen(NULL, 0); + if (copy == NULL) + goto exit; + if (cur->name == xmlStringTextNoenc) + copy->name = xmlStringTextNoenc; + + /* + * Must confirm that content is in dict (bug 302821) + * TODO: This check should be not needed for text coming + * from the stylesheets + */ + if (xmlDictOwns(ctxt->dict, cur->content)) + copy->content = cur->content; + else { + if ((copy->content = xmlStrdup(cur->content)) == NULL) + return NULL; + } + } else { + /* + * normal processing. keep counters to extend the text node + * in xsltAddTextString if needed. + */ + unsigned int len; + + len = xmlStrlen(cur->content); + copy = xmlNewTextLen(cur->content, len); + if (copy == NULL) + goto exit; + if (cur->name == xmlStringTextNoenc) + copy->name = xmlStringTextNoenc; + ctxt->lasttext = copy->content; + ctxt->lasttsize = len; + ctxt->lasttuse = len; + } + if (copy != NULL) { + if (target != NULL) { + copy->doc = target->doc; + /* + * MAYBE TODO: Maybe we should reset the ctxt->lasttext here + * to ensure that the optimized text-merging mechanism + * won't interfere with normal node-merging in any case. + */ + copy = xsltAddChild(target, copy); + } + } else { + xsltTransformError(ctxt, NULL, target, + "xsltCopyText: text copy failed\n"); + } + +exit: + if ((copy == NULL) || (copy->content == NULL)) { + xsltTransformError(ctxt, NULL, target, + "Internal error in xsltCopyText(): " + "Failed to copy the string.\n"); + ctxt->state = XSLT_STATE_STOPPED; + } + return(copy); +} + +/** + * xsltShallowCopyAttr: + * @ctxt: a XSLT process context + * @invocNode: responsible node in the stylesheet; used for error reports + * @target: the element where the attribute will be grafted + * @attr: the attribute to be copied + * + * Do a copy of an attribute. + * Called by: + * - xsltCopyTreeInternal() + * - xsltCopyOf() + * - xsltCopy() + * + * Returns: a new xmlAttrPtr, or NULL in case of error. + */ +static xmlAttrPtr +xsltShallowCopyAttr(xsltTransformContextPtr ctxt, xmlNodePtr invocNode, + xmlNodePtr target, xmlAttrPtr attr) +{ + xmlAttrPtr copy; + xmlChar *value; + + if (attr == NULL) + return(NULL); + + if (target->type != XML_ELEMENT_NODE) { + xsltTransformError(ctxt, NULL, invocNode, + "Cannot add an attribute node to a non-element node.\n"); + return(NULL); + } + + if (target->children != NULL) { + xsltTransformError(ctxt, NULL, invocNode, + "Attribute nodes must be added before " + "any child nodes to an element.\n"); + return(NULL); + } + + value = xmlNodeListGetString(attr->doc, attr->children, 1); + if (attr->ns != NULL) { + xmlNsPtr ns; + + ns = xsltGetSpecialNamespace(ctxt, invocNode, + attr->ns->href, attr->ns->prefix, target); + if (ns == NULL) { + xsltTransformError(ctxt, NULL, invocNode, + "Namespace fixup error: Failed to acquire an in-scope " + "namespace binding of the copied attribute '{%s}%s'.\n", + attr->ns->href, attr->name); + /* + * TODO: Should we just stop here? + */ + } + /* + * Note that xmlSetNsProp() will take care of duplicates + * and assigns the new namespace even to a duplicate. + */ + copy = xmlSetNsProp(target, ns, attr->name, value); + } else { + copy = xmlSetNsProp(target, NULL, attr->name, value); + } + if (value != NULL) + xmlFree(value); + + if (copy == NULL) + return(NULL); + +#if 0 + /* + * NOTE: This was optimized according to bug #342695. + * TODO: Can this further be optimized, if source and target + * share the same dict and attr->children is just 1 text node + * which is in the dict? How probable is such a case? + */ + /* + * TODO: Do we need to create an empty text node if the value + * is the empty string? + */ + value = xmlNodeListGetString(attr->doc, attr->children, 1); + if (value != NULL) { + txtNode = xmlNewDocText(target->doc, NULL); + if (txtNode == NULL) + return(NULL); + if ((target->doc != NULL) && + (target->doc->dict != NULL)) + { + txtNode->content = + (xmlChar *) xmlDictLookup(target->doc->dict, + BAD_CAST value, -1); + xmlFree(value); + } else + txtNode->content = value; + copy->children = txtNode; + } +#endif + + return(copy); +} + +/** + * xsltCopyAttrListNoOverwrite: + * @ctxt: a XSLT process context + * @invocNode: responsible node in the stylesheet; used for error reports + * @target: the element where the new attributes will be grafted + * @attr: the first attribute in the list to be copied + * + * Copies a list of attribute nodes, starting with @attr, over to the + * @target element node. + * + * Called by: + * - xsltCopyTreeInternal() + * + * Returns 0 on success and -1 on errors and internal errors. + */ +static int +xsltCopyAttrListNoOverwrite(xsltTransformContextPtr ctxt, + xmlNodePtr invocNode, + xmlNodePtr target, xmlAttrPtr attr) +{ + xmlAttrPtr copy; + xmlNsPtr origNs = NULL, copyNs = NULL; + xmlChar *value; + + /* + * Don't use xmlCopyProp() here, since it will try to + * reconciliate namespaces. + */ + while (attr != NULL) { + /* + * Find a namespace node in the tree of @target. + * Avoid searching for the same ns. + */ + if (attr->ns != origNs) { + origNs = attr->ns; + if (attr->ns != NULL) { + copyNs = xsltGetSpecialNamespace(ctxt, invocNode, + attr->ns->href, attr->ns->prefix, target); + if (copyNs == NULL) + return(-1); + } else + copyNs = NULL; + } + /* + * If attribute has a value, we need to copy it (watching out + * for possible entities) + */ + if ((attr->children) && (attr->children->type == XML_TEXT_NODE) && + (attr->children->next == NULL)) { + copy = xmlNewNsProp(target, copyNs, attr->name, + attr->children->content); + } else if (attr->children != NULL) { + value = xmlNodeListGetString(attr->doc, attr->children, 1); + copy = xmlNewNsProp(target, copyNs, attr->name, BAD_CAST value); + xmlFree(value); + } else { + copy = xmlNewNsProp(target, copyNs, attr->name, NULL); + } + + if (copy == NULL) + return(-1); + + attr = attr->next; + } + return(0); +} + +/** + * xsltShallowCopyElem: + * @ctxt: the XSLT process context + * @node: the element node in the source tree + * or the Literal Result Element + * @insert: the parent in the result tree + * @isLRE: if @node is a Literal Result Element + * + * Make a copy of the element node @node + * and insert it as last child of @insert. + * + * URGENT TODO: The problem with this one (for the non-refactored code) + * is that it is used for both, Literal Result Elements *and* + * copying input nodes. + * + * BIG NOTE: This is only called for XML_ELEMENT_NODEs. + * + * Called from: + * xsltApplySequenceConstructor() + * (for Literal Result Elements - which is a problem) + * xsltCopy() (for shallow-copying elements via xsl:copy) + * + * Returns a pointer to the new node, or NULL in case of error + */ +static xmlNodePtr +xsltShallowCopyElem(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr insert, int isLRE) +{ + xmlNodePtr copy; + + if ((node->type == XML_DTD_NODE) || (insert == NULL)) + return(NULL); + if ((node->type == XML_TEXT_NODE) || + (node->type == XML_CDATA_SECTION_NODE)) + return(xsltCopyText(ctxt, insert, node, 0)); + + copy = xmlDocCopyNode(node, insert->doc, 0); + if (copy != NULL) { + copy->doc = ctxt->output; + copy = xsltAddChild(insert, copy); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, node, + "xsltShallowCopyElem: copy failed\n"); + return (copy); + } + + if (node->type == XML_ELEMENT_NODE) { + /* + * Add namespaces as they are needed + */ + if (node->nsDef != NULL) { + /* + * TODO: Remove the LRE case in the refactored code + * gets enabled. + */ + if (isLRE) + xsltCopyNamespaceList(ctxt, copy, node->nsDef); + else + xsltCopyNamespaceListInternal(copy, node->nsDef); + } + + /* + * URGENT TODO: The problem with this is that it does not + * copy over all namespace nodes in scope. + * The damn thing about this is, that we would need to + * use the xmlGetNsList(), for every single node; this is + * also done in xsltCopyTreeInternal(), but only for the top node. + */ + if (node->ns != NULL) { + if (isLRE) { + /* + * REVISIT TODO: Since the non-refactored code still does + * ns-aliasing, we need to call xsltGetNamespace() here. + * Remove this when ready. + */ + copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy); + } else { + copy->ns = xsltGetSpecialNamespace(ctxt, + node, node->ns->href, node->ns->prefix, copy); + + } + } else if ((insert->type == XML_ELEMENT_NODE) && + (insert->ns != NULL)) + { + /* + * "Undeclare" the default namespace. + */ + xsltGetSpecialNamespace(ctxt, node, NULL, NULL, copy); + } + } + } else { + xsltTransformError(ctxt, NULL, node, + "xsltShallowCopyElem: copy %s failed\n", node->name); + } + return(copy); +} + +/** + * xsltCopyTreeList: + * @ctxt: a XSLT process context + * @invocNode: responsible node in the stylesheet; used for error reports + * @list: the list of element nodes in the source tree. + * @insert: the parent in the result tree. + * @isLRE: is this a literal result element list + * @topElemVisited: indicates if a top-most element was already processed + * + * Make a copy of the full list of tree @list + * and insert it as last children of @insert + * + * NOTE: Not to be used for Literal Result Elements. + * + * Used by: + * - xsltCopyOf() + * + * Returns a pointer to the new list, or NULL in case of error + */ +static xmlNodePtr +xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr invocNode, + xmlNodePtr list, + xmlNodePtr insert, int isLRE, int topElemVisited) +{ + xmlNodePtr copy, ret = NULL; + + while (list != NULL) { + copy = xsltCopyTreeInternal(ctxt, invocNode, + list, insert, isLRE, topElemVisited); + if (copy != NULL) { + if (ret == NULL) { + ret = copy; + } + } + list = list->next; + } + return(ret); +} + +/** + * xsltCopyNamespaceListInternal: + * @node: the target node + * @cur: the first namespace + * + * Do a copy of a namespace list. If @node is non-NULL the + * new namespaces are added automatically. + * Called by: + * xsltCopyTreeInternal() + * + * QUESTION: What is the exact difference between this function + * and xsltCopyNamespaceList() in "namespaces.c"? + * ANSWER: xsltCopyNamespaceList() tries to apply ns-aliases. + * + * Returns: a new xmlNsPtr, or NULL in case of error. + */ +static xmlNsPtr +xsltCopyNamespaceListInternal(xmlNodePtr elem, xmlNsPtr ns) { + xmlNsPtr ret = NULL; + xmlNsPtr p = NULL, q, luNs; + + if (ns == NULL) + return(NULL); + /* + * One can add namespaces only on element nodes + */ + if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE)) + elem = NULL; + + do { + if (ns->type != XML_NAMESPACE_DECL) + break; + /* + * Avoid duplicating namespace declarations on the tree. + */ + if (elem != NULL) { + if ((elem->ns != NULL) && + xmlStrEqual(elem->ns->prefix, ns->prefix) && + xmlStrEqual(elem->ns->href, ns->href)) + { + ns = ns->next; + continue; + } + luNs = xmlSearchNs(elem->doc, elem, ns->prefix); + if ((luNs != NULL) && (xmlStrEqual(luNs->href, ns->href))) + { + ns = ns->next; + continue; + } + } + q = xmlNewNs(elem, ns->href, ns->prefix); + if (p == NULL) { + ret = p = q; + } else if (q != NULL) { + p->next = q; + p = q; + } + ns = ns->next; + } while (ns != NULL); + return(ret); +} + +/** + * xsltShallowCopyNsNode: + * @ctxt: the XSLT transformation context + * @invocNode: responsible node in the stylesheet; used for error reports + * @insert: the target element node in the result tree + * @ns: the namespace node + * + * This is used for copying ns-nodes with xsl:copy-of and xsl:copy. + * + * Returns a new/existing ns-node, or NULL. + */ +static xmlNsPtr +xsltShallowCopyNsNode(xsltTransformContextPtr ctxt, + xmlNodePtr invocNode, + xmlNodePtr insert, + xmlNsPtr ns) +{ + /* + * TODO: Contrary to header comments, this is declared as int. + * be modified to return a node pointer, or NULL if any error + */ + xmlNsPtr tmpns; + + if ((insert == NULL) || (insert->type != XML_ELEMENT_NODE)) + return(NULL); + + if (insert->children != NULL) { + xsltTransformError(ctxt, NULL, invocNode, + "Namespace nodes must be added before " + "any child nodes are added to an element.\n"); + return(NULL); + } + /* + * BIG NOTE: Xalan-J simply overwrites any ns-decls with + * an equal prefix. We definitively won't do that. + * + * MSXML 4.0 and the .NET ignores ns-decls for which an + * equal prefix is already in use. + * + * Saxon raises an error like: + * "net.sf.saxon.xpath.DynamicError: Cannot create two namespace + * nodes with the same name". + * + * NOTE: We'll currently follow MSXML here. + * REVISIT TODO: Check if it's better to follow Saxon here. + */ + if (ns->prefix == NULL) { + /* + * If we are adding ns-nodes to an element using e.g. + * <xsl:copy-of select="/foo/namespace::*">, then we need + * to ensure that we don't incorrectly declare a default + * namespace on an element in no namespace, which otherwise + * would move the element incorrectly into a namespace, if + * the node tree is serialized. + */ + if (insert->ns == NULL) + goto occupied; + } else if ((ns->prefix[0] == 'x') && + xmlStrEqual(ns->prefix, BAD_CAST "xml")) + { + /* + * The XML namespace is built in. + */ + return(NULL); + } + + if (insert->nsDef != NULL) { + tmpns = insert->nsDef; + do { + if ((tmpns->prefix == NULL) == (ns->prefix == NULL)) { + if ((tmpns->prefix == ns->prefix) || + xmlStrEqual(tmpns->prefix, ns->prefix)) + { + /* + * Same prefix. + */ + if (xmlStrEqual(tmpns->href, ns->href)) + return(NULL); + goto occupied; + } + } + tmpns = tmpns->next; + } while (tmpns != NULL); + } + tmpns = xmlSearchNs(insert->doc, insert, ns->prefix); + if ((tmpns != NULL) && xmlStrEqual(tmpns->href, ns->href)) + return(NULL); + /* + * Declare a new namespace. + * TODO: The problem (wrt efficiency) with this xmlNewNs() is + * that it will again search the already declared namespaces + * for a duplicate :-/ + */ + return(xmlNewNs(insert, ns->href, ns->prefix)); + +occupied: + /* + * TODO: We could as well raise an error here (like Saxon does), + * or at least generate a warning. + */ + return(NULL); +} + +/** + * xsltCopyTreeInternal: + * @ctxt: the XSLT transformation context + * @invocNode: responsible node in the stylesheet; used for error reports + * @node: the element node in the source tree + * @insert: the parent in the result tree + * @isLRE: indicates if @node is a Literal Result Element + * @topElemVisited: indicates if a top-most element was already processed + * + * Make a copy of the full tree under the element node @node + * and insert it as last child of @insert + * + * NOTE: Not to be used for Literal Result Elements. + * + * Used by: + * - xsltCopyOf() + * + * Returns a pointer to the new tree, or NULL in case of error + */ +static xmlNodePtr +xsltCopyTreeInternal(xsltTransformContextPtr ctxt, + xmlNodePtr invocNode, + xmlNodePtr node, + xmlNodePtr insert, int isLRE, int topElemVisited) +{ + xmlNodePtr copy; + + if (node == NULL) + return(NULL); + switch (node->type) { + case XML_ELEMENT_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + break; + case XML_TEXT_NODE: { + int noenc = (node->name == xmlStringTextNoenc); + return(xsltCopyTextString(ctxt, insert, node->content, noenc)); + } + case XML_CDATA_SECTION_NODE: + return(xsltCopyTextString(ctxt, insert, node->content, 0)); + case XML_ATTRIBUTE_NODE: + return((xmlNodePtr) + xsltShallowCopyAttr(ctxt, invocNode, insert, (xmlAttrPtr) node)); + case XML_NAMESPACE_DECL: + return((xmlNodePtr) xsltShallowCopyNsNode(ctxt, invocNode, + insert, (xmlNsPtr) node)); + + case XML_DOCUMENT_TYPE_NODE: + case XML_DOCUMENT_FRAG_NODE: + case XML_NOTATION_NODE: + case XML_DTD_NODE: + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + case XML_ENTITY_DECL: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + return(NULL); + } + if (XSLT_IS_RES_TREE_FRAG(node)) { + if (node->children != NULL) + copy = xsltCopyTreeList(ctxt, invocNode, + node->children, insert, 0, 0); + else + copy = NULL; + return(copy); + } + copy = xmlDocCopyNode(node, insert->doc, 0); + if (copy != NULL) { + copy->doc = ctxt->output; + copy = xsltAddChild(insert, copy); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, invocNode, + "xsltCopyTreeInternal: Copying of '%s' failed.\n", node->name); + return (copy); + } + /* + * The node may have been coalesced into another text node. + */ + if (insert->last != copy) + return(insert->last); + copy->next = NULL; + + if (node->type == XML_ELEMENT_NODE) { + /* + * Copy in-scope namespace nodes. + * + * REVISIT: Since we try to reuse existing in-scope ns-decls by + * using xmlSearchNsByHref(), this will eventually change + * the prefix of an original ns-binding; thus it might + * break QNames in element/attribute content. + * OPTIMIZE TODO: If we had a xmlNsPtr * on the transformation + * context, plus a ns-lookup function, which writes directly + * to a given list, then we wouldn't need to create/free the + * nsList every time. + */ + if ((topElemVisited == 0) && + (node->parent != NULL) && + (node->parent->type != XML_DOCUMENT_NODE) && + (node->parent->type != XML_HTML_DOCUMENT_NODE)) + { + xmlNsPtr *nsList, *curns, ns; + + /* + * If this is a top-most element in a tree to be + * copied, then we need to ensure that all in-scope + * namespaces are copied over. For nodes deeper in the + * tree, it is sufficient to reconcile only the ns-decls + * (node->nsDef entries). + */ + + nsList = xmlGetNsList(node->doc, node); + if (nsList != NULL) { + curns = nsList; + do { + /* + * Search by prefix first in order to break as less + * QNames in element/attribute content as possible. + */ + ns = xmlSearchNs(insert->doc, insert, + (*curns)->prefix); + + if ((ns == NULL) || + (! xmlStrEqual(ns->href, (*curns)->href))) + { + ns = NULL; + /* + * Search by namespace name. + * REVISIT TODO: Currently disabled. + */ +#if 0 + ns = xmlSearchNsByHref(insert->doc, + insert, (*curns)->href); +#endif + } + if (ns == NULL) { + /* + * Declare a new namespace on the copied element. + */ + ns = xmlNewNs(copy, (*curns)->href, + (*curns)->prefix); + /* TODO: Handle errors */ + } + if (node->ns == *curns) { + /* + * If this was the original's namespace then set + * the generated counterpart on the copy. + */ + copy->ns = ns; + } + curns++; + } while (*curns != NULL); + xmlFree(nsList); + } + } else if (node->nsDef != NULL) { + /* + * Copy over all namespace declaration attributes. + */ + if (node->nsDef != NULL) { + if (isLRE) + xsltCopyNamespaceList(ctxt, copy, node->nsDef); + else + xsltCopyNamespaceListInternal(copy, node->nsDef); + } + } + /* + * Set the namespace. + */ + if (node->ns != NULL) { + if (copy->ns == NULL) { + /* + * This will map copy->ns to one of the newly created + * in-scope ns-decls, OR create a new ns-decl on @copy. + */ + copy->ns = xsltGetSpecialNamespace(ctxt, invocNode, + node->ns->href, node->ns->prefix, copy); + } + } else if ((insert->type == XML_ELEMENT_NODE) && + (insert->ns != NULL)) + { + /* + * "Undeclare" the default namespace on @copy with xmlns="". + */ + xsltGetSpecialNamespace(ctxt, invocNode, NULL, NULL, copy); + } + /* + * Copy attribute nodes. + */ + if (node->properties != NULL) { + xsltCopyAttrListNoOverwrite(ctxt, invocNode, + copy, node->properties); + } + if (topElemVisited == 0) + topElemVisited = 1; + } + /* + * Copy the subtree. + */ + if (node->children != NULL) { + xsltCopyTreeList(ctxt, invocNode, + node->children, copy, isLRE, topElemVisited); + } + } else { + xsltTransformError(ctxt, NULL, invocNode, + "xsltCopyTreeInternal: Copying of '%s' failed.\n", node->name); + } + return(copy); +} + +/** + * xsltCopyTree: + * @ctxt: the XSLT transformation context + * @node: the element node in the source tree + * @insert: the parent in the result tree + * @literal: indicates if @node is a Literal Result Element + * + * Make a copy of the full tree under the element node @node + * and insert it as last child of @insert + * For literal result element, some of the namespaces may not be copied + * over according to section 7.1. + * TODO: Why is this a public function? + * + * Returns a pointer to the new tree, or NULL in case of error + */ +xmlNodePtr +xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr insert, int literal) +{ + return(xsltCopyTreeInternal(ctxt, node, node, insert, literal, 0)); + +} + +/************************************************************************ + * * + * Error/fallback processing * + * * + ************************************************************************/ + +/** + * xsltApplyFallbacks: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the node generating the error + * + * Process possible xsl:fallback nodes present under @inst + * + * Returns the number of xsl:fallback element found and processed + */ +static int +xsltApplyFallbacks(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst) { + + xmlNodePtr child; + int ret = 0; + + if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || + (inst->children == NULL)) + return(0); + + child = inst->children; + while (child != NULL) { + if ((IS_XSLT_ELEM(child)) && + (xmlStrEqual(child->name, BAD_CAST "fallback"))) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "applying xsl:fallback\n"); +#endif + ret++; + xsltApplySequenceConstructor(ctxt, node, child->children, + NULL); + } + child = child->next; + } + return(ret); +} + +/************************************************************************ + * * + * Default processing * + * * + ************************************************************************/ + +/** + * xsltDefaultProcessOneNode: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @params: extra parameters passed to the template if any + * + * Process the source node with the default built-in template rule: + * <xsl:template match="*|/"> + * <xsl:apply-templates/> + * </xsl:template> + * + * and + * + * <xsl:template match="text()|@*"> + * <xsl:value-of select="."/> + * </xsl:template> + * + * Note also that namespace declarations are copied directly: + * + * the built-in template rule is the only template rule that is applied + * for namespace nodes. + */ +static void +xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node, + xsltStackElemPtr params) { + xmlNodePtr copy; + xmlNodePtr delete = NULL, cur; + int nbchild = 0, oldSize; + int childno = 0, oldPos; + xsltTemplatePtr template; + + CHECK_STOPPED; + /* + * Handling of leaves + */ + switch (node->type) { + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_ELEMENT_NODE: + break; + case XML_CDATA_SECTION_NODE: +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: copy CDATA %s\n", + node->content)); +#endif + copy = xsltCopyText(ctxt, ctxt->insert, node, 0); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, node, + "xsltDefaultProcessOneNode: cdata copy failed\n"); + } + return; + case XML_TEXT_NODE: +#ifdef WITH_XSLT_DEBUG_PROCESS + if (node->content == NULL) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: copy empty text\n")); + return; + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: copy text %s\n", + node->content)); + } +#endif + copy = xsltCopyText(ctxt, ctxt->insert, node, 0); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, node, + "xsltDefaultProcessOneNode: text copy failed\n"); + } + return; + case XML_ATTRIBUTE_NODE: + cur = node->children; + while ((cur != NULL) && (cur->type != XML_TEXT_NODE)) + cur = cur->next; + if (cur == NULL) { + xsltTransformError(ctxt, NULL, node, + "xsltDefaultProcessOneNode: no text for attribute\n"); + } else { +#ifdef WITH_XSLT_DEBUG_PROCESS + if (cur->content == NULL) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: copy empty text\n")); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: copy text %s\n", + cur->content)); + } +#endif + copy = xsltCopyText(ctxt, ctxt->insert, cur, 0); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, node, + "xsltDefaultProcessOneNode: text copy failed\n"); + } + } + return; + default: + return; + } + /* + * Handling of Elements: first pass, cleanup and counting + */ + cur = node->children; + while (cur != NULL) { + switch (cur->type) { + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_ELEMENT_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + nbchild++; + break; + case XML_DTD_NODE: + /* Unlink the DTD, it's still reachable using doc->intSubset */ + if (cur->next != NULL) + cur->next->prev = cur->prev; + if (cur->prev != NULL) + cur->prev->next = cur->next; + break; + default: +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: skipping node type %d\n", + cur->type)); +#endif + delete = cur; + } + cur = cur->next; + if (delete != NULL) { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: removing ignorable blank node\n")); +#endif + xmlUnlinkNode(delete); + xmlFreeNode(delete); + delete = NULL; + } + } + if (delete != NULL) { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: removing ignorable blank node\n")); +#endif + xmlUnlinkNode(delete); + xmlFreeNode(delete); + delete = NULL; + } + + /* + * Handling of Elements: second pass, actual processing + * + * Note that params are passed to the next template. This matches + * XSLT 2.0 behavior but doesn't conform to XSLT 1.0. + */ + oldSize = ctxt->xpathCtxt->contextSize; + oldPos = ctxt->xpathCtxt->proximityPosition; + cur = node->children; + while (cur != NULL) { + childno++; + switch (cur->type) { + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_ELEMENT_NODE: + ctxt->xpathCtxt->contextSize = nbchild; + ctxt->xpathCtxt->proximityPosition = childno; + xsltProcessOneNode(ctxt, cur, params); + break; + case XML_CDATA_SECTION_NODE: + template = xsltGetTemplate(ctxt, cur, NULL); + if (template) { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: applying template for CDATA %s\n", + cur->content)); +#endif + /* + * Instantiate the xsl:template. + */ + xsltApplyXSLTTemplate(ctxt, cur, template->content, + template, params); + } else /* if (ctxt->mode == NULL) */ { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: copy CDATA %s\n", + cur->content)); +#endif + copy = xsltCopyText(ctxt, ctxt->insert, cur, 0); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, cur, + "xsltDefaultProcessOneNode: cdata copy failed\n"); + } + } + break; + case XML_TEXT_NODE: + template = xsltGetTemplate(ctxt, cur, NULL); + if (template) { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: applying template for text %s\n", + cur->content)); +#endif + ctxt->xpathCtxt->contextSize = nbchild; + ctxt->xpathCtxt->proximityPosition = childno; + /* + * Instantiate the xsl:template. + */ + xsltApplyXSLTTemplate(ctxt, cur, template->content, + template, params); + } else /* if (ctxt->mode == NULL) */ { +#ifdef WITH_XSLT_DEBUG_PROCESS + if (cur->content == NULL) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: copy empty text\n")); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: copy text %s\n", + cur->content)); + } +#endif + copy = xsltCopyText(ctxt, ctxt->insert, cur, 0); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, cur, + "xsltDefaultProcessOneNode: text copy failed\n"); + } + } + break; + case XML_PI_NODE: + case XML_COMMENT_NODE: + template = xsltGetTemplate(ctxt, cur, NULL); + if (template) { +#ifdef WITH_XSLT_DEBUG_PROCESS + if (cur->type == XML_PI_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: template found for PI %s\n", + cur->name)); + } else if (cur->type == XML_COMMENT_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltDefaultProcessOneNode: template found for comment\n")); + } +#endif + ctxt->xpathCtxt->contextSize = nbchild; + ctxt->xpathCtxt->proximityPosition = childno; + /* + * Instantiate the xsl:template. + */ + xsltApplyXSLTTemplate(ctxt, cur, template->content, + template, params); + } + break; + default: + break; + } + cur = cur->next; + } + ctxt->xpathCtxt->contextSize = oldSize; + ctxt->xpathCtxt->proximityPosition = oldPos; +} + +/** + * xsltProcessOneNode: + * @ctxt: a XSLT process context + * @contextNode: the "current node" in the source tree + * @withParams: extra parameters (e.g. xsl:with-param) passed to the + * template if any + * + * Process the source node. + */ +void +xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, + xsltStackElemPtr withParams) +{ + xsltTemplatePtr templ; + xmlNodePtr oldNode; + + templ = xsltGetTemplate(ctxt, contextNode, NULL); + /* + * If no template is found, apply the default rule. + */ + if (templ == NULL) { +#ifdef WITH_XSLT_DEBUG_PROCESS + if (contextNode->type == XML_DOCUMENT_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessOneNode: no template found for /\n")); + } else if (contextNode->type == XML_CDATA_SECTION_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessOneNode: no template found for CDATA\n")); + } else if (contextNode->type == XML_ATTRIBUTE_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessOneNode: no template found for attribute %s\n", + ((xmlAttrPtr) contextNode)->name)); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessOneNode: no template found for %s\n", contextNode->name)); + } +#endif + oldNode = ctxt->node; + ctxt->node = contextNode; + xsltDefaultProcessOneNode(ctxt, contextNode, withParams); + ctxt->node = oldNode; + return; + } + + if (contextNode->type == XML_ATTRIBUTE_NODE) { + xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule; + /* + * Set the "current template rule". + */ + ctxt->currentTemplateRule = templ; + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessOneNode: applying template '%s' for attribute %s\n", + templ->match, contextNode->name)); +#endif + xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams); + + ctxt->currentTemplateRule = oldCurTempRule; + } else { + xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule; + /* + * Set the "current template rule". + */ + ctxt->currentTemplateRule = templ; + +#ifdef WITH_XSLT_DEBUG_PROCESS + if (contextNode->type == XML_DOCUMENT_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessOneNode: applying template '%s' for /\n", + templ->match)); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessOneNode: applying template '%s' for %s\n", + templ->match, contextNode->name)); + } +#endif + xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams); + + ctxt->currentTemplateRule = oldCurTempRule; + } +} + +static xmlNodePtr +xsltDebuggerStartSequenceConstructor(xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, + xmlNodePtr list, + xsltTemplatePtr templ, + int *addCallResult) +{ + xmlNodePtr debugedNode = NULL; + + if (ctxt->debugStatus != XSLT_DEBUG_NONE) { + if (templ) { + *addCallResult = xslAddCall(templ, templ->elem); + } else { + *addCallResult = xslAddCall(NULL, list); + } + switch (ctxt->debugStatus) { + case XSLT_DEBUG_RUN_RESTART: + case XSLT_DEBUG_QUIT: + if (*addCallResult) + xslDropCall(); + return(NULL); + } + if (templ) { + xslHandleDebugger(templ->elem, contextNode, templ, ctxt); + debugedNode = templ->elem; + } else if (list) { + xslHandleDebugger(list, contextNode, templ, ctxt); + debugedNode = list; + } else if (ctxt->inst) { + xslHandleDebugger(ctxt->inst, contextNode, templ, ctxt); + debugedNode = ctxt->inst; + } + } + return(debugedNode); +} + +/** + * xsltLocalVariablePush: + * @ctxt: the transformation context + * @variable: variable to be pushed to the variable stack + * @level: new value for variable's level + * + * Places the variable onto the local variable stack + * + * Returns: 0 for success, -1 for any error + * **NOTE:** + * This is an internal routine and should not be called by users! + */ +int +xsltLocalVariablePush(xsltTransformContextPtr ctxt, + xsltStackElemPtr variable, + int level) +{ + if (ctxt->varsMax == 0) { + ctxt->varsMax = 10; + ctxt->varsTab = + (xsltStackElemPtr *) xmlMalloc(ctxt->varsMax * + sizeof(ctxt->varsTab[0])); + if (ctxt->varsTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); + return (-1); + } + } + if (ctxt->varsNr >= ctxt->varsMax) { + ctxt->varsMax *= 2; + ctxt->varsTab = + (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab, + ctxt->varsMax * + sizeof(ctxt->varsTab[0])); + if (ctxt->varsTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); + return (-1); + } + } + ctxt->varsTab[ctxt->varsNr++] = variable; + ctxt->vars = variable; + variable->level = level; + return(0); +} + +/** + * xsltReleaseLocalRVTs: + * + * Fragments which are results of extension instructions + * are preserved; all other fragments are freed/cached. + */ +static void +xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt, xmlDocPtr base) +{ + xmlDocPtr cur = ctxt->localRVT, tmp; + + if (cur == base) + return; + if (cur->prev != NULL) + xsltTransformError(ctxt, NULL, NULL, "localRVT not head of list\n"); + + do { + tmp = cur; + cur = (xmlDocPtr) cur->next; + if (tmp->psvi == XSLT_RVT_LOCAL) { + xsltReleaseRVT(ctxt, tmp); + } else if (tmp->psvi == XSLT_RVT_GLOBAL) { + xsltRegisterPersistRVT(ctxt, tmp); + } else if (tmp->psvi != XSLT_RVT_FUNC_RESULT) { + xmlGenericError(xmlGenericErrorContext, + "xsltReleaseLocalRVTs: Unexpected RVT flag %p\n", + tmp->psvi); + } + } while (cur != base); + + if (base != NULL) + base->prev = NULL; + ctxt->localRVT = base; +} + +/** + * xsltApplySequenceConstructor: + * @ctxt: a XSLT process context + * @contextNode: the "current node" in the source tree + * @list: the nodes of a sequence constructor; + * (plus leading xsl:param elements) + * @templ: the compiled xsl:template (optional) + * + * Processes a sequence constructor. + * + * NOTE: ctxt->currentTemplateRule was introduced to reflect the + * semantics of "current template rule". I.e. the field ctxt->templ + * is not intended to reflect this, thus always pushed onto the + * template stack. + */ +static void +xsltApplySequenceConstructor(xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, xmlNodePtr list, + xsltTemplatePtr templ) +{ + xmlNodePtr oldInsert, oldInst, oldCurInst, oldContextNode; + xmlNodePtr cur, insert, copy = NULL; + int level = 0, oldVarsNr; + xmlDocPtr oldLocalFragmentTop; + +#ifdef XSLT_REFACTORED + xsltStylePreCompPtr info; +#endif + +#ifdef WITH_DEBUGGER + int addCallResult = 0; + xmlNodePtr debuggedNode = NULL; +#endif + + if (ctxt == NULL) + return; + +#ifdef WITH_DEBUGGER + if (ctxt->debugStatus != XSLT_DEBUG_NONE) { + debuggedNode = + xsltDebuggerStartSequenceConstructor(ctxt, contextNode, + list, templ, &addCallResult); + if (debuggedNode == NULL) + return; + } +#endif + + if (list == NULL) + return; + CHECK_STOPPED; + + /* + * Check for infinite recursion: stop if the maximum of nested templates + * is excceeded. Adjust xsltMaxDepth if you need more. + */ + if (ctxt->depth >= ctxt->maxTemplateDepth) { + xsltTransformError(ctxt, NULL, list, + "xsltApplySequenceConstructor: A potential infinite template " + "recursion was detected.\n" + "You can adjust xsltMaxDepth (--maxdepth) in order to " + "raise the maximum number of nested template calls and " + "variables/params (currently set to %d).\n", + ctxt->maxTemplateDepth); + xsltDebug(ctxt, contextNode, list, NULL); + ctxt->state = XSLT_STATE_STOPPED; + return; + } + ctxt->depth++; + + oldLocalFragmentTop = ctxt->localRVT; + oldInsert = insert = ctxt->insert; + oldInst = oldCurInst = ctxt->inst; + oldContextNode = ctxt->node; + /* + * Save current number of variables on the stack; new vars are popped when + * exiting. + */ + oldVarsNr = ctxt->varsNr; + /* + * Process the sequence constructor. + */ + cur = list; + while (cur != NULL) { + ctxt->inst = cur; + +#ifdef WITH_DEBUGGER + switch (ctxt->debugStatus) { + case XSLT_DEBUG_RUN_RESTART: + case XSLT_DEBUG_QUIT: + break; + + } +#endif + /* + * Test; we must have a valid insertion point. + */ + if (insert == NULL) { + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: insert == NULL !\n")); +#endif + goto error; + } + +#ifdef WITH_DEBUGGER + if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (debuggedNode != cur)) + xslHandleDebugger(cur, contextNode, templ, ctxt); +#endif + +#ifdef XSLT_REFACTORED + if (cur->type == XML_ELEMENT_NODE) { + info = (xsltStylePreCompPtr) cur->psvi; + /* + * We expect a compiled representation on: + * 1) XSLT instructions of this XSLT version (1.0) + * (with a few exceptions) + * 2) Literal result elements + * 3) Extension instructions + * 4) XSLT instructions of future XSLT versions + * (forwards-compatible mode). + */ + if (info == NULL) { + /* + * Handle the rare cases where we don't expect a compiled + * representation on an XSLT element. + */ + if (IS_XSLT_ELEM_FAST(cur) && IS_XSLT_NAME(cur, "message")) { + xsltMessage(ctxt, contextNode, cur); + goto skip_children; + } + /* + * Something really went wrong: + */ + xsltTransformError(ctxt, NULL, cur, + "Internal error in xsltApplySequenceConstructor(): " + "The element '%s' in the stylesheet has no compiled " + "representation.\n", + cur->name); + goto skip_children; + } + + if (info->type == XSLT_FUNC_LITERAL_RESULT_ELEMENT) { + xsltStyleItemLRElementInfoPtr lrInfo = + (xsltStyleItemLRElementInfoPtr) info; + /* + * Literal result elements + * -------------------------------------------------------- + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, + xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: copy literal result " + "element '%s'\n", cur->name)); +#endif + /* + * Copy the raw element-node. + * OLD: if ((copy = xsltShallowCopyElem(ctxt, cur, insert)) + * == NULL) + * goto error; + */ + copy = xmlDocCopyNode(cur, insert->doc, 0); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, cur, + "Internal error in xsltApplySequenceConstructor(): " + "Failed to copy literal result element '%s'.\n", + cur->name); + goto error; + } else { + /* + * Add the element-node to the result tree. + */ + copy->doc = ctxt->output; + copy = xsltAddChild(insert, copy); + /* + * Create effective namespaces declarations. + * OLD: xsltCopyNamespaceList(ctxt, copy, cur->nsDef); + */ + if (lrInfo->effectiveNs != NULL) { + xsltEffectiveNsPtr effNs = lrInfo->effectiveNs; + xmlNsPtr ns, lastns = NULL; + + while (effNs != NULL) { + /* + * Avoid generating redundant namespace + * declarations; thus lookup if there is already + * such a ns-decl in the result. + */ + ns = xmlSearchNs(copy->doc, copy, effNs->prefix); + if ((ns != NULL) && + (xmlStrEqual(ns->href, effNs->nsName))) + { + effNs = effNs->next; + continue; + } + ns = xmlNewNs(copy, effNs->nsName, effNs->prefix); + if (ns == NULL) { + xsltTransformError(ctxt, NULL, cur, + "Internal error in " + "xsltApplySequenceConstructor(): " + "Failed to copy a namespace " + "declaration.\n"); + goto error; + } + + if (lastns == NULL) + copy->nsDef = ns; + else + lastns->next =ns; + lastns = ns; + + effNs = effNs->next; + } + + } + /* + * NOTE that we don't need to apply ns-alising: this was + * already done at compile-time. + */ + if (cur->ns != NULL) { + /* + * If there's no such ns-decl in the result tree, + * then xsltGetSpecialNamespace() will + * create a ns-decl on the copied node. + */ + copy->ns = xsltGetSpecialNamespace(ctxt, cur, + cur->ns->href, cur->ns->prefix, copy); + } else { + /* + * Undeclare the default namespace if needed. + * This can be skipped, if the result element has + * no ns-decls, in which case the result element + * obviously does not declare a default namespace; + * AND there's either no parent, or the parent + * element is in no namespace; this means there's no + * default namespace is scope to care about. + * + * REVISIT: This might result in massive + * generation of ns-decls if nodes in a default + * namespaces are mixed with nodes in no namespace. + * + */ + if (copy->nsDef || + ((insert != NULL) && + (insert->type == XML_ELEMENT_NODE) && + (insert->ns != NULL))) + { + xsltGetSpecialNamespace(ctxt, cur, + NULL, NULL, copy); + } + } + } + /* + * SPEC XSLT 2.0 "Each attribute of the literal result + * element, other than an attribute in the XSLT namespace, + * is processed to produce an attribute for the element in + * the result tree." + * NOTE: See bug #341325. + */ + if (cur->properties != NULL) { + xsltAttrListTemplateProcess(ctxt, copy, cur->properties); + } + } else if (IS_XSLT_ELEM_FAST(cur)) { + /* + * XSLT instructions + * -------------------------------------------------------- + */ + if (info->type == XSLT_FUNC_UNKOWN_FORWARDS_COMPAT) { + /* + * We hit an unknown XSLT element. + * Try to apply one of the fallback cases. + */ + ctxt->insert = insert; + if (!xsltApplyFallbacks(ctxt, contextNode, cur)) { + xsltTransformError(ctxt, NULL, cur, + "The is no fallback behaviour defined for " + "the unknown XSLT element '%s'.\n", + cur->name); + } + ctxt->insert = oldInsert; + } else if (info->func != NULL) { + /* + * Execute the XSLT instruction. + */ + ctxt->insert = insert; + + info->func(ctxt, contextNode, cur, + (xsltElemPreCompPtr) info); + + /* + * Cleanup temporary tree fragments. + */ + if (oldLocalFragmentTop != ctxt->localRVT) + xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); + + ctxt->insert = oldInsert; + } else if (info->type == XSLT_FUNC_VARIABLE) { + xsltStackElemPtr tmpvar = ctxt->vars; + + xsltParseStylesheetVariable(ctxt, cur); + + if (tmpvar != ctxt->vars) { + /* + * TODO: Using a @tmpvar is an annoying workaround, but + * the current mechanisms do not provide any other way + * of knowing if the var was really pushed onto the + * stack. + */ + ctxt->vars->level = level; + } + } else if (info->type == XSLT_FUNC_MESSAGE) { + /* + * TODO: Won't be hit, since we don't compile xsl:message. + */ + xsltMessage(ctxt, contextNode, cur); + } else { + xsltTransformError(ctxt, NULL, cur, + "Unexpected XSLT element '%s'.\n", cur->name); + } + goto skip_children; + + } else { + xsltTransformFunction func; + /* + * Extension intructions (elements) + * -------------------------------------------------------- + */ + if (cur->psvi == xsltExtMarker) { + /* + * The xsltExtMarker was set during the compilation + * of extension instructions if there was no registered + * handler for this specific extension function at + * compile-time. + * Libxslt will now lookup if a handler is + * registered in the context of this transformation. + */ + func = (xsltTransformFunction) + xsltExtElementLookup(ctxt, cur->name, cur->ns->href); + } else + func = ((xsltElemPreCompPtr) cur->psvi)->func; + + if (func == NULL) { + /* + * No handler available. + * Try to execute fallback behaviour via xsl:fallback. + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, + xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: unknown extension %s\n", + cur->name)); +#endif + ctxt->insert = insert; + if (!xsltApplyFallbacks(ctxt, contextNode, cur)) { + xsltTransformError(ctxt, NULL, cur, + "Unknown extension instruction '{%s}%s'.\n", + cur->ns->href, cur->name); + } + ctxt->insert = oldInsert; + } else { + /* + * Execute the handler-callback. + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: extension construct %s\n", + cur->name)); +#endif + ctxt->insert = insert; + + func(ctxt, contextNode, cur, cur->psvi); + + /* + * Cleanup temporary tree fragments. + */ + if (oldLocalFragmentTop != ctxt->localRVT) + xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); + + ctxt->insert = oldInsert; + } + goto skip_children; + } + + } else if (XSLT_IS_TEXT_NODE(cur)) { + /* + * Text + * ------------------------------------------------------------ + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + if (cur->name == xmlStringTextNoenc) { + XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, + xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: copy unescaped text '%s'\n", + cur->content)); + } else { + XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE, + xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: copy text '%s'\n", + cur->content)); + } +#endif + if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL) + goto error; + } + +#else /* XSLT_REFACTORED */ + + if (IS_XSLT_ELEM(cur)) { + /* + * This is an XSLT node + */ + xsltStylePreCompPtr info = (xsltStylePreCompPtr) cur->psvi; + + if (info == NULL) { + if (IS_XSLT_NAME(cur, "message")) { + xsltMessage(ctxt, contextNode, cur); + } else { + /* + * That's an error try to apply one of the fallback cases + */ + ctxt->insert = insert; + if (!xsltApplyFallbacks(ctxt, contextNode, cur)) { + xsltGenericError(xsltGenericErrorContext, + "xsltApplySequenceConstructor: %s was not compiled\n", + cur->name); + } + ctxt->insert = oldInsert; + } + goto skip_children; + } + + if (info->func != NULL) { + oldCurInst = ctxt->inst; + ctxt->inst = cur; + ctxt->insert = insert; + + info->func(ctxt, contextNode, cur, (xsltElemPreCompPtr) info); + + /* + * Cleanup temporary tree fragments. + */ + if (oldLocalFragmentTop != ctxt->localRVT) + xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); + + ctxt->insert = oldInsert; + ctxt->inst = oldCurInst; + goto skip_children; + } + + if (IS_XSLT_NAME(cur, "variable")) { + xsltStackElemPtr tmpvar = ctxt->vars; + + oldCurInst = ctxt->inst; + ctxt->inst = cur; + + xsltParseStylesheetVariable(ctxt, cur); + + ctxt->inst = oldCurInst; + + if (tmpvar != ctxt->vars) { + /* + * TODO: Using a @tmpvar is an annoying workaround, but + * the current mechanisms do not provide any other way + * of knowing if the var was really pushed onto the + * stack. + */ + ctxt->vars->level = level; + } + } else if (IS_XSLT_NAME(cur, "message")) { + xsltMessage(ctxt, contextNode, cur); + } else { + xsltTransformError(ctxt, NULL, cur, + "Unexpected XSLT element '%s'.\n", cur->name); + } + goto skip_children; + } else if ((cur->type == XML_TEXT_NODE) || + (cur->type == XML_CDATA_SECTION_NODE)) { + + /* + * This text comes from the stylesheet + * For stylesheets, the set of whitespace-preserving + * element names consists of just xsl:text. + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + if (cur->type == XML_CDATA_SECTION_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: copy CDATA text %s\n", + cur->content)); + } else if (cur->name == xmlStringTextNoenc) { + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: copy unescaped text %s\n", + cur->content)); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: copy text %s\n", + cur->content)); + } +#endif + if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL) + goto error; + } else if ((cur->type == XML_ELEMENT_NODE) && + (cur->ns != NULL) && (cur->psvi != NULL)) { + xsltTransformFunction function; + + oldCurInst = ctxt->inst; + ctxt->inst = cur; + /* + * Flagged as an extension element + */ + if (cur->psvi == xsltExtMarker) + function = (xsltTransformFunction) + xsltExtElementLookup(ctxt, cur->name, cur->ns->href); + else + function = ((xsltElemPreCompPtr) cur->psvi)->func; + + if (function == NULL) { + xmlNodePtr child; + int found = 0; + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: unknown extension %s\n", + cur->name)); +#endif + /* + * Search if there are fallbacks + */ + child = cur->children; + while (child != NULL) { + if ((IS_XSLT_ELEM(child)) && + (IS_XSLT_NAME(child, "fallback"))) + { + found = 1; + xsltApplySequenceConstructor(ctxt, contextNode, + child->children, NULL); + } + child = child->next; + } + + if (!found) { + xsltTransformError(ctxt, NULL, cur, + "xsltApplySequenceConstructor: failed to find extension %s\n", + cur->name); + } + } else { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: extension construct %s\n", + cur->name)); +#endif + + ctxt->insert = insert; + + function(ctxt, contextNode, cur, cur->psvi); + /* + * Cleanup temporary tree fragments. + */ + if (oldLocalFragmentTop != ctxt->localRVT) + xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); + + ctxt->insert = oldInsert; + + } + ctxt->inst = oldCurInst; + goto skip_children; + } else if (cur->type == XML_ELEMENT_NODE) { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplySequenceConstructor: copy node %s\n", + cur->name)); +#endif + oldCurInst = ctxt->inst; + ctxt->inst = cur; + + if ((copy = xsltShallowCopyElem(ctxt, cur, insert, 1)) == NULL) + goto error; + /* + * Add extra namespaces inherited from the current template + * if we are in the first level children and this is a + * "real" template. + */ + if ((templ != NULL) && (oldInsert == insert) && + (ctxt->templ != NULL) && (ctxt->templ->inheritedNs != NULL)) { + int i; + xmlNsPtr ns, ret; + + for (i = 0; i < ctxt->templ->inheritedNsNr; i++) { + const xmlChar *URI = NULL; + xsltStylesheetPtr style; + ns = ctxt->templ->inheritedNs[i]; + + /* Note that the XSLT namespace was already excluded + * in xsltGetInheritedNsList(). + */ +#if 0 + if (xmlStrEqual(ns->href, XSLT_NAMESPACE)) + continue; +#endif + style = ctxt->style; + while (style != NULL) { + if (style->nsAliases != NULL) + URI = (const xmlChar *) + xmlHashLookup(style->nsAliases, ns->href); + if (URI != NULL) + break; + + style = xsltNextImport(style); + } + if (URI == UNDEFINED_DEFAULT_NS) + continue; + if (URI == NULL) + URI = ns->href; + /* + * TODO: The following will still be buggy for the + * non-refactored code. + */ + ret = xmlSearchNs(copy->doc, copy, ns->prefix); + if ((ret == NULL) || (!xmlStrEqual(ret->href, URI))) + { + xmlNewNs(copy, URI, ns->prefix); + } + } + if (copy->ns != NULL) { + /* + * Fix the node namespace if needed + */ + copy->ns = xsltGetNamespace(ctxt, cur, copy->ns, copy); + } + } + /* + * all the attributes are directly inherited + */ + if (cur->properties != NULL) { + xsltAttrListTemplateProcess(ctxt, copy, cur->properties); + } + ctxt->inst = oldCurInst; + } +#endif /* else of XSLT_REFACTORED */ + + /* + * Descend into content in document order. + */ + if (cur->children != NULL) { + if (cur->children->type != XML_ENTITY_DECL) { + cur = cur->children; + level++; + if (copy != NULL) + insert = copy; + continue; + } + } + +skip_children: + /* + * If xslt:message was just processed, we might have hit a + * terminate='yes'; if so, then break the loop and clean up. + * TODO: Do we need to check this also before trying to descend + * into the content? + */ + if (ctxt->state == XSLT_STATE_STOPPED) + break; + if (cur->next != NULL) { + cur = cur->next; + continue; + } + + do { + cur = cur->parent; + level--; + /* + * Pop variables/params (xsl:variable and xsl:param). + */ + if ((ctxt->varsNr > oldVarsNr) && (ctxt->vars->level > level)) { + xsltLocalVariablePop(ctxt, oldVarsNr, level); + } + + insert = insert->parent; + if (cur == NULL) + break; + if (cur == list->parent) { + cur = NULL; + break; + } + if (cur->next != NULL) { + cur = cur->next; + break; + } + } while (cur != NULL); + } + +error: + /* + * In case of errors: pop remaining variables. + */ + if (ctxt->varsNr > oldVarsNr) + xsltLocalVariablePop(ctxt, oldVarsNr, -1); + + ctxt->node = oldContextNode; + ctxt->inst = oldInst; + ctxt->insert = oldInsert; + + ctxt->depth--; + +#ifdef WITH_DEBUGGER + if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) { + xslDropCall(); + } +#endif +} + +/* +* xsltApplyXSLTTemplate: +* @ctxt: a XSLT transformation context +* @contextNode: the node in the source tree. +* @list: the nodes of a sequence constructor; +* (plus leading xsl:param elements) +* @templ: the compiled xsl:template declaration; +* NULL if a sequence constructor +* @withParams: a set of caller-parameters (xsl:with-param) or NULL +* +* Called by: +* - xsltApplyImports() +* - xsltCallTemplate() +* - xsltDefaultProcessOneNode() +* - xsltProcessOneNode() +*/ +static void +xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, + xmlNodePtr list, + xsltTemplatePtr templ, + xsltStackElemPtr withParams) +{ + int oldVarsBase = 0; + long start = 0; + xmlNodePtr cur; + xsltStackElemPtr tmpParam = NULL; + xmlDocPtr oldUserFragmentTop; + +#ifdef XSLT_REFACTORED + xsltStyleItemParamPtr iparam; +#else + xsltStylePreCompPtr iparam; +#endif + +#ifdef WITH_DEBUGGER + int addCallResult = 0; +#endif + + if (ctxt == NULL) + return; + if (templ == NULL) { + xsltTransformError(ctxt, NULL, list, + "xsltApplyXSLTTemplate: Bad arguments; @templ is mandatory.\n"); + return; + } + +#ifdef WITH_DEBUGGER + if (ctxt->debugStatus != XSLT_DEBUG_NONE) { + if (xsltDebuggerStartSequenceConstructor(ctxt, contextNode, + list, templ, &addCallResult) == NULL) + return; + } +#endif + + if (list == NULL) + return; + CHECK_STOPPED; + + if (ctxt->varsNr >= ctxt->maxTemplateVars) + { + xsltTransformError(ctxt, NULL, list, + "xsltApplyXSLTTemplate: A potential infinite template recursion " + "was detected.\n" + "You can adjust maxTemplateVars (--maxvars) in order to " + "raise the maximum number of variables/params (currently set to %d).\n", + ctxt->maxTemplateVars); + xsltDebug(ctxt, contextNode, list, NULL); + ctxt->state = XSLT_STATE_STOPPED; + return; + } + + oldUserFragmentTop = ctxt->tmpRVT; + ctxt->tmpRVT = NULL; + + /* + * Initiate a distinct scope of local params/variables. + */ + oldVarsBase = ctxt->varsBase; + ctxt->varsBase = ctxt->varsNr; + + ctxt->node = contextNode; + if (ctxt->profile) { + templ->nbCalls++; + start = xsltTimestamp(); + profPush(ctxt, 0); + profCallgraphAdd(templ, ctxt->templ); + } + /* + * Push the xsl:template declaration onto the stack. + */ + templPush(ctxt, templ); + +#ifdef WITH_XSLT_DEBUG_PROCESS + if (templ->name != NULL) + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "applying xsl:template '%s'\n", templ->name)); +#endif + /* + * Process xsl:param instructions and skip those elements for + * further processing. + */ + cur = list; + do { + if (cur->type == XML_TEXT_NODE) { + cur = cur->next; + continue; + } + if ((cur->type != XML_ELEMENT_NODE) || + (cur->name[0] != 'p') || + (cur->psvi == NULL) || + (! xmlStrEqual(cur->name, BAD_CAST "param")) || + (! IS_XSLT_ELEM(cur))) + { + break; + } + + list = cur->next; + +#ifdef XSLT_REFACTORED + iparam = (xsltStyleItemParamPtr) cur->psvi; +#else + iparam = (xsltStylePreCompPtr) cur->psvi; +#endif + + /* + * Substitute xsl:param for a given xsl:with-param. + * Since the XPath expression will reference the params/vars + * by index, we need to slot the xsl:with-params in the + * order of encountered xsl:params to keep the sequence of + * params/variables in the stack exactly as it was at + * compile time, + */ + tmpParam = NULL; + if (withParams) { + tmpParam = withParams; + do { + if ((tmpParam->name == (iparam->name)) && + (tmpParam->nameURI == (iparam->ns))) + { + /* + * Push the caller-parameter. + */ + xsltLocalVariablePush(ctxt, tmpParam, -1); + break; + } + tmpParam = tmpParam->next; + } while (tmpParam != NULL); + } + /* + * Push the xsl:param. + */ + if (tmpParam == NULL) { + /* + * Note that we must assume that the added parameter + * has a @depth of 0. + */ + xsltParseStylesheetParam(ctxt, cur); + } + cur = cur->next; + } while (cur != NULL); + /* + * Process the sequence constructor. + */ + xsltApplySequenceConstructor(ctxt, contextNode, list, templ); + + /* + * Remove remaining xsl:param and xsl:with-param items from + * the stack. Don't free xsl:with-param items. + */ + if (ctxt->varsNr > ctxt->varsBase) + xsltTemplateParamsCleanup(ctxt); + ctxt->varsBase = oldVarsBase; + + /* + * Release user-created fragments stored in the scope + * of xsl:template. Note that this mechanism is deprecated: + * user code should now use xsltRegisterLocalRVT() instead + * of the obsolete xsltRegisterTmpRVT(). + */ + if (ctxt->tmpRVT) { + xmlDocPtr curdoc = ctxt->tmpRVT, tmp; + + while (curdoc != NULL) { + tmp = curdoc; + curdoc = (xmlDocPtr) curdoc->next; + xsltReleaseRVT(ctxt, tmp); + } + } + ctxt->tmpRVT = oldUserFragmentTop; + + /* + * Pop the xsl:template declaration from the stack. + */ + templPop(ctxt); + if (ctxt->profile) { + long spent, child, total, end; + + end = xsltTimestamp(); + child = profPop(ctxt); + total = end - start; + spent = total - child; + if (spent <= 0) { + /* + * Not possible unless the original calibration failed + * we can try to correct it on the fly. + */ + xsltCalibrateAdjust(spent); + spent = 0; + } + + templ->time += spent; + if (ctxt->profNr > 0) + ctxt->profTab[ctxt->profNr - 1] += total; + } + +#ifdef WITH_DEBUGGER + if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) { + xslDropCall(); + } +#endif +} + + +/** + * xsltApplyOneTemplate: + * @ctxt: a XSLT process context + * @contextNode: the node in the source tree. + * @list: the nodes of a sequence constructor + * @templ: not used + * @params: a set of parameters (xsl:param) or NULL + * + * Processes a sequence constructor on the current node in the source tree. + * + * @params are the already computed variable stack items; this function + * pushes them on the variable stack, and pops them before exiting; it's + * left to the caller to free or reuse @params afterwards. The initial + * states of the variable stack will always be restored before this + * function exits. + * NOTE that this does *not* initiate a new distinct variable scope; i.e. + * variables already on the stack are visible to the process. The caller's + * side needs to start a new variable scope if needed (e.g. in exsl:function). + * + * @templ is obsolete and not used anymore (e.g. <exslt:function> does not + * provide a @templ); a non-NULL @templ might raise an error in the future. + * + * BIG NOTE: This function is not intended to process the content of an + * xsl:template; it does not expect xsl:param instructions in @list and + * will report errors if found. + * + * Called by: + * - xsltEvalVariable() (variables.c) + * - exsltFuncFunctionFunction() (libexsl/functions.c) + */ +void +xsltApplyOneTemplate(xsltTransformContextPtr ctxt, + xmlNodePtr contextNode, + xmlNodePtr list, + xsltTemplatePtr templ ATTRIBUTE_UNUSED, + xsltStackElemPtr params) +{ + if ((ctxt == NULL) || (list == NULL)) + return; + CHECK_STOPPED; + + if (params) { + /* + * This code should be obsolete - was previously used + * by libexslt/functions.c, but due to bug 381319 the + * logic there was changed. + */ + int oldVarsNr = ctxt->varsNr; + + /* + * Push the given xsl:param(s) onto the variable stack. + */ + while (params != NULL) { + xsltLocalVariablePush(ctxt, params, -1); + params = params->next; + } + xsltApplySequenceConstructor(ctxt, contextNode, list, templ); + /* + * Pop the given xsl:param(s) from the stack but don't free them. + */ + xsltLocalVariablePop(ctxt, oldVarsNr, -2); + } else + xsltApplySequenceConstructor(ctxt, contextNode, list, templ); +} + +/************************************************************************ + * * + * XSLT-1.1 extensions * + * * + ************************************************************************/ + +/** + * xsltDocumentElem: + * @ctxt: an XSLT processing context + * @node: The current node + * @inst: the instruction in the stylesheet + * @castedComp: precomputed information + * + * Process an EXSLT/XSLT-1.1 document element + */ +void +xsltDocumentElem(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemDocumentPtr comp = (xsltStyleItemDocumentPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xsltStylesheetPtr style = NULL; + int ret; + xmlChar *filename = NULL, *prop, *elements; + xmlChar *element, *end; + xmlDocPtr res = NULL; + xmlDocPtr oldOutput; + xmlNodePtr oldInsert, root; + const char *oldOutputFile; + xsltOutputType oldType; + xmlChar *URL = NULL; + const xmlChar *method; + const xmlChar *doctypePublic; + const xmlChar *doctypeSystem; + const xmlChar *version; + const xmlChar *encoding; + int redirect_write_append = 0; + + if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) + return; + + if (comp->filename == NULL) { + + if (xmlStrEqual(inst->name, (const xmlChar *) "output")) { + /* + * The element "output" is in the namespace XSLT_SAXON_NAMESPACE + * (http://icl.com/saxon) + * The @file is in no namespace. + */ +#ifdef WITH_XSLT_DEBUG_EXTRA + xsltGenericDebug(xsltGenericDebugContext, + "Found saxon:output extension\n"); +#endif + URL = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "file", + XSLT_SAXON_NAMESPACE); + + if (URL == NULL) + URL = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "href", + XSLT_SAXON_NAMESPACE); + } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) { +#ifdef WITH_XSLT_DEBUG_EXTRA + xsltGenericDebug(xsltGenericDebugContext, + "Found xalan:write extension\n"); +#endif + URL = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) + "select", + XSLT_XALAN_NAMESPACE); + if (URL != NULL) { + xmlXPathCompExprPtr cmp; + xmlChar *val; + + /* + * Trying to handle bug #59212 + * The value of the "select" attribute is an + * XPath expression. + * (see http://xml.apache.org/xalan-j/extensionslib.html#redirect) + */ + cmp = xmlXPathCompile(URL); + val = xsltEvalXPathString(ctxt, cmp); + xmlXPathFreeCompExpr(cmp); + xmlFree(URL); + URL = val; + } + if (URL == NULL) + URL = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) + "file", + XSLT_XALAN_NAMESPACE); + if (URL == NULL) + URL = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) + "href", + XSLT_XALAN_NAMESPACE); + } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) { + URL = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "href", + NULL); + } + + } else { + URL = xmlStrdup(comp->filename); + } + + if (URL == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsltDocumentElem: href/URI-Reference not found\n"); + return; + } + + /* + * If the computation failed, it's likely that the URL wasn't escaped + */ + filename = xmlBuildURI(URL, (const xmlChar *) ctxt->outputFile); + if (filename == NULL) { + xmlChar *escURL; + + escURL=xmlURIEscapeStr(URL, BAD_CAST ":/.?,"); + if (escURL != NULL) { + filename = xmlBuildURI(escURL, (const xmlChar *) ctxt->outputFile); + xmlFree(escURL); + } + } + + if (filename == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsltDocumentElem: URL computation failed for %s\n", + URL); + xmlFree(URL); + return; + } + + /* + * Security checking: can we write to this resource + */ + if (ctxt->sec != NULL) { + ret = xsltCheckWrite(ctxt->sec, ctxt, filename); + if (ret == 0) { + xsltTransformError(ctxt, NULL, inst, + "xsltDocumentElem: write rights for %s denied\n", + filename); + xmlFree(URL); + xmlFree(filename); + return; + } + } + + oldOutputFile = ctxt->outputFile; + oldOutput = ctxt->output; + oldInsert = ctxt->insert; + oldType = ctxt->type; + ctxt->outputFile = (const char *) filename; + + style = xsltNewStylesheet(); + if (style == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsltDocumentElem: out of memory\n"); + goto error; + } + + /* + * Version described in 1.1 draft allows full parameterization + * of the output. + */ + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "version", + NULL); + if (prop != NULL) { + if (style->version != NULL) + xmlFree(style->version); + style->version = prop; + } + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "encoding", + NULL); + if (prop != NULL) { + if (style->encoding != NULL) + xmlFree(style->encoding); + style->encoding = prop; + } + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "method", + NULL); + if (prop != NULL) { + const xmlChar *URI; + + if (style->method != NULL) + xmlFree(style->method); + style->method = NULL; + if (style->methodURI != NULL) + xmlFree(style->methodURI); + style->methodURI = NULL; + + URI = xsltGetQNameURI(inst, &prop); + if (prop == NULL) { + if (style != NULL) style->errors++; + } else if (URI == NULL) { + if ((xmlStrEqual(prop, (const xmlChar *) "xml")) || + (xmlStrEqual(prop, (const xmlChar *) "html")) || + (xmlStrEqual(prop, (const xmlChar *) "text"))) { + style->method = prop; + } else { + xsltTransformError(ctxt, NULL, inst, + "invalid value for method: %s\n", prop); + if (style != NULL) style->warnings++; + } + } else { + style->method = prop; + style->methodURI = xmlStrdup(URI); + } + } + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) + "doctype-system", NULL); + if (prop != NULL) { + if (style->doctypeSystem != NULL) + xmlFree(style->doctypeSystem); + style->doctypeSystem = prop; + } + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) + "doctype-public", NULL); + if (prop != NULL) { + if (style->doctypePublic != NULL) + xmlFree(style->doctypePublic); + style->doctypePublic = prop; + } + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "standalone", + NULL); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *) "yes")) { + style->standalone = 1; + } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { + style->standalone = 0; + } else { + xsltTransformError(ctxt, NULL, inst, + "invalid value for standalone: %s\n", + prop); + if (style != NULL) style->warnings++; + } + xmlFree(prop); + } + + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "indent", + NULL); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *) "yes")) { + style->indent = 1; + } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { + style->indent = 0; + } else { + xsltTransformError(ctxt, NULL, inst, + "invalid value for indent: %s\n", prop); + if (style != NULL) style->warnings++; + } + xmlFree(prop); + } + + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) + "omit-xml-declaration", + NULL); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *) "yes")) { + style->omitXmlDeclaration = 1; + } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { + style->omitXmlDeclaration = 0; + } else { + xsltTransformError(ctxt, NULL, inst, + "invalid value for omit-xml-declaration: %s\n", + prop); + if (style != NULL) style->warnings++; + } + xmlFree(prop); + } + + elements = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) + "cdata-section-elements", + NULL); + if (elements != NULL) { + if (style->stripSpaces == NULL) + style->stripSpaces = xmlHashCreate(10); + if (style->stripSpaces == NULL) + return; + + element = elements; + while (*element != 0) { + while (IS_BLANK_CH(*element)) + element++; + if (*element == 0) + break; + end = element; + while ((*end != 0) && (!IS_BLANK_CH(*end))) + end++; + element = xmlStrndup(element, end - element); + if (element) { + const xmlChar *URI; + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "add cdata section output element %s\n", + element); +#endif + URI = xsltGetQNameURI(inst, &element); + + xmlHashAddEntry2(style->stripSpaces, element, URI, + (xmlChar *) "cdata"); + xmlFree(element); + } + element = end; + } + xmlFree(elements); + } + + /* + * Create a new document tree and process the element template + */ + XSLT_GET_IMPORT_PTR(method, style, method) + XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) + XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) + XSLT_GET_IMPORT_PTR(version, style, version) + XSLT_GET_IMPORT_PTR(encoding, style, encoding) + + if ((method != NULL) && + (!xmlStrEqual(method, (const xmlChar *) "xml"))) { + if (xmlStrEqual(method, (const xmlChar *) "html")) { + ctxt->type = XSLT_OUTPUT_HTML; + if (((doctypePublic != NULL) || (doctypeSystem != NULL))) + res = htmlNewDoc(doctypeSystem, doctypePublic); + else { + if (version != NULL) { +#ifdef XSLT_GENERATE_HTML_DOCTYPE + xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem); +#endif + } + res = htmlNewDocNoDtD(doctypeSystem, doctypePublic); + } + if (res == NULL) + goto error; + res->dict = ctxt->dict; + xmlDictReference(res->dict); + } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) { + xsltTransformError(ctxt, NULL, inst, + "xsltDocumentElem: unsupported method xhtml\n"); + ctxt->type = XSLT_OUTPUT_HTML; + res = htmlNewDocNoDtD(doctypeSystem, doctypePublic); + if (res == NULL) + goto error; + res->dict = ctxt->dict; + xmlDictReference(res->dict); + } else if (xmlStrEqual(method, (const xmlChar *) "text")) { + ctxt->type = XSLT_OUTPUT_TEXT; + res = xmlNewDoc(style->version); + if (res == NULL) + goto error; + res->dict = ctxt->dict; + xmlDictReference(res->dict); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing transformation dict for output\n"); +#endif + } else { + xsltTransformError(ctxt, NULL, inst, + "xsltDocumentElem: unsupported method (%s)\n", + method); + goto error; + } + } else { + ctxt->type = XSLT_OUTPUT_XML; + res = xmlNewDoc(style->version); + if (res == NULL) + goto error; + res->dict = ctxt->dict; + xmlDictReference(res->dict); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing transformation dict for output\n"); +#endif + } + res->charset = XML_CHAR_ENCODING_UTF8; + if (encoding != NULL) + res->encoding = xmlStrdup(encoding); + ctxt->output = res; + ctxt->insert = (xmlNodePtr) res; + xsltApplySequenceConstructor(ctxt, node, inst->children, NULL); + + /* + * Do some post processing work depending on the generated output + */ + root = xmlDocGetRootElement(res); + if (root != NULL) { + const xmlChar *doctype = NULL; + + if ((root->ns != NULL) && (root->ns->prefix != NULL)) + doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name); + if (doctype == NULL) + doctype = root->name; + + /* + * Apply the default selection of the method + */ + if ((method == NULL) && + (root->ns == NULL) && + (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) { + xmlNodePtr tmp; + + tmp = res->children; + while ((tmp != NULL) && (tmp != root)) { + if (tmp->type == XML_ELEMENT_NODE) + break; + if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp))) + break; + tmp = tmp->next; + } + if (tmp == root) { + ctxt->type = XSLT_OUTPUT_HTML; + res->type = XML_HTML_DOCUMENT_NODE; + if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { + res->intSubset = xmlCreateIntSubset(res, doctype, + doctypePublic, + doctypeSystem); +#ifdef XSLT_GENERATE_HTML_DOCTYPE + } else if (version != NULL) { + xsltGetHTMLIDs(version, &doctypePublic, + &doctypeSystem); + if (((doctypePublic != NULL) || (doctypeSystem != NULL))) + res->intSubset = + xmlCreateIntSubset(res, doctype, + doctypePublic, + doctypeSystem); +#endif + } + } + + } + if (ctxt->type == XSLT_OUTPUT_XML) { + XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) + XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) + if (((doctypePublic != NULL) || (doctypeSystem != NULL))) + res->intSubset = xmlCreateIntSubset(res, doctype, + doctypePublic, + doctypeSystem); + } + } + + /* + * Calls to redirect:write also take an optional attribute append. + * Attribute append="true|yes" which will attempt to simply append + * to an existing file instead of always opening a new file. The + * default behavior of always overwriting the file still happens + * if we do not specify append. + * Note that append use will forbid use of remote URI target. + */ + prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"append", + NULL); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *) "true") || + xmlStrEqual(prop, (const xmlChar *) "yes")) { + style->omitXmlDeclaration = 1; + redirect_write_append = 1; + } else + style->omitXmlDeclaration = 0; + xmlFree(prop); + } + + if (redirect_write_append) { + FILE *f; + + f = fopen((const char *) filename, "ab"); + if (f == NULL) { + ret = -1; + } else { + ret = xsltSaveResultToFile(f, res, style); + fclose(f); + } + } else { + ret = xsltSaveResultToFilename((const char *) filename, res, style, 0); + } + if (ret < 0) { + xsltTransformError(ctxt, NULL, inst, + "xsltDocumentElem: unable to save to %s\n", + filename); +#ifdef WITH_XSLT_DEBUG_EXTRA + } else { + xsltGenericDebug(xsltGenericDebugContext, + "Wrote %d bytes to %s\n", ret, filename); +#endif + } + + error: + ctxt->output = oldOutput; + ctxt->insert = oldInsert; + ctxt->type = oldType; + ctxt->outputFile = oldOutputFile; + if (URL != NULL) + xmlFree(URL); + if (filename != NULL) + xmlFree(filename); + if (style != NULL) + xsltFreeStylesheet(style); + if (res != NULL) + xmlFreeDoc(res); +} + +/************************************************************************ + * * + * Most of the XSLT-1.0 transformations * + * * + ************************************************************************/ + +/** + * xsltSort: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt sort node + * @comp: precomputed information + * + * function attached to xsl:sort nodes, but this should not be + * called directly + */ +void +xsltSort(xsltTransformContextPtr ctxt, + xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst, + xsltStylePreCompPtr comp) { + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:sort : compilation failed\n"); + return; + } + xsltTransformError(ctxt, NULL, inst, + "xsl:sort : improper use this should not be reached\n"); +} + +/** + * xsltCopy: + * @ctxt: an XSLT process context + * @node: the node in the source tree + * @inst: the element node of the XSLT-copy instruction + * @castedComp: computed information of the XSLT-copy instruction + * + * Execute the XSLT-copy instruction on the source node. + */ +void +xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemCopyPtr comp = (xsltStyleItemCopyPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xmlNodePtr copy, oldInsert; + + oldInsert = ctxt->insert; + if (ctxt->insert != NULL) { + switch (node->type) { + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + /* + * This text comes from the stylesheet + * For stylesheets, the set of whitespace-preserving + * element names consists of just xsl:text. + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + if (node->type == XML_CDATA_SECTION_NODE) { + XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopy: CDATA text %s\n", node->content)); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopy: text %s\n", node->content)); + } +#endif + xsltCopyText(ctxt, ctxt->insert, node, 0); + break; + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + break; + case XML_ELEMENT_NODE: + /* + * REVISIT NOTE: The "fake" is a doc-node, not an element node. + * REMOVED: + * if (xmlStrEqual(node->name, BAD_CAST " fake node libxslt")) + * return; + */ + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopy: node %s\n", node->name)); +#endif + copy = xsltShallowCopyElem(ctxt, node, ctxt->insert, 0); + ctxt->insert = copy; + if (comp->use != NULL) { + xsltApplyAttributeSet(ctxt, node, inst, comp->use); + } + break; + case XML_ATTRIBUTE_NODE: { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopy: attribute %s\n", node->name)); +#endif + /* + * REVISIT: We could also raise an error if the parent is not + * an element node. + * OPTIMIZE TODO: Can we set the value/children of the + * attribute without an intermediate copy of the string value? + */ + xsltShallowCopyAttr(ctxt, inst, ctxt->insert, (xmlAttrPtr) node); + break; + } + case XML_PI_NODE: +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopy: PI %s\n", node->name)); +#endif + copy = xmlNewDocPI(ctxt->insert->doc, node->name, + node->content); + copy = xsltAddChild(ctxt->insert, copy); + break; + case XML_COMMENT_NODE: +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopy: comment\n")); +#endif + copy = xmlNewComment(node->content); + copy = xsltAddChild(ctxt->insert, copy); + break; + case XML_NAMESPACE_DECL: +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopy: namespace declaration\n")); +#endif + xsltShallowCopyNsNode(ctxt, inst, ctxt->insert, (xmlNsPtr)node); + break; + default: + break; + + } + } + + switch (node->type) { + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_ELEMENT_NODE: + xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children, + NULL); + break; + default: + break; + } + ctxt->insert = oldInsert; +} + +/** + * xsltText: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt text node + * @comp: precomputed information + * + * Process the xslt text node on the source node + */ +void +xsltText(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED, + xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) { + if ((inst->children != NULL) && (comp != NULL)) { + xmlNodePtr text = inst->children; + xmlNodePtr copy; + + while (text != NULL) { + if ((text->type != XML_TEXT_NODE) && + (text->type != XML_CDATA_SECTION_NODE)) { + xsltTransformError(ctxt, NULL, inst, + "xsl:text content problem\n"); + break; + } + copy = xmlNewDocText(ctxt->output, text->content); + if (text->type != XML_CDATA_SECTION_NODE) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "Disable escaping: %s\n", text->content); +#endif + copy->name = xmlStringTextNoenc; + } + copy = xsltAddChild(ctxt->insert, copy); + text = text->next; + } + } +} + +/** + * xsltElement: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt element node + * @castedComp: precomputed information + * + * Process the xslt element node on the source node + */ +void +xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) { +#ifdef XSLT_REFACTORED + xsltStyleItemElementPtr comp = (xsltStyleItemElementPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xmlChar *prop = NULL; + const xmlChar *name, *prefix = NULL, *nsName = NULL; + xmlNodePtr copy; + xmlNodePtr oldInsert; + + if (ctxt->insert == NULL) + return; + + /* + * A comp->has_name == 0 indicates that we need to skip this instruction, + * since it was evaluated to be invalid already during compilation. + */ + if (!comp->has_name) + return; + + /* + * stack and saves + */ + oldInsert = ctxt->insert; + + if (comp->name == NULL) { + /* TODO: fix attr acquisition wrt to the XSLT namespace */ + prop = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "name", XSLT_NAMESPACE); + if (prop == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:element: The attribute 'name' is missing.\n"); + goto error; + } + if (xmlValidateQName(prop, 0)) { + xsltTransformError(ctxt, NULL, inst, + "xsl:element: The effective name '%s' is not a " + "valid QName.\n", prop); + /* we fall through to catch any further errors, if possible */ + } + name = xsltSplitQName(ctxt->dict, prop, &prefix); + xmlFree(prop); + } else { + /* + * The "name" value was static. + */ +#ifdef XSLT_REFACTORED + prefix = comp->nsPrefix; + name = comp->name; +#else + name = xsltSplitQName(ctxt->dict, comp->name, &prefix); +#endif + } + + /* + * Create the new element + */ + if (ctxt->output->dict == ctxt->dict) { + copy = xmlNewDocNodeEatName(ctxt->output, NULL, (xmlChar *)name, NULL); + } else { + copy = xmlNewDocNode(ctxt->output, NULL, (xmlChar *)name, NULL); + } + if (copy == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:element : creation of %s failed\n", name); + return; + } + copy = xsltAddChild(ctxt->insert, copy); + if (copy == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:element : xsltAddChild failed\n"); + return; + } + + /* + * Namespace + * --------- + */ + if (comp->has_ns) { + if (comp->ns != NULL) { + /* + * No AVT; just plain text for the namespace name. + */ + if (comp->ns[0] != 0) + nsName = comp->ns; + } else { + xmlChar *tmpNsName; + /* + * Eval the AVT. + */ + /* TODO: check attr acquisition wrt to the XSLT namespace */ + tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *) "namespace", XSLT_NAMESPACE); + /* + * SPEC XSLT 1.0: + * "If the string is empty, then the expanded-name of the + * attribute has a null namespace URI." + */ + if ((tmpNsName != NULL) && (tmpNsName[0] != 0)) + nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1); + xmlFree(tmpNsName); + } + + if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) { + xsltTransformError(ctxt, NULL, inst, + "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ " + "forbidden.\n"); + goto error; + } + if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) { + prefix = BAD_CAST "xml"; + } else if (xmlStrEqual(prefix, BAD_CAST "xml")) { + prefix = NULL; + } + } else { + xmlNsPtr ns; + /* + * SPEC XSLT 1.0: + * "If the namespace attribute is not present, then the QName is + * expanded into an expanded-name using the namespace declarations + * in effect for the xsl:element element, including any default + * namespace declaration. + */ + ns = xmlSearchNs(inst->doc, inst, prefix); + if (ns == NULL) { + /* + * TODO: Check this in the compilation layer in case it's a + * static value. + */ + if (prefix != NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:element: The QName '%s:%s' has no " + "namespace binding in scope in the stylesheet; " + "this is an error, since the namespace was not " + "specified by the instruction itself.\n", prefix, name); + } + } else + nsName = ns->href; + } + /* + * Find/create a matching ns-decl in the result tree. + */ + if (nsName != NULL) { + if (xmlStrEqual(prefix, BAD_CAST "xmlns")) { + /* Don't use a prefix of "xmlns" */ + xmlChar *pref = xmlStrdup(BAD_CAST "ns_1"); + + copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, copy); + + xmlFree(pref); + } else { + copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix, + copy); + } + } else if ((copy->parent != NULL) && + (copy->parent->type == XML_ELEMENT_NODE) && + (copy->parent->ns != NULL)) + { + /* + * "Undeclare" the default namespace. + */ + xsltGetSpecialNamespace(ctxt, inst, NULL, NULL, copy); + } + + ctxt->insert = copy; + + if (comp->has_use) { + if (comp->use != NULL) { + xsltApplyAttributeSet(ctxt, node, inst, comp->use); + } else { + xmlChar *attrSets = NULL; + /* + * BUG TODO: use-attribute-sets is not a value template. + * use-attribute-sets = qnames + */ + attrSets = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *)"use-attribute-sets", NULL); + if (attrSets != NULL) { + xsltApplyAttributeSet(ctxt, node, inst, attrSets); + xmlFree(attrSets); + } + } + } + /* + * Instantiate the sequence constructor. + */ + if (inst->children != NULL) + xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children, + NULL); + +error: + ctxt->insert = oldInsert; + return; +} + + +/** + * xsltComment: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt comment node + * @comp: precomputed information + * + * Process the xslt comment node on the source node + */ +void +xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) { + xmlChar *value = NULL; + xmlNodePtr commentNode; + int len; + + value = xsltEvalTemplateString(ctxt, node, inst); + /* TODO: use or generate the compiled form */ + len = xmlStrlen(value); + if (len > 0) { + if ((value[len-1] == '-') || + (xmlStrstr(value, BAD_CAST "--"))) { + xsltTransformError(ctxt, NULL, inst, + "xsl:comment : '--' or ending '-' not allowed in comment\n"); + /* fall through to try to catch further errors */ + } + } +#ifdef WITH_XSLT_DEBUG_PROCESS + if (value == NULL) { + XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext, + "xsltComment: empty\n")); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext, + "xsltComment: content %s\n", value)); + } +#endif + + commentNode = xmlNewComment(value); + commentNode = xsltAddChild(ctxt->insert, commentNode); + + if (value != NULL) + xmlFree(value); +} + +/** + * xsltProcessingInstruction: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt processing-instruction node + * @castedComp: precomputed information + * + * Process the xslt processing-instruction node on the source node + */ +void +xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) { +#ifdef XSLT_REFACTORED + xsltStyleItemPIPtr comp = (xsltStyleItemPIPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + const xmlChar *name; + xmlChar *value = NULL; + xmlNodePtr pi; + + + if (ctxt->insert == NULL) + return; + if (comp->has_name == 0) + return; + if (comp->name == NULL) { + name = xsltEvalAttrValueTemplate(ctxt, inst, + (const xmlChar *)"name", NULL); + if (name == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:processing-instruction : name is missing\n"); + goto error; + } + } else { + name = comp->name; + } + /* TODO: check that it's both an an NCName and a PITarget. */ + + + value = xsltEvalTemplateString(ctxt, node, inst); + if (xmlStrstr(value, BAD_CAST "?>") != NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:processing-instruction: '?>' not allowed within PI content\n"); + goto error; + } +#ifdef WITH_XSLT_DEBUG_PROCESS + if (value == NULL) { + XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessingInstruction: %s empty\n", name)); + } else { + XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext, + "xsltProcessingInstruction: %s content %s\n", name, value)); + } +#endif + + pi = xmlNewDocPI(ctxt->insert->doc, name, value); + pi = xsltAddChild(ctxt->insert, pi); + +error: + if ((name != NULL) && (name != comp->name)) + xmlFree((xmlChar *) name); + if (value != NULL) + xmlFree(value); +} + +/** + * xsltCopyOf: + * @ctxt: an XSLT transformation context + * @node: the current node in the source tree + * @inst: the element node of the XSLT copy-of instruction + * @castedComp: precomputed information of the XSLT copy-of instruction + * + * Process the XSLT copy-of instruction. + */ +void +xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) { +#ifdef XSLT_REFACTORED + xsltStyleItemCopyOfPtr comp = (xsltStyleItemCopyOfPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xmlXPathObjectPtr res = NULL; + xmlNodeSetPtr list = NULL; + int i; + + if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) + return; + if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) { + xsltTransformError(ctxt, NULL, inst, + "xsl:copy-of : compilation failed\n"); + return; + } + + /* + * SPEC XSLT 1.0: + * "The xsl:copy-of element can be used to insert a result tree + * fragment into the result tree, without first converting it to + * a string as xsl:value-of does (see [7.6.1 Generating Text with + * xsl:value-of]). The required select attribute contains an + * expression. When the result of evaluating the expression is a + * result tree fragment, the complete fragment is copied into the + * result tree. When the result is a node-set, all the nodes in the + * set are copied in document order into the result tree; copying + * an element node copies the attribute nodes, namespace nodes and + * children of the element node as well as the element node itself; + * a root node is copied by copying its children. When the result + * is neither a node-set nor a result tree fragment, the result is + * converted to a string and then inserted into the result tree, + * as with xsl:value-of. + */ + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopyOf: select %s\n", comp->select)); +#endif + + /* + * Evaluate the "select" expression. + */ + res = xsltPreCompEval(ctxt, node, comp); + + if (res != NULL) { + if (res->type == XPATH_NODESET) { + /* + * Node-set + * -------- + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopyOf: result is a node set\n")); +#endif + list = res->nodesetval; + if (list != NULL) { + xmlNodePtr cur; + /* + * The list is already sorted in document order by XPath. + * Append everything in this order under ctxt->insert. + */ + for (i = 0;i < list->nodeNr;i++) { + cur = list->nodeTab[i]; + if (cur == NULL) + continue; + if ((cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE)) + { + xsltCopyTreeList(ctxt, inst, + cur->children, ctxt->insert, 0, 0); + } else if (cur->type == XML_ATTRIBUTE_NODE) { + xsltShallowCopyAttr(ctxt, inst, + ctxt->insert, (xmlAttrPtr) cur); + } else { + xsltCopyTreeInternal(ctxt, inst, + cur, ctxt->insert, 0, 0); + } + } + } + } else if (res->type == XPATH_XSLT_TREE) { + /* + * Result tree fragment + * -------------------- + * E.g. via <xsl:variable ...><foo/></xsl:variable> + * Note that the root node of such trees is an xmlDocPtr in Libxslt. + */ +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopyOf: result is a result tree fragment\n")); +#endif + list = res->nodesetval; + if ((list != NULL) && (list->nodeTab != NULL) && + (list->nodeTab[0] != NULL) && + (IS_XSLT_REAL_NODE(list->nodeTab[0]))) + { + xsltCopyTreeList(ctxt, inst, + list->nodeTab[0]->children, ctxt->insert, 0, 0); + } + } else { + xmlChar *value = NULL; + /* + * Convert to a string. + */ + value = xmlXPathCastToString(res); + if (value == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltCopyOf(): " + "failed to cast an XPath object to string.\n"); + ctxt->state = XSLT_STATE_STOPPED; + } else { + if (value[0] != 0) { + /* + * Append content as text node. + */ + xsltCopyTextString(ctxt, ctxt->insert, value, 0); + } + xmlFree(value); + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext, + "xsltCopyOf: result %s\n", res->stringval)); +#endif + } + } + } else { + ctxt->state = XSLT_STATE_STOPPED; + } + + if (res != NULL) + xmlXPathFreeObject(res); +} + +/** + * xsltValueOf: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt value-of node + * @castedComp: precomputed information + * + * Process the xslt value-of node on the source node + */ +void +xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemValueOfPtr comp = (xsltStyleItemValueOfPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xmlXPathObjectPtr res = NULL; + xmlChar *value = NULL; + + if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) + return; + + if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltValueOf(): " + "The XSLT 'value-of' instruction was not compiled.\n"); + return; + } + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext, + "xsltValueOf: select %s\n", comp->select)); +#endif + + res = xsltPreCompEval(ctxt, node, comp); + + /* + * Cast the XPath object to string. + */ + if (res != NULL) { + value = xmlXPathCastToString(res); + if (value == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltValueOf(): " + "failed to cast an XPath object to string.\n"); + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + if (value[0] != 0) { + xsltCopyTextString(ctxt, ctxt->insert, value, comp->noescape); + } + } else { + xsltTransformError(ctxt, NULL, inst, + "XPath evaluation returned no result.\n"); + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + +#ifdef WITH_XSLT_DEBUG_PROCESS + if (value) { + XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext, + "xsltValueOf: result '%s'\n", value)); + } +#endif + +error: + if (value != NULL) + xmlFree(value); + if (res != NULL) + xmlXPathFreeObject(res); +} + +/** + * xsltNumber: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt number node + * @castedComp: precomputed information + * + * Process the xslt number node on the source node + */ +void +xsltNumber(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemNumberPtr comp = (xsltStyleItemNumberPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xmlXPathContextPtr xpctxt; + xmlNsPtr *oldXPNamespaces; + int oldXPNsNr; + + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:number : compilation failed\n"); + return; + } + + if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) + return; + + comp->numdata.doc = inst->doc; + comp->numdata.node = inst; + + xpctxt = ctxt->xpathCtxt; + oldXPNsNr = xpctxt->nsNr; + oldXPNamespaces = xpctxt->namespaces; + +#ifdef XSLT_REFACTORED + if (comp->inScopeNs != NULL) { + xpctxt->namespaces = comp->inScopeNs->list; + xpctxt->nsNr = comp->inScopeNs->xpathNumber; + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } +#else + xpctxt->namespaces = comp->nsList; + xpctxt->nsNr = comp->nsNr; +#endif + + xsltNumberFormat(ctxt, &comp->numdata, node); + + xpctxt->nsNr = oldXPNsNr; + xpctxt->namespaces = oldXPNamespaces; +} + +/** + * xsltApplyImports: + * @ctxt: an XSLT transformation context + * @contextNode: the current node in the source tree. + * @inst: the element node of the XSLT 'apply-imports' instruction + * @comp: the compiled instruction + * + * Process the XSLT apply-imports element. + */ +void +xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, + xmlNodePtr inst, + xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) +{ + xsltTemplatePtr templ; + + if ((ctxt == NULL) || (inst == NULL)) + return; + + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltApplyImports(): " + "The XSLT 'apply-imports' instruction was not compiled.\n"); + return; + } + /* + * NOTE that ctxt->currentTemplateRule and ctxt->templ is not the + * same; the former is the "Current Template Rule" as defined by the + * XSLT spec, the latter is simply the template struct being + * currently processed. + */ + if (ctxt->currentTemplateRule == NULL) { + /* + * SPEC XSLT 2.0: + * "[ERR XTDE0560] It is a non-recoverable dynamic error if + * xsl:apply-imports or xsl:next-match is evaluated when the + * current template rule is null." + */ + xsltTransformError(ctxt, NULL, inst, + "It is an error to call 'apply-imports' " + "when there's no current template rule.\n"); + return; + } + /* + * TODO: Check if this is correct. + */ + templ = xsltGetTemplate(ctxt, contextNode, + ctxt->currentTemplateRule->style); + + if (templ != NULL) { + xsltTemplatePtr oldCurTemplRule = ctxt->currentTemplateRule; + /* + * Set the current template rule. + */ + ctxt->currentTemplateRule = templ; + /* + * URGENT TODO: Need xsl:with-param be handled somehow here? + */ + xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, + templ, NULL); + + ctxt->currentTemplateRule = oldCurTemplRule; + } + else { + /* Use built-in templates. */ + xsltDefaultProcessOneNode(ctxt, contextNode, NULL); + } +} + +/** + * xsltCallTemplate: + * @ctxt: a XSLT transformation context + * @node: the "current node" in the source tree + * @inst: the XSLT 'call-template' instruction + * @castedComp: the compiled information of the instruction + * + * Processes the XSLT call-template instruction on the source node. + */ +void +xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemCallTemplatePtr comp = + (xsltStyleItemCallTemplatePtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xsltStackElemPtr withParams = NULL; + + if (ctxt->insert == NULL) + return; + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "The XSLT 'call-template' instruction was not compiled.\n"); + return; + } + + /* + * The template must have been precomputed + */ + if (comp->templ == NULL) { + comp->templ = xsltFindTemplate(ctxt, comp->name, comp->ns); + if (comp->templ == NULL) { + if (comp->ns != NULL) { + xsltTransformError(ctxt, NULL, inst, + "The called template '{%s}%s' was not found.\n", + comp->ns, comp->name); + } else { + xsltTransformError(ctxt, NULL, inst, + "The called template '%s' was not found.\n", + comp->name); + } + return; + } + } + +#ifdef WITH_XSLT_DEBUG_PROCESS + if ((comp != NULL) && (comp->name != NULL)) + XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "call-template: name %s\n", comp->name)); +#endif + + if (inst->children) { + xmlNodePtr cur; + xsltStackElemPtr param; + + cur = inst->children; + while (cur != NULL) { +#ifdef WITH_DEBUGGER + if (ctxt->debugStatus != XSLT_DEBUG_NONE) + xslHandleDebugger(cur, node, comp->templ, ctxt); +#endif + if (ctxt->state == XSLT_STATE_STOPPED) break; + /* + * TODO: The "with-param"s could be part of the "call-template" + * structure. Avoid to "search" for params dynamically + * in the XML tree every time. + */ + if (IS_XSLT_ELEM(cur)) { + if (IS_XSLT_NAME(cur, "with-param")) { + param = xsltParseStylesheetCallerParam(ctxt, cur); + if (param != NULL) { + param->next = withParams; + withParams = param; + } + } else { + xsltGenericError(xsltGenericErrorContext, + "xsl:call-template: misplaced xsl:%s\n", cur->name); + } + } else { + xsltGenericError(xsltGenericErrorContext, + "xsl:call-template: misplaced %s element\n", cur->name); + } + cur = cur->next; + } + } + /* + * Create a new frame using the params first + */ + xsltApplyXSLTTemplate(ctxt, node, comp->templ->content, comp->templ, + withParams); + if (withParams != NULL) + xsltFreeStackElemList(withParams); + +#ifdef WITH_XSLT_DEBUG_PROCESS + if ((comp != NULL) && (comp->name != NULL)) + XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext, + "call-template returned: name %s\n", comp->name)); +#endif +} + +/** + * xsltApplyTemplates: + * @ctxt: a XSLT transformation context + * @node: the 'current node' in the source tree + * @inst: the element node of an XSLT 'apply-templates' instruction + * @castedComp: the compiled instruction + * + * Processes the XSLT 'apply-templates' instruction on the current node. + */ +void +xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemApplyTemplatesPtr comp = + (xsltStyleItemApplyTemplatesPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + int i; + xmlNodePtr cur, delNode = NULL, oldContextNode; + xmlNodeSetPtr list = NULL, oldList; + xsltStackElemPtr withParams = NULL; + int oldXPProximityPosition, oldXPContextSize; + const xmlChar *oldMode, *oldModeURI; + xmlDocPtr oldXPDoc; + xsltDocumentPtr oldDocInfo; + xmlXPathContextPtr xpctxt; + + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:apply-templates : compilation failed\n"); + return; + } + if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) + return; + +#ifdef WITH_XSLT_DEBUG_PROCESS + if ((node != NULL) && (node->name != NULL)) + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyTemplates: node: '%s'\n", node->name)); +#endif + + xpctxt = ctxt->xpathCtxt; + /* + * Save context states. + */ + oldContextNode = ctxt->node; + oldMode = ctxt->mode; + oldModeURI = ctxt->modeURI; + oldDocInfo = ctxt->document; + oldList = ctxt->nodeList; + + /* + * The xpath context size and proximity position, as + * well as the xpath and context documents, may be changed + * so we save their initial state and will restore on exit + */ + oldXPContextSize = xpctxt->contextSize; + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPDoc = xpctxt->doc; + + /* + * Set up contexts. + */ + ctxt->mode = comp->mode; + ctxt->modeURI = comp->modeURI; + + if (comp->select != NULL) { + xmlXPathObjectPtr res = NULL; + + if (comp->comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:apply-templates : compilation failed\n"); + goto error; + } +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyTemplates: select %s\n", comp->select)); +#endif + + res = xsltPreCompEval(ctxt, node, comp); + + if (res != NULL) { + if (res->type == XPATH_NODESET) { + list = res->nodesetval; /* consume the node set */ + res->nodesetval = NULL; + } else { + xsltTransformError(ctxt, NULL, inst, + "The 'select' expression did not evaluate to a " + "node set.\n"); + ctxt->state = XSLT_STATE_STOPPED; + xmlXPathFreeObject(res); + goto error; + } + xmlXPathFreeObject(res); + /* + * Note: An xsl:apply-templates with a 'select' attribute, + * can change the current source doc. + */ + } else { + xsltTransformError(ctxt, NULL, inst, + "Failed to evaluate the 'select' expression.\n"); + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + if (list == NULL) { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyTemplates: select didn't evaluate to a node list\n")); +#endif + goto exit; + } + /* + * + * NOTE: Previously a document info (xsltDocument) was + * created and attached to the Result Tree Fragment. + * But such a document info is created on demand in + * xsltKeyFunction() (functions.c), so we need to create + * it here beforehand. + * In order to take care of potential keys we need to + * do some extra work for the case when a Result Tree Fragment + * is converted into a nodeset (e.g. exslt:node-set()) : + * We attach a "pseudo-doc" (xsltDocument) to _private. + * This xsltDocument, together with the keyset, will be freed + * when the Result Tree Fragment is freed. + * + */ +#if 0 + if ((ctxt->nbKeys > 0) && + (list->nodeNr != 0) && + (list->nodeTab[0]->doc != NULL) && + XSLT_IS_RES_TREE_FRAG(list->nodeTab[0]->doc)) + { + /* + * NOTE that it's also OK if @effectiveDocInfo will be + * set to NULL. + */ + isRTF = 1; + effectiveDocInfo = list->nodeTab[0]->doc->_private; + } +#endif + } else { + /* + * Build an XPath node set with the children + */ + list = xmlXPathNodeSetCreate(NULL); + if (list == NULL) + goto error; + if (node->type != XML_NAMESPACE_DECL) + cur = node->children; + else + cur = NULL; + while (cur != NULL) { + switch (cur->type) { + case XML_TEXT_NODE: + if ((IS_BLANK_NODE(cur)) && + (cur->parent != NULL) && + (cur->parent->type == XML_ELEMENT_NODE) && + (ctxt->style->stripSpaces != NULL)) { + const xmlChar *val; + + if (cur->parent->ns != NULL) { + val = (const xmlChar *) + xmlHashLookup2(ctxt->style->stripSpaces, + cur->parent->name, + cur->parent->ns->href); + if (val == NULL) { + val = (const xmlChar *) + xmlHashLookup2(ctxt->style->stripSpaces, + BAD_CAST "*", + cur->parent->ns->href); + } + } else { + val = (const xmlChar *) + xmlHashLookup2(ctxt->style->stripSpaces, + cur->parent->name, NULL); + } + if ((val != NULL) && + (xmlStrEqual(val, (xmlChar *) "strip"))) { + delNode = cur; + break; + } + } + /* no break on purpose */ + case XML_ELEMENT_NODE: + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + xmlXPathNodeSetAddUnique(list, cur); + break; + case XML_DTD_NODE: + /* Unlink the DTD, it's still reachable + * using doc->intSubset */ + if (cur->next != NULL) + cur->next->prev = cur->prev; + if (cur->prev != NULL) + cur->prev->next = cur->next; + break; + case XML_NAMESPACE_DECL: + break; + default: +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyTemplates: skipping cur type %d\n", + cur->type)); +#endif + delNode = cur; + } + cur = cur->next; + if (delNode != NULL) { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyTemplates: removing ignorable blank cur\n")); +#endif + xmlUnlinkNode(delNode); + xmlFreeNode(delNode); + delNode = NULL; + } + } + } + +#ifdef WITH_XSLT_DEBUG_PROCESS + if (list != NULL) + XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyTemplates: list of %d nodes\n", list->nodeNr)); +#endif + + if ((list == NULL) || (list->nodeNr == 0)) + goto exit; + + /* + * Set the context's node set and size; this is also needed for + * for xsltDoSortFunction(). + */ + ctxt->nodeList = list; + /* + * Process xsl:with-param and xsl:sort instructions. + * (The code became so verbose just to avoid the + * xmlNodePtr sorts[XSLT_MAX_SORT] if there's no xsl:sort) + * BUG TODO: We are not using namespaced potentially defined on the + * xsl:sort or xsl:with-param elements; XPath expression might fail. + */ + if (inst->children) { + xsltStackElemPtr param; + + cur = inst->children; + while (cur) { + +#ifdef WITH_DEBUGGER + if (ctxt->debugStatus != XSLT_DEBUG_NONE) + xslHandleDebugger(cur, node, NULL, ctxt); +#endif + if (ctxt->state == XSLT_STATE_STOPPED) + break; + if (cur->type == XML_TEXT_NODE) { + cur = cur->next; + continue; + } + if (! IS_XSLT_ELEM(cur)) + break; + if (IS_XSLT_NAME(cur, "with-param")) { + param = xsltParseStylesheetCallerParam(ctxt, cur); + if (param != NULL) { + param->next = withParams; + withParams = param; + } + } + if (IS_XSLT_NAME(cur, "sort")) { + xsltTemplatePtr oldCurTempRule = + ctxt->currentTemplateRule; + int nbsorts = 0; + xmlNodePtr sorts[XSLT_MAX_SORT]; + + sorts[nbsorts++] = cur; + + while (cur) { + +#ifdef WITH_DEBUGGER + if (ctxt->debugStatus != XSLT_DEBUG_NONE) + xslHandleDebugger(cur, node, NULL, ctxt); +#endif + if (ctxt->state == XSLT_STATE_STOPPED) + break; + + if (cur->type == XML_TEXT_NODE) { + cur = cur->next; + continue; + } + + if (! IS_XSLT_ELEM(cur)) + break; + if (IS_XSLT_NAME(cur, "with-param")) { + param = xsltParseStylesheetCallerParam(ctxt, cur); + if (param != NULL) { + param->next = withParams; + withParams = param; + } + } + if (IS_XSLT_NAME(cur, "sort")) { + if (nbsorts >= XSLT_MAX_SORT) { + xsltTransformError(ctxt, NULL, cur, + "The number (%d) of xsl:sort instructions exceeds the " + "maximum allowed by this processor's settings.\n", + nbsorts); + ctxt->state = XSLT_STATE_STOPPED; + break; + } else { + sorts[nbsorts++] = cur; + } + } + cur = cur->next; + } + /* + * The "current template rule" is cleared for xsl:sort. + */ + ctxt->currentTemplateRule = NULL; + /* + * Sort. + */ + xsltDoSortFunction(ctxt, sorts, nbsorts); + ctxt->currentTemplateRule = oldCurTempRule; + break; + } + cur = cur->next; + } + } + xpctxt->contextSize = list->nodeNr; + /* + * Apply templates for all selected source nodes. + */ + for (i = 0; i < list->nodeNr; i++) { + cur = list->nodeTab[i]; + /* + * The node becomes the "current node". + */ + ctxt->node = cur; + /* + * An xsl:apply-templates can change the current context doc. + * OPTIMIZE TODO: Get rid of the need to set the context doc. + */ + if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL)) + xpctxt->doc = cur->doc; + + xpctxt->proximityPosition = i + 1; + /* + * Find and apply a template for this node. + */ + xsltProcessOneNode(ctxt, cur, withParams); + } + +exit: +error: + /* + * Free the parameter list. + */ + if (withParams != NULL) + xsltFreeStackElemList(withParams); + if (list != NULL) + xmlXPathFreeNodeSet(list); + /* + * Restore context states. + */ + xpctxt->doc = oldXPDoc; + xpctxt->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; + + ctxt->document = oldDocInfo; + ctxt->nodeList = oldList; + ctxt->node = oldContextNode; + ctxt->mode = oldMode; + ctxt->modeURI = oldModeURI; +} + + +/** + * xsltChoose: + * @ctxt: a XSLT process context + * @contextNode: the current node in the source tree + * @inst: the xsl:choose instruction + * @comp: compiled information of the instruction + * + * Processes the xsl:choose instruction on the source node. + */ +void +xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, + xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) +{ + xmlNodePtr cur; + + if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) + return; + + /* + * TODO: Content model checks should be done only at compilation + * time. + */ + cur = inst->children; + if (cur == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsl:choose: The instruction has no content.\n"); + return; + } + +#ifdef XSLT_REFACTORED + /* + * We don't check the content model during transformation. + */ +#else + if ((! IS_XSLT_ELEM(cur)) || (! IS_XSLT_NAME(cur, "when"))) { + xsltTransformError(ctxt, NULL, inst, + "xsl:choose: xsl:when expected first\n"); + return; + } +#endif + + { + int testRes = 0, res = 0; + +#ifdef XSLT_REFACTORED + xsltStyleItemWhenPtr wcomp = NULL; +#else + xsltStylePreCompPtr wcomp = NULL; +#endif + + /* + * Process xsl:when --------------------------------------------------- + */ + while (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "when")) { + wcomp = cur->psvi; + + if ((wcomp == NULL) || (wcomp->test == NULL) || + (wcomp->comp == NULL)) + { + xsltTransformError(ctxt, NULL, cur, + "Internal error in xsltChoose(): " + "The XSLT 'when' instruction was not compiled.\n"); + goto error; + } + + +#ifdef WITH_DEBUGGER + if (xslDebugStatus != XSLT_DEBUG_NONE) { + /* + * TODO: Isn't comp->templ always NULL for xsl:choose? + */ + xslHandleDebugger(cur, contextNode, NULL, ctxt); + } +#endif +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, + "xsltChoose: test %s\n", wcomp->test)); +#endif + +#ifdef XSLT_FAST_IF + res = xsltPreCompEvalToBoolean(ctxt, contextNode, wcomp); + + if (res == -1) { + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + testRes = (res == 1) ? 1 : 0; + +#else /* XSLT_FAST_IF */ + + res = xsltPreCompEval(ctxt, cotextNode, wcomp); + + if (res != NULL) { + if (res->type != XPATH_BOOLEAN) + res = xmlXPathConvertBoolean(res); + if (res->type == XPATH_BOOLEAN) + testRes = res->boolval; + else { +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, + "xsltChoose: test didn't evaluate to a boolean\n")); +#endif + goto error; + } + xmlXPathFreeObject(res); + res = NULL; + } else { + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + +#endif /* else of XSLT_FAST_IF */ + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, + "xsltChoose: test evaluate to %d\n", testRes)); +#endif + if (testRes) + goto test_is_true; + + cur = cur->next; + } + + /* + * Process xsl:otherwise ---------------------------------------------- + */ + if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "otherwise")) { + +#ifdef WITH_DEBUGGER + if (xslDebugStatus != XSLT_DEBUG_NONE) + xslHandleDebugger(cur, contextNode, NULL, ctxt); +#endif + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext, + "evaluating xsl:otherwise\n")); +#endif + goto test_is_true; + } + goto exit; + +test_is_true: + + goto process_sequence; + } + +process_sequence: + + /* + * Instantiate the sequence constructor. + */ + xsltApplySequenceConstructor(ctxt, ctxt->node, cur->children, + NULL); + +exit: +error: + return; +} + +/** + * xsltIf: + * @ctxt: a XSLT process context + * @contextNode: the current node in the source tree + * @inst: the xsl:if instruction + * @castedComp: compiled information of the instruction + * + * Processes the xsl:if instruction on the source node. + */ +void +xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ + int res = 0; + +#ifdef XSLT_REFACTORED + xsltStyleItemIfPtr comp = (xsltStyleItemIfPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + + if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) + return; + if ((comp == NULL) || (comp->test == NULL) || (comp->comp == NULL)) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltIf(): " + "The XSLT 'if' instruction was not compiled.\n"); + return; + } + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, + "xsltIf: test %s\n", comp->test)); +#endif + +#ifdef XSLT_FAST_IF + { + xmlDocPtr oldLocalFragmentTop = ctxt->localRVT; + + res = xsltPreCompEvalToBoolean(ctxt, contextNode, comp); + + /* + * Cleanup fragments created during evaluation of the + * "select" expression. + */ + if (oldLocalFragmentTop != ctxt->localRVT) + xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop); + } + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, + "xsltIf: test evaluate to %d\n", res)); +#endif + + if (res == -1) { + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + if (res == 1) { + /* + * Instantiate the sequence constructor of xsl:if. + */ + xsltApplySequenceConstructor(ctxt, + contextNode, inst->children, NULL); + } + +#else /* XSLT_FAST_IF */ + { + /* + * OLD CODE: + */ + xmlXPathObjectPtr xpobj = xsltPreCompEval(ctxt, contextNode, comp); + if (xpobj != NULL) { + if (xpobj->type != XPATH_BOOLEAN) + xpobj = xmlXPathConvertBoolean(xpobj); + if (xpobj->type == XPATH_BOOLEAN) { + res = xpobj->boolval; + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext, + "xsltIf: test evaluate to %d\n", res)); +#endif + if (res) { + xsltApplySequenceConstructor(ctxt, + contextNode, inst->children, NULL); + } + } else { + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt, XSLT_TRACE_IF, + xsltGenericDebug(xsltGenericDebugContext, + "xsltIf: test didn't evaluate to a boolean\n")); +#endif + ctxt->state = XSLT_STATE_STOPPED; + } + xmlXPathFreeObject(xpobj); + } else { + ctxt->state = XSLT_STATE_STOPPED; + } + } +#endif /* else of XSLT_FAST_IF */ + +error: + return; +} + +/** + * xsltForEach: + * @ctxt: an XSLT transformation context + * @contextNode: the "current node" in the source tree + * @inst: the element node of the xsl:for-each instruction + * @castedComp: the compiled information of the instruction + * + * Process the xslt for-each node on the source node + */ +void +xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr contextNode, + xmlNodePtr inst, xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemForEachPtr comp = (xsltStyleItemForEachPtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + int i; + xmlXPathObjectPtr res = NULL; + xmlNodePtr cur, curInst; + xmlNodeSetPtr list = NULL; + xmlNodeSetPtr oldList; + int oldXPProximityPosition, oldXPContextSize; + xmlNodePtr oldContextNode; + xsltTemplatePtr oldCurTemplRule; + xmlDocPtr oldXPDoc; + xsltDocumentPtr oldDocInfo; + xmlXPathContextPtr xpctxt; + + if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) { + xsltGenericError(xsltGenericErrorContext, + "xsltForEach(): Bad arguments.\n"); + return; + } + + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltForEach(): " + "The XSLT 'for-each' instruction was not compiled.\n"); + return; + } + if ((comp->select == NULL) || (comp->comp == NULL)) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltForEach(): " + "The selecting expression of the XSLT 'for-each' " + "instruction was not compiled correctly.\n"); + return; + } + xpctxt = ctxt->xpathCtxt; + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, + "xsltForEach: select %s\n", comp->select)); +#endif + + /* + * Save context states. + */ + oldDocInfo = ctxt->document; + oldList = ctxt->nodeList; + oldContextNode = ctxt->node; + /* + * The "current template rule" is cleared for the instantiation of + * xsl:for-each. + */ + oldCurTemplRule = ctxt->currentTemplateRule; + ctxt->currentTemplateRule = NULL; + + oldXPDoc = xpctxt->doc; + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPContextSize = xpctxt->contextSize; + + /* + * Evaluate the 'select' expression. + */ + res = xsltPreCompEval(ctxt, contextNode, comp); + + if (res != NULL) { + if (res->type == XPATH_NODESET) + list = res->nodesetval; + else { + xsltTransformError(ctxt, NULL, inst, + "The 'select' expression does not evaluate to a node set.\n"); + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, + "xsltForEach: select didn't evaluate to a node list\n")); +#endif + goto error; + } + } else { + xsltTransformError(ctxt, NULL, inst, + "Failed to evaluate the 'select' expression.\n"); + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + + if ((list == NULL) || (list->nodeNr <= 0)) + goto exit; + +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext, + "xsltForEach: select evaluates to %d nodes\n", list->nodeNr)); +#endif + + /* + * Set the list; this has to be done already here for xsltDoSortFunction(). + */ + ctxt->nodeList = list; + /* + * Handle xsl:sort instructions and skip them for further processing. + * BUG TODO: We are not using namespaced potentially defined on the + * xsl:sort element; XPath expression might fail. + */ + curInst = inst->children; + if (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) { + int nbsorts = 0; + xmlNodePtr sorts[XSLT_MAX_SORT]; + + sorts[nbsorts++] = curInst; + +#ifdef WITH_DEBUGGER + if (xslDebugStatus != XSLT_DEBUG_NONE) + xslHandleDebugger(curInst, contextNode, NULL, ctxt); +#endif + + curInst = curInst->next; + while (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) { + if (nbsorts >= XSLT_MAX_SORT) { + xsltTransformError(ctxt, NULL, curInst, + "The number of xsl:sort instructions exceeds the " + "maximum (%d) allowed by this processor.\n", + XSLT_MAX_SORT); + goto error; + } else { + sorts[nbsorts++] = curInst; + } + +#ifdef WITH_DEBUGGER + if (xslDebugStatus != XSLT_DEBUG_NONE) + xslHandleDebugger(curInst, contextNode, NULL, ctxt); +#endif + curInst = curInst->next; + } + xsltDoSortFunction(ctxt, sorts, nbsorts); + } + xpctxt->contextSize = list->nodeNr; + /* + * Instantiate the sequence constructor for each selected node. + */ + for (i = 0; i < list->nodeNr; i++) { + cur = list->nodeTab[i]; + /* + * The selected node becomes the "current node". + */ + ctxt->node = cur; + /* + * An xsl:for-each can change the current context doc. + * OPTIMIZE TODO: Get rid of the need to set the context doc. + */ + if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL)) + xpctxt->doc = cur->doc; + + xpctxt->proximityPosition = i + 1; + + xsltApplySequenceConstructor(ctxt, cur, curInst, NULL); + } + +exit: +error: + if (res != NULL) + xmlXPathFreeObject(res); + /* + * Restore old states. + */ + ctxt->document = oldDocInfo; + ctxt->nodeList = oldList; + ctxt->node = oldContextNode; + ctxt->currentTemplateRule = oldCurTemplRule; + + xpctxt->doc = oldXPDoc; + xpctxt->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; +} + +/************************************************************************ + * * + * Generic interface * + * * + ************************************************************************/ + +#ifdef XSLT_GENERATE_HTML_DOCTYPE +typedef struct xsltHTMLVersion { + const char *version; + const char *public; + const char *system; +} xsltHTMLVersion; + +static xsltHTMLVersion xsltHTMLVersions[] = { + { "4.01frame", "-//W3C//DTD HTML 4.01 Frameset//EN", + "http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd"}, + { "4.01strict", "-//W3C//DTD HTML 4.01//EN", + "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"}, + { "4.01trans", "-//W3C//DTD HTML 4.01 Transitional//EN", + "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"}, + { "4.01", "-//W3C//DTD HTML 4.01 Transitional//EN", + "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"}, + { "4.0strict", "-//W3C//DTD HTML 4.01//EN", + "http://www.w3.org/TR/html4/strict.dtd"}, + { "4.0trans", "-//W3C//DTD HTML 4.01 Transitional//EN", + "http://www.w3.org/TR/html4/loose.dtd"}, + { "4.0frame", "-//W3C//DTD HTML 4.01 Frameset//EN", + "http://www.w3.org/TR/html4/frameset.dtd"}, + { "4.0", "-//W3C//DTD HTML 4.01 Transitional//EN", + "http://www.w3.org/TR/html4/loose.dtd"}, + { "3.2", "-//W3C//DTD HTML 3.2//EN", NULL } +}; + +/** + * xsltGetHTMLIDs: + * @version: the version string + * @publicID: used to return the public ID + * @systemID: used to return the system ID + * + * Returns -1 if not found, 0 otherwise and the system and public + * Identifier for this given verion of HTML + */ +static int +xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID, + const xmlChar **systemID) { + unsigned int i; + if (version == NULL) + return(-1); + for (i = 0;i < (sizeof(xsltHTMLVersions)/sizeof(xsltHTMLVersions[1])); + i++) { + if (!xmlStrcasecmp(version, + (const xmlChar *) xsltHTMLVersions[i].version)) { + if (publicID != NULL) + *publicID = (const xmlChar *) xsltHTMLVersions[i].public; + if (systemID != NULL) + *systemID = (const xmlChar *) xsltHTMLVersions[i].system; + return(0); + } + } + return(-1); +} +#endif + +/** + * xsltApplyStripSpaces: + * @ctxt: a XSLT process context + * @node: the root of the XML tree + * + * Strip the unwanted ignorable spaces from the input tree + */ +void +xsltApplyStripSpaces(xsltTransformContextPtr ctxt, xmlNodePtr node) { + xmlNodePtr current; +#ifdef WITH_XSLT_DEBUG_PROCESS + int nb = 0; +#endif + + + current = node; + while (current != NULL) { + /* + * Cleanup children empty nodes if asked for + */ + if ((IS_XSLT_REAL_NODE(current)) && + (current->children != NULL) && + (xsltFindElemSpaceHandling(ctxt, current))) { + xmlNodePtr delete = NULL, cur = current->children; + + while (cur != NULL) { + if (IS_BLANK_NODE(cur)) + delete = cur; + + cur = cur->next; + if (delete != NULL) { + xmlUnlinkNode(delete); + xmlFreeNode(delete); + delete = NULL; +#ifdef WITH_XSLT_DEBUG_PROCESS + nb++; +#endif + } + } + } + + /* + * Skip to next node in document order. + */ + if (node->type == XML_ENTITY_REF_NODE) { + /* process deep in entities */ + xsltApplyStripSpaces(ctxt, node->children); + } + if ((current->children != NULL) && + (current->type != XML_ENTITY_REF_NODE)) { + current = current->children; + } else if (current->next != NULL) { + current = current->next; + } else { + do { + current = current->parent; + if (current == NULL) + break; + if (current == node) + goto done; + if (current->next != NULL) { + current = current->next; + break; + } + } while (current != NULL); + } + } + +done: +#ifdef WITH_XSLT_DEBUG_PROCESS + XSLT_TRACE(ctxt,XSLT_TRACE_STRIP_SPACES,xsltGenericDebug(xsltGenericDebugContext, + "xsltApplyStripSpaces: removed %d ignorable blank node\n", nb)); +#endif + return; +} + +static int +xsltCountKeys(xsltTransformContextPtr ctxt) +{ + xsltStylesheetPtr style; + xsltKeyDefPtr keyd; + + if (ctxt == NULL) + return(-1); + + /* + * Do we have those nastly templates with a key() in the match pattern? + */ + ctxt->hasTemplKeyPatterns = 0; + style = ctxt->style; + while (style != NULL) { + if (style->keyMatch != NULL) { + ctxt->hasTemplKeyPatterns = 1; + break; + } + style = xsltNextImport(style); + } + /* + * Count number of key declarations. + */ + ctxt->nbKeys = 0; + style = ctxt->style; + while (style != NULL) { + keyd = style->keys; + while (keyd) { + ctxt->nbKeys++; + keyd = keyd->next; + } + style = xsltNextImport(style); + } + return(ctxt->nbKeys); +} + +/** + * xsltApplyStylesheetInternal: + * @style: a parsed XSLT stylesheet + * @doc: a parsed XML document + * @params: a NULL terminated array of parameters names/values tuples + * @output: the targetted output + * @profile: profile FILE * output or NULL + * @user: user provided parameter + * + * Apply the stylesheet to the document + * NOTE: This may lead to a non-wellformed output XML wise ! + * + * Returns the result document or NULL in case of error + */ +static xmlDocPtr +xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc, + const char **params, const char *output, + FILE * profile, xsltTransformContextPtr userCtxt) +{ + xmlDocPtr res = NULL; + xsltTransformContextPtr ctxt = NULL; + xmlNodePtr root, node; + const xmlChar *method; + const xmlChar *doctypePublic; + const xmlChar *doctypeSystem; + const xmlChar *version; + const xmlChar *encoding; + xsltStackElemPtr variables; + xsltStackElemPtr vptr; + + xsltInitGlobals(); + + if ((style == NULL) || (doc == NULL)) + return (NULL); + + if (style->internalized == 0) { +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "Stylesheet was not fully internalized !\n"); +#endif + } + if (doc->intSubset != NULL) { + /* + * Avoid hitting the DTD when scanning nodes + * but keep it linked as doc->intSubset + */ + xmlNodePtr cur = (xmlNodePtr) doc->intSubset; + if (cur->next != NULL) + cur->next->prev = cur->prev; + if (cur->prev != NULL) + cur->prev->next = cur->next; + if (doc->children == cur) + doc->children = cur->next; + if (doc->last == cur) + doc->last = cur->prev; + cur->prev = cur->next = NULL; + } + + /* + * Check for XPath document order availability + */ + root = xmlDocGetRootElement(doc); + if (root != NULL) { + if (((long) root->content) >= 0 && (xslDebugStatus == XSLT_DEBUG_NONE)) + xmlXPathOrderDocElems(doc); + } + + if (userCtxt != NULL) + ctxt = userCtxt; + else + ctxt = xsltNewTransformContext(style, doc); + + if (ctxt == NULL) + return (NULL); + + ctxt->initialContextDoc = doc; + ctxt->initialContextNode = (xmlNodePtr) doc; + + if (profile != NULL) + ctxt->profile = 1; + + if (output != NULL) + ctxt->outputFile = output; + else + ctxt->outputFile = NULL; + + /* + * internalize the modes if needed + */ + if (ctxt->dict != NULL) { + if (ctxt->mode != NULL) + ctxt->mode = xmlDictLookup(ctxt->dict, ctxt->mode, -1); + if (ctxt->modeURI != NULL) + ctxt->modeURI = xmlDictLookup(ctxt->dict, ctxt->modeURI, -1); + } + + XSLT_GET_IMPORT_PTR(method, style, method) + XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) + XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) + XSLT_GET_IMPORT_PTR(version, style, version) + XSLT_GET_IMPORT_PTR(encoding, style, encoding) + + if ((method != NULL) && + (!xmlStrEqual(method, (const xmlChar *) "xml"))) + { + if (xmlStrEqual(method, (const xmlChar *) "html")) { + ctxt->type = XSLT_OUTPUT_HTML; + if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { + res = htmlNewDoc(doctypeSystem, doctypePublic); + } else { + if (version == NULL) { + xmlDtdPtr dtd; + + res = htmlNewDoc(NULL, NULL); + /* + * Make sure no DTD node is generated in this case + */ + if (res != NULL) { + dtd = xmlGetIntSubset(res); + if (dtd != NULL) { + xmlUnlinkNode((xmlNodePtr) dtd); + xmlFreeDtd(dtd); + } + res->intSubset = NULL; + res->extSubset = NULL; + } + } else { + +#ifdef XSLT_GENERATE_HTML_DOCTYPE + xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem); +#endif + res = htmlNewDoc(doctypeSystem, doctypePublic); + } + } + if (res == NULL) + goto error; + res->dict = ctxt->dict; + xmlDictReference(res->dict); + +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing transformation dict for output\n"); +#endif + } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) { + xsltTransformError(ctxt, NULL, (xmlNodePtr) doc, + "xsltApplyStylesheetInternal: unsupported method xhtml, using html\n"); + ctxt->type = XSLT_OUTPUT_HTML; + res = htmlNewDoc(doctypeSystem, doctypePublic); + if (res == NULL) + goto error; + res->dict = ctxt->dict; + xmlDictReference(res->dict); + +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing transformation dict for output\n"); +#endif + } else if (xmlStrEqual(method, (const xmlChar *) "text")) { + ctxt->type = XSLT_OUTPUT_TEXT; + res = xmlNewDoc(style->version); + if (res == NULL) + goto error; + res->dict = ctxt->dict; + xmlDictReference(res->dict); + +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing transformation dict for output\n"); +#endif + } else { + xsltTransformError(ctxt, NULL, (xmlNodePtr) doc, + "xsltApplyStylesheetInternal: unsupported method (%s)\n", + method); + goto error; + } + } else { + ctxt->type = XSLT_OUTPUT_XML; + res = xmlNewDoc(style->version); + if (res == NULL) + goto error; + res->dict = ctxt->dict; + xmlDictReference(ctxt->dict); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing transformation dict for output\n"); +#endif + } + res->charset = XML_CHAR_ENCODING_UTF8; + if (encoding != NULL) + res->encoding = xmlStrdup(encoding); + variables = style->variables; + + /* + * Start the evaluation, evaluate the params, the stylesheets globals + * and start by processing the top node. + */ + if (xsltNeedElemSpaceHandling(ctxt)) + xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc)); + /* + * Evaluate global params and user-provided params. + */ + ctxt->node = (xmlNodePtr) doc; + if (ctxt->globalVars == NULL) + ctxt->globalVars = xmlHashCreate(20); + if (params != NULL) { + xsltEvalUserParams(ctxt, params); + } + + /* need to be called before evaluating global variables */ + xsltCountKeys(ctxt); + + xsltEvalGlobalVariables(ctxt); + + /* Clean up any unused RVTs. */ + xsltReleaseLocalRVTs(ctxt, NULL); + + ctxt->node = (xmlNodePtr) doc; + ctxt->output = res; + ctxt->insert = (xmlNodePtr) res; + ctxt->varsBase = ctxt->varsNr - 1; + + ctxt->xpathCtxt->contextSize = 1; + ctxt->xpathCtxt->proximityPosition = 1; + ctxt->xpathCtxt->node = NULL; /* TODO: Set the context node here? */ + /* + * Start processing the source tree ----------------------------------- + */ + xsltProcessOneNode(ctxt, ctxt->node, NULL); + /* + * Remove all remaining vars from the stack. + */ + xsltLocalVariablePop(ctxt, 0, -2); + xsltShutdownCtxtExts(ctxt); + + xsltCleanupTemplates(style); /* TODO: <- style should be read only */ + + /* + * Now cleanup our variables so stylesheet can be re-used + * + * TODO: this is not needed anymore global variables are copied + * and not evaluated directly anymore, keep this as a check + */ + if (style->variables != variables) { + vptr = style->variables; + while (vptr->next != variables) + vptr = vptr->next; + vptr->next = NULL; + xsltFreeStackElemList(style->variables); + style->variables = variables; + } + vptr = style->variables; + while (vptr != NULL) { + if (vptr->computed) { + if (vptr->value != NULL) { + xmlXPathFreeObject(vptr->value); + vptr->value = NULL; + vptr->computed = 0; + } + } + vptr = vptr->next; + } +#if 0 + /* + * code disabled by wmb; awaiting kb's review + * problem is that global variable(s) may contain xpath objects + * from doc associated with RVT, so can't be freed at this point. + * xsltFreeTransformContext includes a call to xsltFreeRVTs, so + * I assume this shouldn't be required at this point. + */ + /* + * Free all remaining tree fragments. + */ + xsltFreeRVTs(ctxt); +#endif + /* + * Do some post processing work depending on the generated output + */ + root = xmlDocGetRootElement(res); + if (root != NULL) { + const xmlChar *doctype = NULL; + + if ((root->ns != NULL) && (root->ns->prefix != NULL)) + doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name); + if (doctype == NULL) + doctype = root->name; + + /* + * Apply the default selection of the method + */ + if ((method == NULL) && + (root->ns == NULL) && + (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) { + xmlNodePtr tmp; + + tmp = res->children; + while ((tmp != NULL) && (tmp != root)) { + if (tmp->type == XML_ELEMENT_NODE) + break; + if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp))) + break; + tmp = tmp->next; + } + if (tmp == root) { + ctxt->type = XSLT_OUTPUT_HTML; + /* + * REVISIT TODO: XML_HTML_DOCUMENT_NODE is set after the + * transformation on the doc, but functions like + */ + res->type = XML_HTML_DOCUMENT_NODE; + if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { + res->intSubset = xmlCreateIntSubset(res, doctype, + doctypePublic, + doctypeSystem); +#ifdef XSLT_GENERATE_HTML_DOCTYPE + } else if (version != NULL) { + xsltGetHTMLIDs(version, &doctypePublic, + &doctypeSystem); + if (((doctypePublic != NULL) || (doctypeSystem != NULL))) + res->intSubset = + xmlCreateIntSubset(res, doctype, + doctypePublic, + doctypeSystem); +#endif + } + } + + } + if (ctxt->type == XSLT_OUTPUT_XML) { + XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic) + XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem) + if (((doctypePublic != NULL) || (doctypeSystem != NULL))) { + xmlNodePtr last; + /* Need a small "hack" here to assure DTD comes before + possible comment nodes */ + node = res->children; + last = res->last; + res->children = NULL; + res->last = NULL; + res->intSubset = xmlCreateIntSubset(res, doctype, + doctypePublic, + doctypeSystem); + if (res->children != NULL) { + res->children->next = node; + node->prev = res->children; + res->last = last; + } else { + res->children = node; + res->last = last; + } + } + } + } + xmlXPathFreeNodeSet(ctxt->nodeList); + if (profile != NULL) { + xsltSaveProfiling(ctxt, profile); + } + + /* + * Be pedantic. + */ + if ((ctxt != NULL) && (ctxt->state != XSLT_STATE_OK)) { + xmlFreeDoc(res); + res = NULL; + } + if ((res != NULL) && (ctxt != NULL) && (output != NULL)) { + int ret; + + ret = xsltCheckWrite(ctxt->sec, ctxt, (const xmlChar *) output); + if (ret == 0) { + xsltTransformError(ctxt, NULL, NULL, + "xsltApplyStylesheet: forbidden to save to %s\n", + output); + } else if (ret < 0) { + xsltTransformError(ctxt, NULL, NULL, + "xsltApplyStylesheet: saving to %s may not be possible\n", + output); + } + } + +#ifdef XSLT_DEBUG_PROFILE_CACHE + printf("# Cache:\n"); + printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs); + printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars); +#endif + + if ((ctxt != NULL) && (userCtxt == NULL)) + xsltFreeTransformContext(ctxt); + + return (res); + +error: + if (res != NULL) + xmlFreeDoc(res); + +#ifdef XSLT_DEBUG_PROFILE_CACHE + printf("# Cache:\n"); + printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs); + printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars); +#endif + + if ((ctxt != NULL) && (userCtxt == NULL)) + xsltFreeTransformContext(ctxt); + return (NULL); +} + +/** + * xsltApplyStylesheet: + * @style: a parsed XSLT stylesheet + * @doc: a parsed XML document + * @params: a NULL terminated arry of parameters names/values tuples + * + * Apply the stylesheet to the document + * NOTE: This may lead to a non-wellformed output XML wise ! + * + * Returns the result document or NULL in case of error + */ +xmlDocPtr +xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, + const char **params) +{ + return (xsltApplyStylesheetInternal(style, doc, params, NULL, NULL, NULL)); +} + +/** + * xsltProfileStylesheet: + * @style: a parsed XSLT stylesheet + * @doc: a parsed XML document + * @params: a NULL terminated arry of parameters names/values tuples + * @output: a FILE * for the profiling output + * + * Apply the stylesheet to the document and dump the profiling to + * the given output. + * + * Returns the result document or NULL in case of error + */ +xmlDocPtr +xsltProfileStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, + const char **params, FILE * output) +{ + xmlDocPtr res; + + res = xsltApplyStylesheetInternal(style, doc, params, NULL, output, NULL); + return (res); +} + +/** + * xsltApplyStylesheetUser: + * @style: a parsed XSLT stylesheet + * @doc: a parsed XML document + * @params: a NULL terminated array of parameters names/values tuples + * @output: the targetted output + * @profile: profile FILE * output or NULL + * @userCtxt: user provided transform context + * + * Apply the stylesheet to the document and allow the user to provide + * its own transformation context. + * + * Returns the result document or NULL in case of error + */ +xmlDocPtr +xsltApplyStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc, + const char **params, const char *output, + FILE * profile, xsltTransformContextPtr userCtxt) +{ + xmlDocPtr res; + + res = xsltApplyStylesheetInternal(style, doc, params, output, + profile, userCtxt); + return (res); +} + +/** + * xsltRunStylesheetUser: + * @style: a parsed XSLT stylesheet + * @doc: a parsed XML document + * @params: a NULL terminated array of parameters names/values tuples + * @output: the URL/filename ot the generated resource if available + * @SAX: a SAX handler for progressive callback output (not implemented yet) + * @IObuf: an output buffer for progressive output (not implemented yet) + * @profile: profile FILE * output or NULL + * @userCtxt: user provided transform context + * + * Apply the stylesheet to the document and generate the output according + * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf. + * + * NOTE: This may lead to a non-wellformed output XML wise ! + * NOTE: This may also result in multiple files being generated + * NOTE: using IObuf, the result encoding used will be the one used for + * creating the output buffer, use the following macro to read it + * from the stylesheet + * XSLT_GET_IMPORT_PTR(encoding, style, encoding) + * NOTE: using SAX, any encoding specified in the stylesheet will be lost + * since the interface uses only UTF8 + * + * Returns the number of by written to the main resource or -1 in case of + * error. + */ +int +xsltRunStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc, + const char **params, const char *output, + xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf, + FILE * profile, xsltTransformContextPtr userCtxt) +{ + xmlDocPtr tmp; + int ret; + + if ((output == NULL) && (SAX == NULL) && (IObuf == NULL)) + return (-1); + if ((SAX != NULL) && (IObuf != NULL)) + return (-1); + + /* unsupported yet */ + if (SAX != NULL) { + XSLT_TODO /* xsltRunStylesheet xmlSAXHandlerPtr SAX */ + return (-1); + } + + tmp = xsltApplyStylesheetInternal(style, doc, params, output, profile, + userCtxt); + if (tmp == NULL) { + xsltTransformError(NULL, NULL, (xmlNodePtr) doc, + "xsltRunStylesheet : run failed\n"); + return (-1); + } + if (IObuf != NULL) { + /* TODO: incomplete, IObuf output not progressive */ + ret = xsltSaveResultTo(IObuf, tmp, style); + } else { + ret = xsltSaveResultToFilename(output, tmp, style, 0); + } + xmlFreeDoc(tmp); + return (ret); +} + +/** + * xsltRunStylesheet: + * @style: a parsed XSLT stylesheet + * @doc: a parsed XML document + * @params: a NULL terminated array of parameters names/values tuples + * @output: the URL/filename ot the generated resource if available + * @SAX: a SAX handler for progressive callback output (not implemented yet) + * @IObuf: an output buffer for progressive output (not implemented yet) + * + * Apply the stylesheet to the document and generate the output according + * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf. + * + * NOTE: This may lead to a non-wellformed output XML wise ! + * NOTE: This may also result in multiple files being generated + * NOTE: using IObuf, the result encoding used will be the one used for + * creating the output buffer, use the following macro to read it + * from the stylesheet + * XSLT_GET_IMPORT_PTR(encoding, style, encoding) + * NOTE: using SAX, any encoding specified in the stylesheet will be lost + * since the interface uses only UTF8 + * + * Returns the number of bytes written to the main resource or -1 in case of + * error. + */ +int +xsltRunStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, + const char **params, const char *output, + xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf) +{ + return(xsltRunStylesheetUser(style, doc, params, output, SAX, IObuf, + NULL, NULL)); +} + +/** + * xsltRegisterAllElement: + * @ctxt: the XPath context + * + * Registers all default XSLT elements in this context + */ +void +xsltRegisterAllElement(xsltTransformContextPtr ctxt) +{ + xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-templates", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltApplyTemplates); + xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-imports", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltApplyImports); + xsltRegisterExtElement(ctxt, (const xmlChar *) "call-template", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltCallTemplate); + xsltRegisterExtElement(ctxt, (const xmlChar *) "element", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltElement); + xsltRegisterExtElement(ctxt, (const xmlChar *) "attribute", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltAttribute); + xsltRegisterExtElement(ctxt, (const xmlChar *) "text", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltText); + xsltRegisterExtElement(ctxt, (const xmlChar *) "processing-instruction", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltProcessingInstruction); + xsltRegisterExtElement(ctxt, (const xmlChar *) "comment", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltComment); + xsltRegisterExtElement(ctxt, (const xmlChar *) "copy", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltCopy); + xsltRegisterExtElement(ctxt, (const xmlChar *) "value-of", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltValueOf); + xsltRegisterExtElement(ctxt, (const xmlChar *) "number", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltNumber); + xsltRegisterExtElement(ctxt, (const xmlChar *) "for-each", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltForEach); + xsltRegisterExtElement(ctxt, (const xmlChar *) "if", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltIf); + xsltRegisterExtElement(ctxt, (const xmlChar *) "choose", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltChoose); + xsltRegisterExtElement(ctxt, (const xmlChar *) "sort", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltSort); + xsltRegisterExtElement(ctxt, (const xmlChar *) "copy-of", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltCopyOf); + xsltRegisterExtElement(ctxt, (const xmlChar *) "message", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltMessage); + + /* + * Those don't have callable entry points but are registered anyway + */ + xsltRegisterExtElement(ctxt, (const xmlChar *) "variable", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltDebug); + xsltRegisterExtElement(ctxt, (const xmlChar *) "param", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltDebug); + xsltRegisterExtElement(ctxt, (const xmlChar *) "with-param", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltDebug); + xsltRegisterExtElement(ctxt, (const xmlChar *) "decimal-format", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltDebug); + xsltRegisterExtElement(ctxt, (const xmlChar *) "when", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltDebug); + xsltRegisterExtElement(ctxt, (const xmlChar *) "otherwise", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltDebug); + xsltRegisterExtElement(ctxt, (const xmlChar *) "fallback", + XSLT_NAMESPACE, + (xsltTransformFunction) xsltDebug); + +} diff --git a/libxslt/transform.h b/libxslt/transform.h new file mode 100644 index 0000000..2cfbec2 --- /dev/null +++ b/libxslt/transform.h @@ -0,0 +1,207 @@ +/* + * Summary: the XSLT engine transformation part. + * Description: This module implements the bulk of the actual + * transformation processing. Most of the xsl: element + * constructs are implemented in this module. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_TRANSFORM_H__ +#define __XML_XSLT_TRANSFORM_H__ + +#include <libxml/parser.h> +#include <libxml/xmlIO.h> +#include "xsltexports.h" +#include <libxslt/xsltInternals.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * XInclude default processing. + */ +XSLTPUBFUN void XSLTCALL + xsltSetXIncludeDefault (int xinclude); +XSLTPUBFUN int XSLTCALL + xsltGetXIncludeDefault (void); + +/** + * Export context to users. + */ +XSLTPUBFUN xsltTransformContextPtr XSLTCALL + xsltNewTransformContext (xsltStylesheetPtr style, + xmlDocPtr doc); + +XSLTPUBFUN void XSLTCALL + xsltFreeTransformContext(xsltTransformContextPtr ctxt); + +XSLTPUBFUN xmlDocPtr XSLTCALL + xsltApplyStylesheetUser (xsltStylesheetPtr style, + xmlDocPtr doc, + const char **params, + const char *output, + FILE * profile, + xsltTransformContextPtr userCtxt); +XSLTPUBFUN void XSLTCALL + xsltProcessOneNode (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xsltStackElemPtr params); +/** + * Private Interfaces. + */ +XSLTPUBFUN void XSLTCALL + xsltApplyStripSpaces (xsltTransformContextPtr ctxt, + xmlNodePtr node); +XSLTPUBFUN xmlDocPtr XSLTCALL + xsltApplyStylesheet (xsltStylesheetPtr style, + xmlDocPtr doc, + const char **params); +XSLTPUBFUN xmlDocPtr XSLTCALL + xsltProfileStylesheet (xsltStylesheetPtr style, + xmlDocPtr doc, + const char **params, + FILE * output); +XSLTPUBFUN int XSLTCALL + xsltRunStylesheet (xsltStylesheetPtr style, + xmlDocPtr doc, + const char **params, + const char *output, + xmlSAXHandlerPtr SAX, + xmlOutputBufferPtr IObuf); +XSLTPUBFUN int XSLTCALL + xsltRunStylesheetUser (xsltStylesheetPtr style, + xmlDocPtr doc, + const char **params, + const char *output, + xmlSAXHandlerPtr SAX, + xmlOutputBufferPtr IObuf, + FILE * profile, + xsltTransformContextPtr userCtxt); +XSLTPUBFUN void XSLTCALL + xsltApplyOneTemplate (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr list, + xsltTemplatePtr templ, + xsltStackElemPtr params); +XSLTPUBFUN void XSLTCALL + xsltDocumentElem (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltSort (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltCopy (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltText (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltElement (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltComment (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltAttribute (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltProcessingInstruction(xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltCopyOf (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltValueOf (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltNumber (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltApplyImports (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltCallTemplate (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltApplyTemplates (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltChoose (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltIf (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltForEach (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltStylePreCompPtr comp); +XSLTPUBFUN void XSLTCALL + xsltRegisterAllElement (xsltTransformContextPtr ctxt); + +XSLTPUBFUN xmlNodePtr XSLTCALL + xsltCopyTextString (xsltTransformContextPtr ctxt, + xmlNodePtr target, + const xmlChar *string, + int noescape); + +/* Following 2 functions needed for libexslt/functions.c */ +XSLTPUBFUN void XSLTCALL + xsltLocalVariablePop (xsltTransformContextPtr ctxt, + int limitNr, + int level); +XSLTPUBFUN int XSLTCALL + xsltLocalVariablePush (xsltTransformContextPtr ctxt, + xsltStackElemPtr variable, + int level); +/* + * Hook for the debugger if activated. + */ +XSLTPUBFUN void XSLTCALL + xslHandleDebugger (xmlNodePtr cur, + xmlNodePtr node, + xsltTemplatePtr templ, + xsltTransformContextPtr ctxt); + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_TRANSFORM_H__ */ + diff --git a/libxslt/trio.h b/libxslt/trio.h new file mode 100644 index 0000000..941bdd0 --- /dev/null +++ b/libxslt/trio.h @@ -0,0 +1,216 @@ +/************************************************************************* + * + * $Id$ + * + * Copyright (C) 1998 Bjorn Reese and Daniel Stenberg. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************* + * + * http://ctrio.sourceforge.net/ + * + ************************************************************************/ + +#ifndef TRIO_TRIO_H +#define TRIO_TRIO_H + +#if !defined(WITHOUT_TRIO) + +/* + * Use autoconf defines if present. Packages using trio must define + * HAVE_CONFIG_H as a compiler option themselves. + */ +#if defined(HAVE_CONFIG_H) +# include <config.h> +#endif + +#include "triodef.h" + +#include <stdio.h> +#include <stdlib.h> +#if defined(TRIO_COMPILER_ANCIENT) +# include <varargs.h> +#else +# include <stdarg.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Error codes. + * + * Remember to add a textual description to trio_strerror. + */ +enum { + TRIO_EOF = 1, + TRIO_EINVAL = 2, + TRIO_ETOOMANY = 3, + TRIO_EDBLREF = 4, + TRIO_EGAP = 5, + TRIO_ENOMEM = 6, + TRIO_ERANGE = 7, + TRIO_ERRNO = 8, + TRIO_ECUSTOM = 9 +}; + +/* Error macros */ +#define TRIO_ERROR_CODE(x) ((-(x)) & 0x00FF) +#define TRIO_ERROR_POSITION(x) ((-(x)) >> 8) +#define TRIO_ERROR_NAME(x) trio_strerror(x) + +typedef int (*trio_outstream_t) TRIO_PROTO((trio_pointer_t, int)); +typedef int (*trio_instream_t) TRIO_PROTO((trio_pointer_t)); + +TRIO_CONST char *trio_strerror TRIO_PROTO((int)); + +/************************************************************************* + * Print Functions + */ + +int trio_printf TRIO_PROTO((TRIO_CONST char *format, ...)); +int trio_vprintf TRIO_PROTO((TRIO_CONST char *format, va_list args)); +int trio_printfv TRIO_PROTO((TRIO_CONST char *format, void **args)); + +int trio_fprintf TRIO_PROTO((FILE *file, TRIO_CONST char *format, ...)); +int trio_vfprintf TRIO_PROTO((FILE *file, TRIO_CONST char *format, va_list args)); +int trio_fprintfv TRIO_PROTO((FILE *file, TRIO_CONST char *format, void **args)); + +int trio_dprintf TRIO_PROTO((int fd, TRIO_CONST char *format, ...)); +int trio_vdprintf TRIO_PROTO((int fd, TRIO_CONST char *format, va_list args)); +int trio_dprintfv TRIO_PROTO((int fd, TRIO_CONST char *format, void **args)); + +int trio_cprintf TRIO_PROTO((trio_outstream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, ...)); +int trio_vcprintf TRIO_PROTO((trio_outstream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, va_list args)); +int trio_cprintfv TRIO_PROTO((trio_outstream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, void **args)); + +int trio_sprintf TRIO_PROTO((char *buffer, TRIO_CONST char *format, ...)); +int trio_vsprintf TRIO_PROTO((char *buffer, TRIO_CONST char *format, va_list args)); +int trio_sprintfv TRIO_PROTO((char *buffer, TRIO_CONST char *format, void **args)); + +int trio_snprintf TRIO_PROTO((char *buffer, size_t max, TRIO_CONST char *format, ...)); +int trio_vsnprintf TRIO_PROTO((char *buffer, size_t bufferSize, TRIO_CONST char *format, + va_list args)); +int trio_snprintfv TRIO_PROTO((char *buffer, size_t bufferSize, TRIO_CONST char *format, + void **args)); + +int trio_snprintfcat TRIO_PROTO((char *buffer, size_t max, TRIO_CONST char *format, ...)); +int trio_vsnprintfcat TRIO_PROTO((char *buffer, size_t bufferSize, TRIO_CONST char *format, + va_list args)); + +char *trio_aprintf TRIO_PROTO((TRIO_CONST char *format, ...)); +char *trio_vaprintf TRIO_PROTO((TRIO_CONST char *format, va_list args)); + +int trio_asprintf TRIO_PROTO((char **ret, TRIO_CONST char *format, ...)); +int trio_vasprintf TRIO_PROTO((char **ret, TRIO_CONST char *format, va_list args)); + +/************************************************************************* + * Scan Functions + */ +int trio_scanf TRIO_PROTO((TRIO_CONST char *format, ...)); +int trio_vscanf TRIO_PROTO((TRIO_CONST char *format, va_list args)); +int trio_scanfv TRIO_PROTO((TRIO_CONST char *format, void **args)); + +int trio_fscanf TRIO_PROTO((FILE *file, TRIO_CONST char *format, ...)); +int trio_vfscanf TRIO_PROTO((FILE *file, TRIO_CONST char *format, va_list args)); +int trio_fscanfv TRIO_PROTO((FILE *file, TRIO_CONST char *format, void **args)); + +int trio_dscanf TRIO_PROTO((int fd, TRIO_CONST char *format, ...)); +int trio_vdscanf TRIO_PROTO((int fd, TRIO_CONST char *format, va_list args)); +int trio_dscanfv TRIO_PROTO((int fd, TRIO_CONST char *format, void **args)); + +int trio_cscanf TRIO_PROTO((trio_instream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, ...)); +int trio_vcscanf TRIO_PROTO((trio_instream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, va_list args)); +int trio_cscanfv TRIO_PROTO((trio_instream_t stream, trio_pointer_t closure, + TRIO_CONST char *format, void **args)); + +int trio_sscanf TRIO_PROTO((TRIO_CONST char *buffer, TRIO_CONST char *format, ...)); +int trio_vsscanf TRIO_PROTO((TRIO_CONST char *buffer, TRIO_CONST char *format, va_list args)); +int trio_sscanfv TRIO_PROTO((TRIO_CONST char *buffer, TRIO_CONST char *format, void **args)); + +/************************************************************************* + * Locale Functions + */ +void trio_locale_set_decimal_point TRIO_PROTO((char *decimalPoint)); +void trio_locale_set_thousand_separator TRIO_PROTO((char *thousandSeparator)); +void trio_locale_set_grouping TRIO_PROTO((char *grouping)); + +/************************************************************************* + * Renaming + */ +#ifdef TRIO_REPLACE_STDIO +/* Replace the <stdio.h> functions */ +#ifndef HAVE_PRINTF +# define printf trio_printf +#endif +#ifndef HAVE_VPRINTF +# define vprintf trio_vprintf +#endif +#ifndef HAVE_FPRINTF +# define fprintf trio_fprintf +#endif +#ifndef HAVE_VFPRINTF +# define vfprintf trio_vfprintf +#endif +#ifndef HAVE_SPRINTF +# define sprintf trio_sprintf +#endif +#ifndef HAVE_VSPRINTF +# define vsprintf trio_vsprintf +#endif +#ifndef HAVE_SNPRINTF +# define snprintf trio_snprintf +#endif +#ifndef HAVE_VSNPRINTF +# define vsnprintf trio_vsnprintf +#endif +#ifndef HAVE_SCANF +# define scanf trio_scanf +#endif +#ifndef HAVE_VSCANF +# define vscanf trio_vscanf +#endif +#ifndef HAVE_FSCANF +# define fscanf trio_fscanf +#endif +#ifndef HAVE_VFSCANF +# define vfscanf trio_vfscanf +#endif +#ifndef HAVE_SSCANF +# define sscanf trio_sscanf +#endif +#ifndef HAVE_VSSCANF +# define vsscanf trio_vsscanf +#endif +/* These aren't stdio functions, but we make them look similar */ +#define dprintf trio_dprintf +#define vdprintf trio_vdprintf +#define aprintf trio_aprintf +#define vaprintf trio_vaprintf +#define asprintf trio_asprintf +#define vasprintf trio_vasprintf +#define dscanf trio_dscanf +#define vdscanf trio_vdscanf +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* WITHOUT_TRIO */ + +#endif /* TRIO_TRIO_H */ diff --git a/libxslt/triodef.h b/libxslt/triodef.h new file mode 100644 index 0000000..0fd32fb --- /dev/null +++ b/libxslt/triodef.h @@ -0,0 +1,220 @@ +/************************************************************************* + * + * $Id$ + * + * Copyright (C) 2001 Bjorn Reese <breese@users.sourceforge.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************/ + +#ifndef TRIO_TRIODEF_H +#define TRIO_TRIODEF_H + +/************************************************************************* + * Platform and compiler support detection + */ +#if defined(__GNUC__) +# define TRIO_COMPILER_GCC +#elif defined(__SUNPRO_C) +# define TRIO_COMPILER_SUNPRO +#elif defined(__SUNPRO_CC) +# define TRIO_COMPILER_SUNPRO +# define __SUNPRO_C __SUNPRO_CC +#elif defined(__xlC__) || defined(__IBMC__) || defined(__IBMCPP__) +# define TRIO_COMPILER_XLC +#elif defined(_AIX) && !defined(__GNUC__) +# define TRIO_COMPILER_XLC /* Workaround for old xlc */ +#elif defined(__DECC) || defined(__DECCXX) +# define TRIO_COMPILER_DECC +#elif defined(__osf__) && defined(__LANGUAGE_C__) +# define TRIO_COMPILER_DECC /* Workaround for old DEC C compilers */ +#elif defined(_MSC_VER) +# define TRIO_COMPILER_MSVC +#elif defined(__BORLANDC__) +# define TRIO_COMPILER_BCB +#endif + +#if defined(VMS) || defined(__VMS) +/* + * VMS is placed first to avoid identifying the platform as Unix + * based on the DECC compiler later on. + */ +# define TRIO_PLATFORM_VMS +#elif defined(unix) || defined(__unix) || defined(__unix__) +# define TRIO_PLATFORM_UNIX +#elif defined(TRIO_COMPILER_XLC) || defined(_AIX) +# define TRIO_PLATFORM_UNIX +#elif defined(TRIO_COMPILER_DECC) || defined(__osf___) +# define TRIO_PLATFORM_UNIX +#elif defined(__NetBSD__) +# define TRIO_PLATFORM_UNIX +#elif defined(__QNX__) +# define TRIO_PLATFORM_UNIX +# define TRIO_PLATFORM_QNX +#elif defined(__CYGWIN__) +# define TRIO_PLATFORM_UNIX +#elif defined(AMIGA) && defined(TRIO_COMPILER_GCC) +# define TRIO_PLATFORM_UNIX +#elif defined(TRIO_COMPILER_MSVC) || defined(WIN32) || defined(_WIN32) +# define TRIO_PLATFORM_WIN32 +#elif defined(mpeix) || defined(__mpexl) +# define TRIO_PLATFORM_MPEIX +#endif + +#if defined(_AIX) +# define TRIO_PLATFORM_AIX +#elif defined(__hpux) +# define TRIO_PLATFORM_HPUX +#elif defined(sun) || defined(__sun__) +# if defined(__SVR4) || defined(__svr4__) +# define TRIO_PLATFORM_SOLARIS +# else +# define TRIO_PLATFORM_SUNOS +# endif +#endif + +#if defined(__STDC__) || defined(TRIO_COMPILER_MSVC) || defined(TRIO_COMPILER_BCB) +# define TRIO_COMPILER_SUPPORTS_C89 +# if defined(__STDC_VERSION__) +# define TRIO_COMPILER_SUPPORTS_C90 +# if (__STDC_VERSION__ >= 199409L) +# define TRIO_COMPILER_SUPPORTS_C94 +# endif +# if (__STDC_VERSION__ >= 199901L) +# define TRIO_COMPILER_SUPPORTS_C99 +# endif +# elif defined(TRIO_COMPILER_SUNPRO) +# if (__SUNPRO_C >= 0x420) +# define TRIO_COMPILER_SUPPORTS_C94 +# endif +# endif +#endif + +#if defined(_XOPEN_SOURCE) +# if defined(_XOPEN_SOURCE_EXTENDED) +# define TRIO_COMPILER_SUPPORTS_UNIX95 +# endif +# if (_XOPEN_VERSION >= 500) +# define TRIO_COMPILER_SUPPORTS_UNIX98 +# endif +# if (_XOPEN_VERSION >= 600) +# define TRIO_COMPILER_SUPPORTS_UNIX01 +# endif +#endif + +/************************************************************************* + * Generic defines + */ + +#if !defined(TRIO_PUBLIC) +# define TRIO_PUBLIC +#endif +#if !defined(TRIO_PRIVATE) +# define TRIO_PRIVATE static +#endif + +#if !(defined(TRIO_COMPILER_SUPPORTS_C89) || defined(__cplusplus)) +# define TRIO_COMPILER_ANCIENT +#endif + +#if defined(TRIO_COMPILER_ANCIENT) +# define TRIO_CONST +# define TRIO_VOLATILE +# define TRIO_SIGNED +typedef double trio_long_double_t; +typedef char * trio_pointer_t; +# define TRIO_SUFFIX_LONG(x) x +# define TRIO_PROTO(x) () +# define TRIO_NOARGS +# define TRIO_ARGS1(list,a1) list a1; +# define TRIO_ARGS2(list,a1,a2) list a1; a2; +# define TRIO_ARGS3(list,a1,a2,a3) list a1; a2; a3; +# define TRIO_ARGS4(list,a1,a2,a3,a4) list a1; a2; a3; a4; +# define TRIO_ARGS5(list,a1,a2,a3,a4,a5) list a1; a2; a3; a4; a5; +# define TRIO_ARGS6(list,a1,a2,a3,a4,a5,a6) list a1; a2; a3; a4; a5; a6; +# define TRIO_VARGS2(list,a1,a2) list a1; a2 +# define TRIO_VARGS3(list,a1,a2,a3) list a1; a2; a3 +# define TRIO_VARGS4(list,a1,a2,a3,a4) list a1; a2; a3; a4 +# define TRIO_VARGS5(list,a1,a2,a3,a4,a5) list a1; a2; a3; a4; a5 +# define TRIO_VA_DECL va_dcl +# define TRIO_VA_START(x,y) va_start(x) +# define TRIO_VA_END(x) va_end(x) +#else /* ANSI C */ +# define TRIO_CONST const +# define TRIO_VOLATILE volatile +# define TRIO_SIGNED signed +typedef long double trio_long_double_t; +typedef void * trio_pointer_t; +# define TRIO_SUFFIX_LONG(x) x ## L +# define TRIO_PROTO(x) x +# define TRIO_NOARGS void +# define TRIO_ARGS1(list,a1) (a1) +# define TRIO_ARGS2(list,a1,a2) (a1,a2) +# define TRIO_ARGS3(list,a1,a2,a3) (a1,a2,a3) +# define TRIO_ARGS4(list,a1,a2,a3,a4) (a1,a2,a3,a4) +# define TRIO_ARGS5(list,a1,a2,a3,a4,a5) (a1,a2,a3,a4,a5) +# define TRIO_ARGS6(list,a1,a2,a3,a4,a5,a6) (a1,a2,a3,a4,a5,a6) +# define TRIO_VARGS2 TRIO_ARGS2 +# define TRIO_VARGS3 TRIO_ARGS3 +# define TRIO_VARGS4 TRIO_ARGS4 +# define TRIO_VARGS5 TRIO_ARGS5 +# define TRIO_VA_DECL ... +# define TRIO_VA_START(x,y) va_start(x,y) +# define TRIO_VA_END(x) va_end(x) +#endif + +#if defined(TRIO_COMPILER_SUPPORTS_C99) || defined(__cplusplus) +# define TRIO_INLINE inline +#elif defined(TRIO_COMPILER_GCC) +# define TRIO_INLINE __inline__ +#elif defined(TRIO_COMPILER_MSVC) +# define TRIO_INLINE _inline +#elif defined(TRIO_COMPILER_BCB) +# define TRIO_INLINE __inline +#else +# define TRIO_INLINE +#endif + +/************************************************************************* + * Workarounds + */ + +#if defined(TRIO_PLATFORM_VMS) +/* + * Computations done with constants at compile time can trigger these + * even when compiling with IEEE enabled. + */ +# pragma message disable (UNDERFLOW, FLOATOVERFL) + +# if (__CRTL_VER < 80000000) +/* + * Although the compiler supports C99 language constructs, the C + * run-time library does not contain all C99 functions. + * + * This was the case for 70300022. Update the 80000000 value when + * it has been accurately determined what version of the library + * supports C99. + */ +# if defined(TRIO_COMPILER_SUPPORTS_C99) +# undef TRIO_COMPILER_SUPPORTS_C99 +# endif +# endif +#endif + +/* + * Not all preprocessors supports the LL token. + */ +#if defined(TRIO_COMPILER_BCB) +#else +# define TRIO_COMPILER_SUPPORTS_LL +#endif + +#endif /* TRIO_TRIODEF_H */ diff --git a/libxslt/variables.c b/libxslt/variables.c new file mode 100644 index 0000000..e1a80ee --- /dev/null +++ b/libxslt/variables.c @@ -0,0 +1,2365 @@ +/* + * variables.c: Implementation of the variable storage and lookup + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> + +#include <libxml/xmlmemory.h> +#include <libxml/tree.h> +#include <libxml/valid.h> +#include <libxml/hash.h> +#include <libxml/xmlerror.h> +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> +#include <libxml/parserInternals.h> +#include <libxml/dict.h> +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "variables.h" +#include "transform.h" +#include "imports.h" +#include "preproc.h" +#include "keys.h" + +#ifdef WITH_XSLT_DEBUG + #define WITH_XSLT_DEBUG_VARIABLE +#endif + +#ifdef XSLT_REFACTORED +const xmlChar *xsltDocFragFake = (const xmlChar *) " fake node libxslt"; +#endif + +const xmlChar *xsltComputingGlobalVarMarker = + (const xmlChar *) " var/param being computed"; + +#define XSLT_VAR_GLOBAL (1<<0) +#define XSLT_VAR_IN_SELECT (1<<1) +#define XSLT_TCTXT_VARIABLE(c) ((xsltStackElemPtr) (c)->contextVariable) + +/************************************************************************ + * * + * Result Value Tree (Result Tree Fragment) interfaces * + * * + ************************************************************************/ +/** + * xsltCreateRVT: + * @ctxt: an XSLT transformation context + * + * Creates a Result Value Tree + * (the XSLT 1.0 term for this is "Result Tree Fragment") + * + * Returns the result value tree or NULL in case of API or internal errors. + */ +xmlDocPtr +xsltCreateRVT(xsltTransformContextPtr ctxt) +{ + xmlDocPtr container; + + /* + * Question: Why is this function public? + * Answer: It is called by the EXSLT module. + */ + if (ctxt == NULL) + return(NULL); + + /* + * Reuse a RTF from the cache if available. + */ + if (ctxt->cache->RVT) { + container = ctxt->cache->RVT; + ctxt->cache->RVT = (xmlDocPtr) container->next; + /* clear the internal pointers */ + container->next = NULL; + container->prev = NULL; + if (ctxt->cache->nbRVT > 0) + ctxt->cache->nbRVT--; +#ifdef XSLT_DEBUG_PROFILE_CACHE + ctxt->cache->dbgReusedRVTs++; +#endif + return(container); + } + + container = xmlNewDoc(NULL); + if (container == NULL) + return(NULL); + container->dict = ctxt->dict; + xmlDictReference(container->dict); + XSLT_MARK_RES_TREE_FRAG(container); + container->doc = container; + container->parent = NULL; + return(container); +} + +/** + * xsltRegisterTmpRVT: + * @ctxt: an XSLT transformation context + * @RVT: a result value tree (Result Tree Fragment) + * + * Registers the result value tree (XSLT 1.0 term: Result Tree Fragment) + * in the garbage collector. + * The fragment will be freed at the exit of the currently + * instantiated xsl:template. + * Obsolete; this function might produce massive memory overhead, + * since the fragment is only freed when the current xsl:template + * exits. Use xsltRegisterLocalRVT() instead. + * + * Returns 0 in case of success and -1 in case of API or internal errors. + */ +int +xsltRegisterTmpRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) +{ + if ((ctxt == NULL) || (RVT == NULL)) + return(-1); + + RVT->prev = NULL; + RVT->psvi = XSLT_RVT_VARIABLE; + + /* + * We'll restrict the lifetime of user-created fragments + * insinde an xsl:variable and xsl:param to the lifetime of the + * var/param itself. + */ + if (ctxt->contextVariable != NULL) { + RVT->next = (xmlNodePtr) XSLT_TCTXT_VARIABLE(ctxt)->fragment; + XSLT_TCTXT_VARIABLE(ctxt)->fragment = RVT; + return(0); + } + + RVT->next = (xmlNodePtr) ctxt->tmpRVT; + if (ctxt->tmpRVT != NULL) + ctxt->tmpRVT->prev = (xmlNodePtr) RVT; + ctxt->tmpRVT = RVT; + return(0); +} + +/** + * xsltRegisterLocalRVT: + * @ctxt: an XSLT transformation context + * @RVT: a result value tree (Result Tree Fragment; xmlDocPtr) + * + * Registers a result value tree (XSLT 1.0 term: Result Tree Fragment) + * in the RVT garbage collector. + * The fragment will be freed when the instruction which created the + * fragment exits. + * + * Returns 0 in case of success and -1 in case of API or internal errors. + */ +int +xsltRegisterLocalRVT(xsltTransformContextPtr ctxt, + xmlDocPtr RVT) +{ + if ((ctxt == NULL) || (RVT == NULL)) + return(-1); + + RVT->prev = NULL; + + /* + * When evaluating "select" expressions of xsl:variable + * and xsl:param, we need to bind newly created tree fragments + * to the variable itself; otherwise the fragment will be + * freed before we leave the scope of a var. + */ + if ((ctxt->contextVariable != NULL) && + (XSLT_TCTXT_VARIABLE(ctxt)->flags & XSLT_VAR_IN_SELECT)) + { + RVT->psvi = XSLT_RVT_VARIABLE; + RVT->next = (xmlNodePtr) XSLT_TCTXT_VARIABLE(ctxt)->fragment; + XSLT_TCTXT_VARIABLE(ctxt)->fragment = RVT; + return(0); + } + /* + * Store the fragment in the scope of the current instruction. + * If not reference by a returning instruction (like EXSLT's function), + * then this fragment will be freed, when the instruction exits. + */ + RVT->psvi = XSLT_RVT_LOCAL; + RVT->next = (xmlNodePtr) ctxt->localRVT; + if (ctxt->localRVT != NULL) + ctxt->localRVT->prev = (xmlNodePtr) RVT; + ctxt->localRVT = RVT; + return(0); +} + +/** + * xsltExtensionInstructionResultFinalize: + * @ctxt: an XSLT transformation context + * + * Finalizes the data (e.g. result tree fragments) created + * within a value-returning process (e.g. EXSLT's function). + * Tree fragments marked as being returned by a function are + * set to normal state, which means that the fragment garbage + * collector will free them after the function-calling process exits. + * + * Returns 0 in case of success and -1 in case of API or internal errors. + * + * This function is unsupported in newer releases of libxslt. + */ +int +xsltExtensionInstructionResultFinalize(xsltTransformContextPtr ctxt) +{ + xmlGenericError(xmlGenericErrorContext, + "xsltExtensionInstructionResultFinalize is unsupported " + "in this release of libxslt.\n"); + return(-1); +} + +/** + * xsltExtensionInstructionResultRegister: + * @ctxt: an XSLT transformation context + * @obj: an XPath object to be inspected for result tree fragments + * + * Marks the result of a value-returning extension instruction + * in order to avoid it being garbage collected before the + * extension instruction exits. + * Note that one still has to additionally register any newly created + * tree fragments (via xsltCreateRVT()) with xsltRegisterLocalRVT(). + * + * Returns 0 in case of success and -1 in case of error. + * + * It isn't necessary to call this function in newer releases of + * libxslt. + */ +int +xsltExtensionInstructionResultRegister(xsltTransformContextPtr ctxt, + xmlXPathObjectPtr obj) +{ + return(0); +} + +/** + * xsltFlagRVTs: + * @ctxt: an XSLT transformation context + * @obj: an XPath object to be inspected for result tree fragments + * @val: the flag value + * + * Updates ownership information of RVTs in @obj according to @val. + * + * @val = XSLT_RVT_FUNC_RESULT for the result of an extension function, so its + * RVTs won't be destroyed after leaving the returning scope. + * @val = XSLT_RVT_LOCAL for the result of an extension function to reset + * the state of its RVTs after it was returned to a new scope. + * @val = XSLT_RVT_GLOBAL for parts of global variables. + * + * Returns 0 in case of success and -1 in case of error. + */ +int +xsltFlagRVTs(xsltTransformContextPtr ctxt, xmlXPathObjectPtr obj, void *val) { + int i; + xmlNodePtr cur; + xmlDocPtr doc; + + if ((ctxt == NULL) || (obj == NULL)) + return(-1); + + /* + * OPTIMIZE TODO: If no local variables/params and no local tree + * fragments were created, then we don't need to analyse the XPath + * objects for tree fragments. + */ + + if ((obj->type != XPATH_NODESET) && (obj->type != XPATH_XSLT_TREE)) + return(0); + if ((obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) + return(0); + + for (i = 0; i < obj->nodesetval->nodeNr; i++) { + cur = obj->nodesetval->nodeTab[i]; + if (cur->type == XML_NAMESPACE_DECL) { + /* + * The XPath module sets the owner element of a ns-node on + * the ns->next field. + */ + if ((((xmlNsPtr) cur)->next != NULL) && + (((xmlNsPtr) cur)->next->type == XML_ELEMENT_NODE)) + { + cur = (xmlNodePtr) ((xmlNsPtr) cur)->next; + doc = cur->doc; + } else { + xsltTransformError(ctxt, NULL, ctxt->inst, + "Internal error in xsltFlagRVTs(): " + "Cannot retrieve the doc of a namespace node.\n"); + return(-1); + } + } else { + doc = cur->doc; + } + if (doc == NULL) { + xsltTransformError(ctxt, NULL, ctxt->inst, + "Internal error in xsltFlagRVTs(): " + "Cannot retrieve the doc of a node.\n"); + return(-1); + } + if (doc->name && (doc->name[0] == ' ') && + doc->psvi != XSLT_RVT_GLOBAL) { + /* + * This is a result tree fragment. + * We store ownership information in the @psvi field. + * TODO: How do we know if this is a doc acquired via the + * document() function? + */ +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Flagging RVT %p: %p -> %p\n", doc, doc->psvi, val)); +#endif + + if (val == XSLT_RVT_LOCAL) { + if (doc->psvi != XSLT_RVT_FUNC_RESULT) { + xmlGenericError(xmlGenericErrorContext, + "xsltFlagRVTs: Invalid transition %p => LOCAL\n", + doc->psvi); + return(-1); + } + + xsltRegisterLocalRVT(ctxt, doc); + } else if (val == XSLT_RVT_GLOBAL) { + if (doc->psvi != XSLT_RVT_LOCAL) { + xmlGenericError(xmlGenericErrorContext, + "xsltFlagRVTs: Invalid transition %p => GLOBAL\n", + doc->psvi); + doc->psvi = XSLT_RVT_GLOBAL; + return(-1); + } + + /* Will be registered as persistant in xsltReleaseLocalRVTs. */ + doc->psvi = XSLT_RVT_GLOBAL; + } else if (val == XSLT_RVT_FUNC_RESULT) { + doc->psvi = val; + } + } + } + + return(0); +} + +/** + * xsltReleaseRVT: + * @ctxt: an XSLT transformation context + * @RVT: a result value tree (Result Tree Fragment) + * + * Either frees the RVT (which is an xmlDoc) or stores + * it in the context's cache for later reuse. + */ +void +xsltReleaseRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) +{ + if (RVT == NULL) + return; + + if (ctxt && (ctxt->cache->nbRVT < 40)) { + /* + * Store the Result Tree Fragment. + * Free the document info. + */ + if (RVT->_private != NULL) { + xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private); + xmlFree(RVT->_private); + RVT->_private = NULL; + } + /* + * Clear the document tree. + * REVISIT TODO: Do we expect ID/IDREF tables to be existent? + */ + if (RVT->children != NULL) { + xmlFreeNodeList(RVT->children); + RVT->children = NULL; + RVT->last = NULL; + } + if (RVT->ids != NULL) { + xmlFreeIDTable((xmlIDTablePtr) RVT->ids); + RVT->ids = NULL; + } + if (RVT->refs != NULL) { + xmlFreeRefTable((xmlRefTablePtr) RVT->refs); + RVT->refs = NULL; + } + + /* + * Reset the ownership information. + */ + RVT->psvi = NULL; + + RVT->next = (xmlNodePtr) ctxt->cache->RVT; + ctxt->cache->RVT = RVT; + + ctxt->cache->nbRVT++; + +#ifdef XSLT_DEBUG_PROFILE_CACHE + ctxt->cache->dbgCachedRVTs++; +#endif + return; + } + /* + * Free it. + */ + if (RVT->_private != NULL) { + xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private); + xmlFree(RVT->_private); + } + xmlFreeDoc(RVT); +} + +/** + * xsltRegisterPersistRVT: + * @ctxt: an XSLT transformation context + * @RVT: a result value tree (Result Tree Fragment) + * + * Register the result value tree (XSLT 1.0 term: Result Tree Fragment) + * in the fragment garbage collector. + * The fragment will be freed when the transformation context is + * freed. + * + * Returns 0 in case of success and -1 in case of error. + */ +int +xsltRegisterPersistRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) +{ + if ((ctxt == NULL) || (RVT == NULL)) return(-1); + + RVT->psvi = XSLT_RVT_GLOBAL; + RVT->prev = NULL; + RVT->next = (xmlNodePtr) ctxt->persistRVT; + if (ctxt->persistRVT != NULL) + ctxt->persistRVT->prev = (xmlNodePtr) RVT; + ctxt->persistRVT = RVT; + return(0); +} + +/** + * xsltFreeRVTs: + * @ctxt: an XSLT transformation context + * + * Frees all registered result value trees (Result Tree Fragments) + * of the transformation. Internal function; should not be called + * by user-code. + */ +void +xsltFreeRVTs(xsltTransformContextPtr ctxt) +{ + xmlDocPtr cur, next; + + if (ctxt == NULL) + return; + /* + * Local fragments. + */ + cur = ctxt->localRVT; + while (cur != NULL) { + next = (xmlDocPtr) cur->next; + if (cur->_private != NULL) { + xsltFreeDocumentKeys(cur->_private); + xmlFree(cur->_private); + } + xmlFreeDoc(cur); + cur = next; + } + ctxt->localRVT = NULL; + /* + * User-created per-template fragments. + */ + cur = ctxt->tmpRVT; + while (cur != NULL) { + next = (xmlDocPtr) cur->next; + if (cur->_private != NULL) { + xsltFreeDocumentKeys(cur->_private); + xmlFree(cur->_private); + } + xmlFreeDoc(cur); + cur = next; + } + ctxt->tmpRVT = NULL; + /* + * Global fragments. + */ + cur = ctxt->persistRVT; + while (cur != NULL) { + next = (xmlDocPtr) cur->next; + if (cur->_private != NULL) { + xsltFreeDocumentKeys(cur->_private); + xmlFree(cur->_private); + } + xmlFreeDoc(cur); + cur = next; + } + ctxt->persistRVT = NULL; +} + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +/** + * xsltNewStackElem: + * + * Create a new XSLT ParserContext + * + * Returns the newly allocated xsltParserStackElem or NULL in case of error + */ +static xsltStackElemPtr +xsltNewStackElem(xsltTransformContextPtr ctxt) +{ + xsltStackElemPtr ret; + /* + * Reuse a stack item from the cache if available. + */ + if (ctxt && ctxt->cache->stackItems) { + ret = ctxt->cache->stackItems; + ctxt->cache->stackItems = ret->next; + ret->next = NULL; + ctxt->cache->nbStackItems--; +#ifdef XSLT_DEBUG_PROFILE_CACHE + ctxt->cache->dbgReusedVars++; +#endif + return(ret); + } + ret = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem)); + if (ret == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewStackElem : malloc failed\n"); + return(NULL); + } + memset(ret, 0, sizeof(xsltStackElem)); + ret->context = ctxt; + return(ret); +} + +/** + * xsltCopyStackElem: + * @elem: an XSLT stack element + * + * Makes a copy of the stack element + * + * Returns the copy of NULL + */ +static xsltStackElemPtr +xsltCopyStackElem(xsltStackElemPtr elem) { + xsltStackElemPtr cur; + + cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltCopyStackElem : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltStackElem)); + cur->context = elem->context; + cur->name = elem->name; + cur->nameURI = elem->nameURI; + cur->select = elem->select; + cur->tree = elem->tree; + cur->comp = elem->comp; + return(cur); +} + +/** + * xsltFreeStackElem: + * @elem: an XSLT stack element + * + * Free up the memory allocated by @elem + */ +static void +xsltFreeStackElem(xsltStackElemPtr elem) { + if (elem == NULL) + return; + if (elem->value != NULL) + xmlXPathFreeObject(elem->value); + /* + * Release the list of temporary Result Tree Fragments. + */ + if (elem->context) { + xmlDocPtr cur; + + while (elem->fragment != NULL) { + cur = elem->fragment; + elem->fragment = (xmlDocPtr) cur->next; + + if (cur->psvi == XSLT_RVT_VARIABLE) { + xsltReleaseRVT((xsltTransformContextPtr) elem->context, + cur); + } else if (cur->psvi != XSLT_RVT_FUNC_RESULT) { + xmlGenericError(xmlGenericErrorContext, + "xsltFreeStackElem: Unexpected RVT flag %p\n", + cur->psvi); + } + } + } + /* + * Cache or free the variable structure. + */ + if (elem->context && (elem->context->cache->nbStackItems < 50)) { + /* + * Store the item in the cache. + */ + xsltTransformContextPtr ctxt = elem->context; + memset(elem, 0, sizeof(xsltStackElem)); + elem->context = ctxt; + elem->next = ctxt->cache->stackItems; + ctxt->cache->stackItems = elem; + ctxt->cache->nbStackItems++; +#ifdef XSLT_DEBUG_PROFILE_CACHE + ctxt->cache->dbgCachedVars++; +#endif + return; + } + xmlFree(elem); +} + +/** + * xsltFreeStackElemList: + * @elem: an XSLT stack element + * + * Free up the memory allocated by @elem + */ +void +xsltFreeStackElemList(xsltStackElemPtr elem) { + xsltStackElemPtr next; + + while (elem != NULL) { + next = elem->next; + xsltFreeStackElem(elem); + elem = next; + } +} + +/** + * xsltStackLookup: + * @ctxt: an XSLT transformation context + * @name: the local part of the name + * @nameURI: the URI part of the name + * + * Locate an element in the stack based on its name. + */ +#if 0 /* TODO: Those seem to have been used for debugging. */ +static int stack_addr = 0; +static int stack_cmp = 0; +#endif + +static xsltStackElemPtr +xsltStackLookup(xsltTransformContextPtr ctxt, const xmlChar *name, + const xmlChar *nameURI) { + int i; + xsltStackElemPtr cur; + + if ((ctxt == NULL) || (name == NULL) || (ctxt->varsNr == 0)) + return(NULL); + + /* + * Do the lookup from the top of the stack, but + * don't use params being computed in a call-param + * First lookup expects the variable name and URI to + * come from the disctionnary and hence pointer comparison. + */ + for (i = ctxt->varsNr; i > ctxt->varsBase; i--) { + cur = ctxt->varsTab[i-1]; + while (cur != NULL) { + if ((cur->name == name) && (cur->nameURI == nameURI)) { +#if 0 + stack_addr++; +#endif + return(cur); + } + cur = cur->next; + } + } + + /* + * Redo the lookup with interned string compares + * to avoid string compares. + */ + name = xmlDictLookup(ctxt->dict, name, -1); + if (nameURI != NULL) + nameURI = xmlDictLookup(ctxt->dict, nameURI, -1); + + for (i = ctxt->varsNr; i > ctxt->varsBase; i--) { + cur = ctxt->varsTab[i-1]; + while (cur != NULL) { + if ((cur->name == name) && (cur->nameURI == nameURI)) { +#if 0 + stack_cmp++; +#endif + return(cur); + } + cur = cur->next; + } + } + + return(NULL); +} + +#ifdef XSLT_REFACTORED +#else + +/** + * xsltCheckStackElem: + * @ctxt: xn XSLT transformation context + * @name: the variable name + * @nameURI: the variable namespace URI + * + * Checks whether a variable or param is already defined. + * + * URGENT TODO: Checks for redefinition of vars/params should be + * done only at compilation time. + * + * Returns 1 if variable is present, 2 if param is present, 3 if this + * is an inherited param, 0 if not found, -1 in case of failure. + */ +static int +xsltCheckStackElem(xsltTransformContextPtr ctxt, const xmlChar *name, + const xmlChar *nameURI) { + xsltStackElemPtr cur; + + if ((ctxt == NULL) || (name == NULL)) + return(-1); + + cur = xsltStackLookup(ctxt, name, nameURI); + if (cur == NULL) + return(0); + if (cur->comp != NULL) { + if (cur->comp->type == XSLT_FUNC_WITHPARAM) + return(3); + else if (cur->comp->type == XSLT_FUNC_PARAM) + return(2); + } + + return(1); +} + +#endif /* XSLT_REFACTORED */ + +/** + * xsltAddStackElem: + * @ctxt: xn XSLT transformation context + * @elem: a stack element + * + * Push an element (or list) onto the stack. + * In case of a list, each member will be pushed into + * a seperate slot; i.e. there's always 1 stack entry for + * 1 stack element. + * + * Returns 0 in case of success, -1 in case of failure. + */ +static int +xsltAddStackElem(xsltTransformContextPtr ctxt, xsltStackElemPtr elem) +{ + if ((ctxt == NULL) || (elem == NULL)) + return(-1); + + do { + if (ctxt->varsMax == 0) { + ctxt->varsMax = 10; + ctxt->varsTab = + (xsltStackElemPtr *) xmlMalloc(ctxt->varsMax * + sizeof(ctxt->varsTab[0])); + if (ctxt->varsTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); + return (-1); + } + } + if (ctxt->varsNr >= ctxt->varsMax) { + ctxt->varsMax *= 2; + ctxt->varsTab = + (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab, + ctxt->varsMax * + sizeof(ctxt->varsTab[0])); + if (ctxt->varsTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); + return (-1); + } + } + ctxt->varsTab[ctxt->varsNr++] = elem; + ctxt->vars = elem; + + elem = elem->next; + } while (elem != NULL); + + return(0); +} + +/** + * xsltAddStackElemList: + * @ctxt: xn XSLT transformation context + * @elems: a stack element list + * + * Push an element list onto the stack. + * + * Returns 0 in case of success, -1 in case of failure. + */ +int +xsltAddStackElemList(xsltTransformContextPtr ctxt, xsltStackElemPtr elems) +{ + return(xsltAddStackElem(ctxt, elems)); +} + +/************************************************************************ + * * + * Module interfaces * + * * + ************************************************************************/ + +/** + * xsltEvalVariable: + * @ctxt: the XSLT transformation context + * @variable: the variable or parameter item + * @comp: the compiled XSLT instruction + * + * Evaluate a variable value. + * + * Returns the XPath Object value or NULL in case of error + */ +static xmlXPathObjectPtr +xsltEvalVariable(xsltTransformContextPtr ctxt, xsltStackElemPtr variable, + xsltStylePreCompPtr castedComp) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemVariablePtr comp = + (xsltStyleItemVariablePtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xmlXPathObjectPtr result = NULL; + xmlNodePtr oldInst; + + if ((ctxt == NULL) || (variable == NULL)) + return(NULL); + + /* + * A variable or parameter are evaluated on demand; thus the + * context (of XSLT and XPath) need to be temporarily adjusted and + * restored on exit. + */ + oldInst = ctxt->inst; + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Evaluating variable '%s'\n", variable->name)); +#endif + if (variable->select != NULL) { + xmlXPathCompExprPtr xpExpr = NULL; + xmlDocPtr oldXPDoc; + xmlNodePtr oldXPContextNode; + int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; + xmlNsPtr *oldXPNamespaces; + xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; + xsltStackElemPtr oldVar = ctxt->contextVariable; + + if ((comp != NULL) && (comp->comp != NULL)) { + xpExpr = comp->comp; + } else { + xpExpr = xmlXPathCompile(variable->select); + } + if (xpExpr == NULL) + return(NULL); + /* + * Save context states. + */ + oldXPDoc = xpctxt->doc; + oldXPContextNode = xpctxt->node; + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPContextSize = xpctxt->contextSize; + oldXPNamespaces = xpctxt->namespaces; + oldXPNsNr = xpctxt->nsNr; + + xpctxt->node = ctxt->node; + /* + * OPTIMIZE TODO: Lame try to set the context doc. + * Get rid of this somehow in xpath.c. + */ + if ((ctxt->node->type != XML_NAMESPACE_DECL) && + ctxt->node->doc) + xpctxt->doc = ctxt->node->doc; + /* + * BUG TODO: The proximity position and the context size will + * potentially be wrong. + * Example: + * <xsl:template select="foo"> + * <xsl:variable name="pos" select="position()"/> + * <xsl:for-each select="bar"> + * <xsl:value-of select="$pos"/> + * </xsl:for-each> + * </xsl:template> + * Here the proximity position and context size are changed + * to the context of <xsl:for-each select="bar">, but + * the variable needs to be evaluated in the context of + * <xsl:template select="foo">. + */ + if (comp != NULL) { + +#ifdef XSLT_REFACTORED + if (comp->inScopeNs != NULL) { + xpctxt->namespaces = comp->inScopeNs->list; + xpctxt->nsNr = comp->inScopeNs->xpathNumber; + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } +#else + xpctxt->namespaces = comp->nsList; + xpctxt->nsNr = comp->nsNr; +#endif + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } + + /* + * We need to mark that we are "selecting" a var's value; + * if any tree fragments are created inside the expression, + * then those need to be stored inside the variable; otherwise + * we'll eventually free still referenced fragments, before + * we leave the scope of the variable. + */ + ctxt->contextVariable = variable; + variable->flags |= XSLT_VAR_IN_SELECT; + + result = xmlXPathCompiledEval(xpExpr, xpctxt); + + variable->flags ^= XSLT_VAR_IN_SELECT; + /* + * Restore Context states. + */ + ctxt->contextVariable = oldVar; + + xpctxt->doc = oldXPDoc; + xpctxt->node = oldXPContextNode; + xpctxt->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->namespaces = oldXPNamespaces; + xpctxt->nsNr = oldXPNsNr; + + if ((comp == NULL) || (comp->comp == NULL)) + xmlXPathFreeCompExpr(xpExpr); + if (result == NULL) { + xsltTransformError(ctxt, NULL, + (comp != NULL) ? comp->inst : NULL, + "Failed to evaluate the expression of variable '%s'.\n", + variable->name); + ctxt->state = XSLT_STATE_STOPPED; + +#ifdef WITH_XSLT_DEBUG_VARIABLE +#ifdef LIBXML_DEBUG_ENABLED + } else { + if ((xsltGenericDebugContext == stdout) || + (xsltGenericDebugContext == stderr)) + xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, + result, 0); +#endif +#endif + } + } else { + if (variable->tree == NULL) { + result = xmlXPathNewCString(""); + } else { + if (variable->tree) { + xmlDocPtr container; + xmlNodePtr oldInsert; + xmlDocPtr oldOutput; + xsltStackElemPtr oldVar = ctxt->contextVariable; + + /* + * Generate a result tree fragment. + */ + container = xsltCreateRVT(ctxt); + if (container == NULL) + goto error; + /* + * NOTE: Local Result Tree Fragments of params/variables + * are not registered globally anymore; the life-time + * is not directly dependant of the param/variable itself. + * + * OLD: xsltRegisterTmpRVT(ctxt, container); + */ + /* + * Attach the Result Tree Fragment to the variable; + * when the variable is freed, it will also free + * the Result Tree Fragment. + */ + variable->fragment = container; + container->psvi = XSLT_RVT_VARIABLE; + + oldOutput = ctxt->output; + oldInsert = ctxt->insert; + + ctxt->output = container; + ctxt->insert = (xmlNodePtr) container; + ctxt->contextVariable = variable; + /* + * Process the sequence constructor (variable->tree). + * The resulting tree will be held by @container. + */ + xsltApplyOneTemplate(ctxt, ctxt->node, variable->tree, + NULL, NULL); + + ctxt->contextVariable = oldVar; + ctxt->insert = oldInsert; + ctxt->output = oldOutput; + + result = xmlXPathNewValueTree((xmlNodePtr) container); + } + if (result == NULL) { + result = xmlXPathNewCString(""); + } else { + /* + * Freeing is not handled there anymore. + * QUESTION TODO: What does the above comment mean? + */ + result->boolval = 0; + } +#ifdef WITH_XSLT_DEBUG_VARIABLE +#ifdef LIBXML_DEBUG_ENABLED + + if ((xsltGenericDebugContext == stdout) || + (xsltGenericDebugContext == stderr)) + xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, + result, 0); +#endif +#endif + } + } + +error: + ctxt->inst = oldInst; + return(result); +} + +/** + * xsltEvalGlobalVariable: + * @elem: the variable or parameter + * @ctxt: the XSLT transformation context + * + * Evaluates a the value of a global xsl:variable or + * xsl:param declaration. + * + * Returns the XPath Object value or NULL in case of error + */ +static xmlXPathObjectPtr +xsltEvalGlobalVariable(xsltStackElemPtr elem, xsltTransformContextPtr ctxt) +{ + xmlXPathObjectPtr result = NULL; + xmlNodePtr oldInst; + const xmlChar* oldVarName; + +#ifdef XSLT_REFACTORED + xsltStyleBasicItemVariablePtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((ctxt == NULL) || (elem == NULL)) + return(NULL); + if (elem->computed) + return(elem->value); + + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Evaluating global variable %s\n", elem->name)); +#endif + +#ifdef WITH_DEBUGGER + if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && + elem->comp && elem->comp->inst) + xslHandleDebugger(elem->comp->inst, NULL, NULL, ctxt); +#endif + + oldInst = ctxt->inst; +#ifdef XSLT_REFACTORED + comp = (xsltStyleBasicItemVariablePtr) elem->comp; +#else + comp = elem->comp; +#endif + oldVarName = elem->name; + elem->name = xsltComputingGlobalVarMarker; + /* + * OPTIMIZE TODO: We should consider instantiating global vars/params + * on-demand. The vars/params don't need to be evaluated if never + * called; and in the case of global params, if values for such params + * are provided by the user. + */ + if (elem->select != NULL) { + xmlXPathCompExprPtr xpExpr = NULL; + xmlDocPtr oldXPDoc; + xmlNodePtr oldXPContextNode; + int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; + xmlNsPtr *oldXPNamespaces; + xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; + + if ((comp != NULL) && (comp->comp != NULL)) { + xpExpr = comp->comp; + } else { + xpExpr = xmlXPathCompile(elem->select); + } + if (xpExpr == NULL) + goto error; + + + if (comp != NULL) + ctxt->inst = comp->inst; + else + ctxt->inst = NULL; + /* + * SPEC XSLT 1.0: + * "At top-level, the expression or template specifying the + * variable value is evaluated with the same context as that used + * to process the root node of the source document: the current + * node is the root node of the source document and the current + * node list is a list containing just the root node of the source + * document." + */ + /* + * Save context states. + */ + oldXPDoc = xpctxt->doc; + oldXPContextNode = xpctxt->node; + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPContextSize = xpctxt->contextSize; + oldXPNamespaces = xpctxt->namespaces; + oldXPNsNr = xpctxt->nsNr; + + xpctxt->node = ctxt->initialContextNode; + xpctxt->doc = ctxt->initialContextDoc; + xpctxt->contextSize = 1; + xpctxt->proximityPosition = 1; + + if (comp != NULL) { + +#ifdef XSLT_REFACTORED + if (comp->inScopeNs != NULL) { + xpctxt->namespaces = comp->inScopeNs->list; + xpctxt->nsNr = comp->inScopeNs->xpathNumber; + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } +#else + xpctxt->namespaces = comp->nsList; + xpctxt->nsNr = comp->nsNr; +#endif + } else { + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + } + + result = xmlXPathCompiledEval(xpExpr, xpctxt); + + /* + * Restore Context states. + */ + xpctxt->doc = oldXPDoc; + xpctxt->node = oldXPContextNode; + xpctxt->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->namespaces = oldXPNamespaces; + xpctxt->nsNr = oldXPNsNr; + + if ((comp == NULL) || (comp->comp == NULL)) + xmlXPathFreeCompExpr(xpExpr); + if (result == NULL) { + if (comp == NULL) + xsltTransformError(ctxt, NULL, NULL, + "Evaluating global variable %s failed\n", elem->name); + else + xsltTransformError(ctxt, NULL, comp->inst, + "Evaluating global variable %s failed\n", elem->name); + ctxt->state = XSLT_STATE_STOPPED; + goto error; + } + + /* + * Mark all RVTs that are referenced from result as part + * of this variable so they won't be freed too early. + */ + xsltFlagRVTs(ctxt, result, XSLT_RVT_GLOBAL); + +#ifdef WITH_XSLT_DEBUG_VARIABLE +#ifdef LIBXML_DEBUG_ENABLED + if ((xsltGenericDebugContext == stdout) || + (xsltGenericDebugContext == stderr)) + xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, + result, 0); +#endif +#endif + } else { + if (elem->tree == NULL) { + result = xmlXPathNewCString(""); + } else { + xmlDocPtr container; + xmlNodePtr oldInsert; + xmlDocPtr oldOutput, oldXPDoc; + /* + * Generate a result tree fragment. + */ + container = xsltCreateRVT(ctxt); + if (container == NULL) + goto error; + /* + * Let the lifetime of the tree fragment be handled by + * the Libxslt's garbage collector. + */ + xsltRegisterPersistRVT(ctxt, container); + + oldOutput = ctxt->output; + oldInsert = ctxt->insert; + + oldXPDoc = ctxt->xpathCtxt->doc; + + ctxt->output = container; + ctxt->insert = (xmlNodePtr) container; + + ctxt->xpathCtxt->doc = ctxt->initialContextDoc; + /* + * Process the sequence constructor. + */ + xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, NULL, NULL); + + ctxt->xpathCtxt->doc = oldXPDoc; + + ctxt->insert = oldInsert; + ctxt->output = oldOutput; + + result = xmlXPathNewValueTree((xmlNodePtr) container); + if (result == NULL) { + result = xmlXPathNewCString(""); + } else { + result->boolval = 0; /* Freeing is not handled there anymore */ + } +#ifdef WITH_XSLT_DEBUG_VARIABLE +#ifdef LIBXML_DEBUG_ENABLED + if ((xsltGenericDebugContext == stdout) || + (xsltGenericDebugContext == stderr)) + xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, + result, 0); +#endif +#endif + } + } + +error: + elem->name = oldVarName; + ctxt->inst = oldInst; + if (result != NULL) { + elem->value = result; + elem->computed = 1; + } + return(result); +} + +/** + * xsltEvalGlobalVariables: + * @ctxt: the XSLT transformation context + * + * Evaluates all global variables and parameters of a stylesheet. + * For internal use only. This is called at start of a transformation. + * + * Returns 0 in case of success, -1 in case of error + */ +int +xsltEvalGlobalVariables(xsltTransformContextPtr ctxt) { + xsltStackElemPtr elem; + xsltStylesheetPtr style; + + if ((ctxt == NULL) || (ctxt->document == NULL)) + return(-1); + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Registering global variables\n")); +#endif + /* + * Walk the list from the stylesheets and populate the hash table + */ + style = ctxt->style; + while (style != NULL) { + elem = style->variables; + +#ifdef WITH_XSLT_DEBUG_VARIABLE + if ((style->doc != NULL) && (style->doc->URL != NULL)) { + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Registering global variables from %s\n", + style->doc->URL)); + } +#endif + + while (elem != NULL) { + xsltStackElemPtr def; + + /* + * Global variables are stored in the variables pool. + */ + def = (xsltStackElemPtr) + xmlHashLookup2(ctxt->globalVars, + elem->name, elem->nameURI); + if (def == NULL) { + + def = xsltCopyStackElem(elem); + xmlHashAddEntry2(ctxt->globalVars, + elem->name, elem->nameURI, def); + } else if ((elem->comp != NULL) && + (elem->comp->type == XSLT_FUNC_VARIABLE)) { + /* + * Redefinition of variables from a different stylesheet + * should not generate a message. + */ + if ((elem->comp->inst != NULL) && + (def->comp != NULL) && (def->comp->inst != NULL) && + (elem->comp->inst->doc == def->comp->inst->doc)) + { + xsltTransformError(ctxt, style, elem->comp->inst, + "Global variable %s already defined\n", elem->name); + if (style != NULL) style->errors++; + } + } + elem = elem->next; + } + + style = xsltNextImport(style); + } + + /* + * This part does the actual evaluation + */ + xmlHashScan(ctxt->globalVars, + (xmlHashScanner) xsltEvalGlobalVariable, ctxt); + + return(0); +} + +/** + * xsltRegisterGlobalVariable: + * @style: the XSLT transformation context + * @name: the variable name + * @ns_uri: the variable namespace URI + * @sel: the expression which need to be evaluated to generate a value + * @tree: the subtree if sel is NULL + * @comp: the precompiled value + * @value: the string value if available + * + * Register a new variable value. If @value is NULL it unregisters + * the variable + * + * Returns 0 in case of success, -1 in case of error + */ +static int +xsltRegisterGlobalVariable(xsltStylesheetPtr style, const xmlChar *name, + const xmlChar *ns_uri, const xmlChar *sel, + xmlNodePtr tree, xsltStylePreCompPtr comp, + const xmlChar *value) { + xsltStackElemPtr elem, tmp; + if (style == NULL) + return(-1); + if (name == NULL) + return(-1); + if (comp == NULL) + return(-1); + +#ifdef WITH_XSLT_DEBUG_VARIABLE + if (comp->type == XSLT_FUNC_PARAM) + xsltGenericDebug(xsltGenericDebugContext, + "Defining global param %s\n", name); + else + xsltGenericDebug(xsltGenericDebugContext, + "Defining global variable %s\n", name); +#endif + + elem = xsltNewStackElem(NULL); + if (elem == NULL) + return(-1); + elem->comp = comp; + elem->name = xmlDictLookup(style->dict, name, -1); + elem->select = xmlDictLookup(style->dict, sel, -1); + if (ns_uri) + elem->nameURI = xmlDictLookup(style->dict, ns_uri, -1); + elem->tree = tree; + tmp = style->variables; + if (tmp == NULL) { + elem->next = NULL; + style->variables = elem; + } else { + while (tmp != NULL) { + if ((elem->comp->type == XSLT_FUNC_VARIABLE) && + (tmp->comp->type == XSLT_FUNC_VARIABLE) && + (xmlStrEqual(elem->name, tmp->name)) && + ((elem->nameURI == tmp->nameURI) || + (xmlStrEqual(elem->nameURI, tmp->nameURI)))) + { + xsltTransformError(NULL, style, comp->inst, + "redefinition of global variable %s\n", elem->name); + style->errors++; + } + if (tmp->next == NULL) + break; + tmp = tmp->next; + } + elem->next = NULL; + tmp->next = elem; + } + if (value != NULL) { + elem->computed = 1; + elem->value = xmlXPathNewString(value); + } + return(0); +} + +/** + * xsltProcessUserParamInternal + * + * @ctxt: the XSLT transformation context + * @name: a null terminated parameter name + * @value: a null terminated value (may be an XPath expression) + * @eval: 0 to treat the value literally, else evaluate as XPath expression + * + * If @eval is 0 then @value is treated literally and is stored in the global + * parameter/variable table without any change. + * + * Uf @eval is 1 then @value is treated as an XPath expression and is + * evaluated. In this case, if you want to pass a string which will be + * interpreted literally then it must be enclosed in single or double quotes. + * If the string contains single quotes (double quotes) then it cannot be + * enclosed single quotes (double quotes). If the string which you want to + * be treated literally contains both single and double quotes (e.g. Meet + * at Joe's for "Twelfth Night" at 7 o'clock) then there is no suitable + * quoting character. You cannot use ' or " inside the string + * because the replacement of character entities with their equivalents is + * done at a different stage of processing. The solution is to call + * xsltQuoteUserParams or xsltQuoteOneUserParam. + * + * This needs to be done on parsed stylesheets before starting to apply + * transformations. Normally this will be called (directly or indirectly) + * only from xsltEvalUserParams, xsltEvalOneUserParam, xsltQuoteUserParams, + * or xsltQuoteOneUserParam. + * + * Returns 0 in case of success, -1 in case of error + */ + +static +int +xsltProcessUserParamInternal(xsltTransformContextPtr ctxt, + const xmlChar * name, + const xmlChar * value, + int eval) { + + xsltStylesheetPtr style; + const xmlChar *prefix; + const xmlChar *href; + xmlXPathCompExprPtr xpExpr; + xmlXPathObjectPtr result; + + xsltStackElemPtr elem; + int res; + void *res_ptr; + + if (ctxt == NULL) + return(-1); + if (name == NULL) + return(0); + if (value == NULL) + return(0); + + style = ctxt->style; + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Evaluating user parameter %s=%s\n", name, value)); +#endif + + /* + * Name lookup + */ + href = NULL; + + if (name[0] == '{') { + int len = 0; + + while ((name[len] != 0) && (name[len] != '}')) len++; + if (name[len] == 0) { + xsltTransformError(ctxt, style, NULL, + "user param : malformed parameter name : %s\n", name); + } else { + href = xmlDictLookup(ctxt->dict, &name[1], len-1); + name = xmlDictLookup(ctxt->dict, &name[len + 1], -1); + } + } + else { + name = xsltSplitQName(ctxt->dict, name, &prefix); + if (prefix != NULL) { + xmlNsPtr ns; + + ns = xmlSearchNs(style->doc, xmlDocGetRootElement(style->doc), + prefix); + if (ns == NULL) { + xsltTransformError(ctxt, style, NULL, + "user param : no namespace bound to prefix %s\n", prefix); + href = NULL; + } else { + href = ns->href; + } + } + } + + if (name == NULL) + return (-1); + + res_ptr = xmlHashLookup2(ctxt->globalVars, name, href); + if (res_ptr != 0) { + xsltTransformError(ctxt, style, NULL, + "Global parameter %s already defined\n", name); + } + if (ctxt->globalVars == NULL) + ctxt->globalVars = xmlHashCreate(20); + + /* + * do not overwrite variables with parameters from the command line + */ + while (style != NULL) { + elem = ctxt->style->variables; + while (elem != NULL) { + if ((elem->comp != NULL) && + (elem->comp->type == XSLT_FUNC_VARIABLE) && + (xmlStrEqual(elem->name, name)) && + (xmlStrEqual(elem->nameURI, href))) { + return(0); + } + elem = elem->next; + } + style = xsltNextImport(style); + } + style = ctxt->style; + elem = NULL; + + /* + * Do the evaluation if @eval is non-zero. + */ + + result = NULL; + if (eval != 0) { + xpExpr = xmlXPathCompile(value); + if (xpExpr != NULL) { + xmlDocPtr oldXPDoc; + xmlNodePtr oldXPContextNode; + int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; + xmlNsPtr *oldXPNamespaces; + xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; + + /* + * Save context states. + */ + oldXPDoc = xpctxt->doc; + oldXPContextNode = xpctxt->node; + oldXPProximityPosition = xpctxt->proximityPosition; + oldXPContextSize = xpctxt->contextSize; + oldXPNamespaces = xpctxt->namespaces; + oldXPNsNr = xpctxt->nsNr; + + /* + * SPEC XSLT 1.0: + * "At top-level, the expression or template specifying the + * variable value is evaluated with the same context as that used + * to process the root node of the source document: the current + * node is the root node of the source document and the current + * node list is a list containing just the root node of the source + * document." + */ + xpctxt->doc = ctxt->initialContextDoc; + xpctxt->node = ctxt->initialContextNode; + xpctxt->contextSize = 1; + xpctxt->proximityPosition = 1; + /* + * There is really no in scope namespace for parameters on the + * command line. + */ + xpctxt->namespaces = NULL; + xpctxt->nsNr = 0; + + result = xmlXPathCompiledEval(xpExpr, xpctxt); + + /* + * Restore Context states. + */ + xpctxt->doc = oldXPDoc; + xpctxt->node = oldXPContextNode; + xpctxt->contextSize = oldXPContextSize; + xpctxt->proximityPosition = oldXPProximityPosition; + xpctxt->namespaces = oldXPNamespaces; + xpctxt->nsNr = oldXPNsNr; + + xmlXPathFreeCompExpr(xpExpr); + } + if (result == NULL) { + xsltTransformError(ctxt, style, NULL, + "Evaluating user parameter %s failed\n", name); + ctxt->state = XSLT_STATE_STOPPED; + return(-1); + } + } + + /* + * If @eval is 0 then @value is to be taken literally and result is NULL + * + * If @eval is not 0, then @value is an XPath expression and has been + * successfully evaluated and result contains the resulting value and + * is not NULL. + * + * Now create an xsltStackElemPtr for insertion into the context's + * global variable/parameter hash table. + */ + +#ifdef WITH_XSLT_DEBUG_VARIABLE +#ifdef LIBXML_DEBUG_ENABLED + if ((xsltGenericDebugContext == stdout) || + (xsltGenericDebugContext == stderr)) + xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, + result, 0); +#endif +#endif + + elem = xsltNewStackElem(NULL); + if (elem != NULL) { + elem->name = name; + elem->select = xmlDictLookup(ctxt->dict, value, -1); + if (href != NULL) + elem->nameURI = xmlDictLookup(ctxt->dict, href, -1); + elem->tree = NULL; + elem->computed = 1; + if (eval == 0) { + elem->value = xmlXPathNewString(value); + } + else { + elem->value = result; + } + } + + /* + * Global parameters are stored in the XPath context variables pool. + */ + + res = xmlHashAddEntry2(ctxt->globalVars, name, href, elem); + if (res != 0) { + xsltFreeStackElem(elem); + xsltTransformError(ctxt, style, NULL, + "Global parameter %s already defined\n", name); + } + return(0); +} + +/** + * xsltEvalUserParams: + * + * @ctxt: the XSLT transformation context + * @params: a NULL terminated array of parameters name/value tuples + * + * Evaluate the global variables of a stylesheet. This needs to be + * done on parsed stylesheets before starting to apply transformations. + * Each of the parameters is evaluated as an XPath expression and stored + * in the global variables/parameter hash table. If you want your + * parameter used literally, use xsltQuoteUserParams. + * + * Returns 0 in case of success, -1 in case of error + */ + +int +xsltEvalUserParams(xsltTransformContextPtr ctxt, const char **params) { + int indx = 0; + const xmlChar *name; + const xmlChar *value; + + if (params == NULL) + return(0); + while (params[indx] != NULL) { + name = (const xmlChar *) params[indx++]; + value = (const xmlChar *) params[indx++]; + if (xsltEvalOneUserParam(ctxt, name, value) != 0) + return(-1); + } + return 0; +} + +/** + * xsltQuoteUserParams: + * + * @ctxt: the XSLT transformation context + * @params: a NULL terminated arry of parameters names/values tuples + * + * Similar to xsltEvalUserParams, but the values are treated literally and + * are * *not* evaluated as XPath expressions. This should be done on parsed + * stylesheets before starting to apply transformations. + * + * Returns 0 in case of success, -1 in case of error. + */ + +int +xsltQuoteUserParams(xsltTransformContextPtr ctxt, const char **params) { + int indx = 0; + const xmlChar *name; + const xmlChar *value; + + if (params == NULL) + return(0); + while (params[indx] != NULL) { + name = (const xmlChar *) params[indx++]; + value = (const xmlChar *) params[indx++]; + if (xsltQuoteOneUserParam(ctxt, name, value) != 0) + return(-1); + } + return 0; +} + +/** + * xsltEvalOneUserParam: + * @ctxt: the XSLT transformation context + * @name: a null terminated string giving the name of the parameter + * @value: a null terminated string giving the XPath expression to be evaluated + * + * This is normally called from xsltEvalUserParams to process a single + * parameter from a list of parameters. The @value is evaluated as an + * XPath expression and the result is stored in the context's global + * variable/parameter hash table. + * + * To have a parameter treated literally (not as an XPath expression) + * use xsltQuoteUserParams (or xsltQuoteOneUserParam). For more + * details see description of xsltProcessOneUserParamInternal. + * + * Returns 0 in case of success, -1 in case of error. + */ + +int +xsltEvalOneUserParam(xsltTransformContextPtr ctxt, + const xmlChar * name, + const xmlChar * value) { + return xsltProcessUserParamInternal(ctxt, name, value, + 1 /* xpath eval ? */); +} + +/** + * xsltQuoteOneUserParam: + * @ctxt: the XSLT transformation context + * @name: a null terminated string giving the name of the parameter + * @value: a null terminated string giving the parameter value + * + * This is normally called from xsltQuoteUserParams to process a single + * parameter from a list of parameters. The @value is stored in the + * context's global variable/parameter hash table. + * + * Returns 0 in case of success, -1 in case of error. + */ + +int +xsltQuoteOneUserParam(xsltTransformContextPtr ctxt, + const xmlChar * name, + const xmlChar * value) { + return xsltProcessUserParamInternal(ctxt, name, value, + 0 /* xpath eval ? */); +} + +/** + * xsltBuildVariable: + * @ctxt: the XSLT transformation context + * @comp: the precompiled form + * @tree: the tree if select is NULL + * + * Computes a new variable value. + * + * Returns the xsltStackElemPtr or NULL in case of error + */ +static xsltStackElemPtr +xsltBuildVariable(xsltTransformContextPtr ctxt, + xsltStylePreCompPtr castedComp, + xmlNodePtr tree) +{ +#ifdef XSLT_REFACTORED + xsltStyleBasicItemVariablePtr comp = + (xsltStyleBasicItemVariablePtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; +#endif + xsltStackElemPtr elem; + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Building variable %s", comp->name)); + if (comp->select != NULL) + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + " select %s", comp->select)); + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, "\n")); +#endif + + elem = xsltNewStackElem(ctxt); + if (elem == NULL) + return(NULL); + elem->comp = (xsltStylePreCompPtr) comp; + elem->name = comp->name; + elem->select = comp->select; + elem->nameURI = comp->ns; + elem->tree = tree; + elem->value = xsltEvalVariable(ctxt, elem, + (xsltStylePreCompPtr) comp); + elem->computed = 1; + return(elem); +} + +/** + * xsltRegisterVariable: + * @ctxt: the XSLT transformation context + * @comp: the compiled XSLT-variable (or param) instruction + * @tree: the tree if select is NULL + * @isParam: indicates if this is a parameter + * + * Computes and registers a new variable. + * + * Returns 0 in case of success, -1 in case of error + */ +static int +xsltRegisterVariable(xsltTransformContextPtr ctxt, + xsltStylePreCompPtr castedComp, + xmlNodePtr tree, int isParam) +{ +#ifdef XSLT_REFACTORED + xsltStyleBasicItemVariablePtr comp = + (xsltStyleBasicItemVariablePtr) castedComp; +#else + xsltStylePreCompPtr comp = castedComp; + int present; +#endif + xsltStackElemPtr variable; + +#ifdef XSLT_REFACTORED + /* + * REFACTORED NOTE: Redefinitions of vars/params are checked + * at compilation time in the refactored code. + * xsl:with-param parameters are checked in xsltApplyXSLTTemplate(). + */ +#else + present = xsltCheckStackElem(ctxt, comp->name, comp->ns); + if (isParam == 0) { + if ((present != 0) && (present != 3)) { + /* TODO: report QName. */ + xsltTransformError(ctxt, NULL, comp->inst, + "XSLT-variable: Redefinition of variable '%s'.\n", comp->name); + return(0); + } + } else if (present != 0) { + if ((present == 1) || (present == 2)) { + /* TODO: report QName. */ + xsltTransformError(ctxt, NULL, comp->inst, + "XSLT-param: Redefinition of parameter '%s'.\n", comp->name); + return(0); + } +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "param %s defined by caller\n", comp->name)); +#endif + return(0); + } +#endif /* else of XSLT_REFACTORED */ + + variable = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree); + xsltAddStackElem(ctxt, variable); + return(0); +} + +/** + * xsltGlobalVariableLookup: + * @ctxt: the XSLT transformation context + * @name: the variable name + * @ns_uri: the variable namespace URI + * + * Search in the Variable array of the context for the given + * variable value. + * + * Returns the value or NULL if not found + */ +static xmlXPathObjectPtr +xsltGlobalVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name, + const xmlChar *ns_uri) { + xsltStackElemPtr elem; + xmlXPathObjectPtr ret = NULL; + + /* + * Lookup the global variables in XPath global variable hash table + */ + if ((ctxt->xpathCtxt == NULL) || (ctxt->globalVars == NULL)) + return(NULL); + elem = (xsltStackElemPtr) + xmlHashLookup2(ctxt->globalVars, name, ns_uri); + if (elem == NULL) { +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "global variable not found %s\n", name)); +#endif + return(NULL); + } + /* + * URGENT TODO: Move the detection of recursive definitions + * to compile-time. + */ + if (elem->computed == 0) { + if (elem->name == xsltComputingGlobalVarMarker) { + xsltTransformError(ctxt, NULL, elem->comp->inst, + "Recursive definition of %s\n", name); + return(NULL); + } + ret = xsltEvalGlobalVariable(elem, ctxt); + } else + ret = elem->value; + return(xmlXPathObjectCopy(ret)); +} + +/** + * xsltVariableLookup: + * @ctxt: the XSLT transformation context + * @name: the variable name + * @ns_uri: the variable namespace URI + * + * Search in the Variable array of the context for the given + * variable value. + * + * Returns the value or NULL if not found + */ +xmlXPathObjectPtr +xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name, + const xmlChar *ns_uri) { + xsltStackElemPtr elem; + + if (ctxt == NULL) + return(NULL); + + elem = xsltStackLookup(ctxt, name, ns_uri); + if (elem == NULL) { + return(xsltGlobalVariableLookup(ctxt, name, ns_uri)); + } + if (elem->computed == 0) { +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "uncomputed variable %s\n", name)); +#endif + elem->value = xsltEvalVariable(ctxt, elem, NULL); + elem->computed = 1; + } + if (elem->value != NULL) + return(xmlXPathObjectCopy(elem->value)); +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "variable not found %s\n", name)); +#endif + return(NULL); +} + +/** + * xsltParseStylesheetCallerParam: + * @ctxt: the XSLT transformation context + * @inst: the xsl:with-param instruction element + * + * Processes an xsl:with-param instruction at transformation time. + * The value is compute, but not recorded. + * NOTE that this is also called with an *xsl:param* element + * from exsltFuncFunctionFunction(). + * + * Returns the new xsltStackElemPtr or NULL + */ + +xsltStackElemPtr +xsltParseStylesheetCallerParam(xsltTransformContextPtr ctxt, xmlNodePtr inst) +{ +#ifdef XSLT_REFACTORED + xsltStyleBasicItemVariablePtr comp; +#else + xsltStylePreCompPtr comp; +#endif + xmlNodePtr tree = NULL; /* The first child node of the instruction or + the instruction itself. */ + xsltStackElemPtr param = NULL; + + if ((ctxt == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) + return(NULL); + +#ifdef XSLT_REFACTORED + comp = (xsltStyleBasicItemVariablePtr) inst->psvi; +#else + comp = (xsltStylePreCompPtr) inst->psvi; +#endif + + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltParseStylesheetCallerParam(): " + "The XSLT 'with-param' instruction was not compiled.\n"); + return(NULL); + } + if (comp->name == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltParseStylesheetCallerParam(): " + "XSLT 'with-param': The attribute 'name' was not compiled.\n"); + return(NULL); + } + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Handling xsl:with-param %s\n", comp->name)); +#endif + + if (comp->select == NULL) { + tree = inst->children; + } else { +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + " select %s\n", comp->select)); +#endif + tree = inst; + } + + param = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree); + + return(param); +} + +/** + * xsltParseGlobalVariable: + * @style: the XSLT stylesheet + * @cur: the "variable" element + * + * Parses a global XSLT 'variable' declaration at compilation time + * and registers it + */ +void +xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemVariablePtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + /* + * Note that xsltStylePreCompute() will be called from + * xslt.c only. + */ + comp = (xsltStyleItemVariablePtr) cur->psvi; +#else + xsltStylePreCompute(style, cur); + comp = (xsltStylePreCompPtr) cur->psvi; +#endif + if (comp == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:variable : compilation failed\n"); + return; + } + + if (comp->name == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:variable : missing name attribute\n"); + return; + } + + /* + * Parse the content (a sequence constructor) of xsl:variable. + */ + if (cur->children != NULL) { +#ifdef XSLT_REFACTORED + xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children); +#else + xsltParseTemplateContent(style, cur); +#endif + } +#ifdef WITH_XSLT_DEBUG_VARIABLE + xsltGenericDebug(xsltGenericDebugContext, + "Registering global variable %s\n", comp->name); +#endif + + xsltRegisterGlobalVariable(style, comp->name, comp->ns, + comp->select, cur->children, (xsltStylePreCompPtr) comp, + NULL); +} + +/** + * xsltParseGlobalParam: + * @style: the XSLT stylesheet + * @cur: the "param" element + * + * parse an XSLT transformation param declaration and record + * its value. + */ + +void +xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) { +#ifdef XSLT_REFACTORED + xsltStyleItemParamPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) + return; + +#ifdef XSLT_REFACTORED + /* + * Note that xsltStylePreCompute() will be called from + * xslt.c only. + */ + comp = (xsltStyleItemParamPtr) cur->psvi; +#else + xsltStylePreCompute(style, cur); + comp = (xsltStylePreCompPtr) cur->psvi; +#endif + if (comp == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:param : compilation failed\n"); + return; + } + + if (comp->name == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:param : missing name attribute\n"); + return; + } + + /* + * Parse the content (a sequence constructor) of xsl:param. + */ + if (cur->children != NULL) { +#ifdef XSLT_REFACTORED + xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children); +#else + xsltParseTemplateContent(style, cur); +#endif + } + +#ifdef WITH_XSLT_DEBUG_VARIABLE + xsltGenericDebug(xsltGenericDebugContext, + "Registering global param %s\n", comp->name); +#endif + + xsltRegisterGlobalVariable(style, comp->name, comp->ns, + comp->select, cur->children, (xsltStylePreCompPtr) comp, + NULL); +} + +/** + * xsltParseStylesheetVariable: + * @ctxt: the XSLT transformation context + * @inst: the xsl:variable instruction element + * + * Registers a local XSLT 'variable' instruction at transformation time + * and evaluates its value. + */ +void +xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr inst) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemVariablePtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((inst == NULL) || (ctxt == NULL) || (inst->type != XML_ELEMENT_NODE)) + return; + + comp = inst->psvi; + if (comp == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltParseStylesheetVariable(): " + "The XSLT 'variable' instruction was not compiled.\n"); + return; + } + if (comp->name == NULL) { + xsltTransformError(ctxt, NULL, inst, + "Internal error in xsltParseStylesheetVariable(): " + "The attribute 'name' was not compiled.\n"); + return; + } + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Registering variable '%s'\n", comp->name)); +#endif + + xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, inst->children, 0); +} + +/** + * xsltParseStylesheetParam: + * @ctxt: the XSLT transformation context + * @cur: the XSLT 'param' element + * + * Registers a local XSLT 'param' declaration at transformation time and + * evaluates its value. + */ +void +xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemParamPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + + if ((cur == NULL) || (ctxt == NULL) || (cur->type != XML_ELEMENT_NODE)) + return; + + comp = cur->psvi; + if ((comp == NULL) || (comp->name == NULL)) { + xsltTransformError(ctxt, NULL, cur, + "Internal error in xsltParseStylesheetParam(): " + "The XSLT 'param' declaration was not compiled correctly.\n"); + return; + } + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Registering param %s\n", comp->name)); +#endif + + xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, cur->children, 1); +} + +/** + * xsltFreeGlobalVariables: + * @ctxt: the XSLT transformation context + * + * Free up the data associated to the global variables + * its value. + */ + +void +xsltFreeGlobalVariables(xsltTransformContextPtr ctxt) { + xmlHashFree(ctxt->globalVars, (xmlHashDeallocator) xsltFreeStackElem); +} + +/** + * xsltXPathVariableLookup: + * @ctxt: a void * but the the XSLT transformation context actually + * @name: the variable name + * @ns_uri: the variable namespace URI + * + * This is the entry point when a varibale is needed by the XPath + * interpretor. + * + * Returns the value or NULL if not found + */ +xmlXPathObjectPtr +xsltXPathVariableLookup(void *ctxt, const xmlChar *name, + const xmlChar *ns_uri) { + xsltTransformContextPtr tctxt; + xmlXPathObjectPtr valueObj = NULL; + + if ((ctxt == NULL) || (name == NULL)) + return(NULL); + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(((xsltTransformContextPtr)ctxt),XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "Lookup variable '%s'\n", name)); +#endif + + tctxt = (xsltTransformContextPtr) ctxt; + /* + * Local variables/params --------------------------------------------- + * + * Do the lookup from the top of the stack, but + * don't use params being computed in a call-param + * First lookup expects the variable name and URI to + * come from the disctionnary and hence pointer comparison. + */ + if (tctxt->varsNr != 0) { + int i; + xsltStackElemPtr variable = NULL, cur; + + for (i = tctxt->varsNr; i > tctxt->varsBase; i--) { + cur = tctxt->varsTab[i-1]; + if ((cur->name == name) && (cur->nameURI == ns_uri)) { +#if 0 + stack_addr++; +#endif + variable = cur; + goto local_variable_found; + } + cur = cur->next; + } + /* + * Redo the lookup with interned strings to avoid string comparison. + * + * OPTIMIZE TODO: The problem here is, that if we request a + * global variable, then this will be also executed. + */ + { + const xmlChar *tmpName = name, *tmpNsName = ns_uri; + + name = xmlDictLookup(tctxt->dict, name, -1); + if (ns_uri) + ns_uri = xmlDictLookup(tctxt->dict, ns_uri, -1); + if ((tmpName != name) || (tmpNsName != ns_uri)) { + for (i = tctxt->varsNr; i > tctxt->varsBase; i--) { + cur = tctxt->varsTab[i-1]; + if ((cur->name == name) && (cur->nameURI == ns_uri)) { +#if 0 + stack_cmp++; +#endif + variable = cur; + goto local_variable_found; + } + } + } + } + +local_variable_found: + + if (variable) { + if (variable->computed == 0) { + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "uncomputed variable '%s'\n", name)); +#endif + variable->value = xsltEvalVariable(tctxt, variable, NULL); + variable->computed = 1; + } + if (variable->value != NULL) { + valueObj = xmlXPathObjectCopy(variable->value); + } + return(valueObj); + } + } + /* + * Global variables/params -------------------------------------------- + */ + if (tctxt->globalVars) { + valueObj = xsltGlobalVariableLookup(tctxt, name, ns_uri); + } + + if (valueObj == NULL) { + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "variable not found '%s'\n", name)); +#endif + + if (ns_uri) { + xsltTransformError(tctxt, NULL, tctxt->inst, + "Variable '{%s}%s' has not been declared.\n", ns_uri, name); + } else { + xsltTransformError(tctxt, NULL, tctxt->inst, + "Variable '%s' has not been declared.\n", name); + } + } else { + +#ifdef WITH_XSLT_DEBUG_VARIABLE + XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, + "found variable '%s'\n", name)); +#endif + } + + return(valueObj); +} + + diff --git a/libxslt/variables.h b/libxslt/variables.h new file mode 100644 index 0000000..f80eeab --- /dev/null +++ b/libxslt/variables.h @@ -0,0 +1,100 @@ +/* + * Summary: interface for the variable matching and lookup. + * Description: interface for the variable matching and lookup. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_VARIABLES_H__ +#define __XML_XSLT_VARIABLES_H__ + +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> +#include "xsltexports.h" +#include "xsltInternals.h" +#include "functions.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * XSLT_REGISTER_VARIABLE_LOOKUP: + * + * Registering macro, not general purpose at all but used in different modules. + */ + +#define XSLT_REGISTER_VARIABLE_LOOKUP(ctxt) \ + xmlXPathRegisterVariableLookup((ctxt)->xpathCtxt, \ + xsltXPathVariableLookup, (void *)(ctxt)); \ + xsltRegisterAllFunctions((ctxt)->xpathCtxt); \ + xsltRegisterAllElement(ctxt); \ + (ctxt)->xpathCtxt->extra = ctxt + +/* + * Flags for memory management of RVTs + */ + +#define XSLT_RVT_LOCAL ((void *)1) +#define XSLT_RVT_VARIABLE ((void *)2) +#define XSLT_RVT_FUNC_RESULT ((void *)3) +#define XSLT_RVT_GLOBAL ((void *)4) + +/* + * Interfaces for the variable module. + */ + +XSLTPUBFUN int XSLTCALL + xsltEvalGlobalVariables (xsltTransformContextPtr ctxt); +XSLTPUBFUN int XSLTCALL + xsltEvalUserParams (xsltTransformContextPtr ctxt, + const char **params); +XSLTPUBFUN int XSLTCALL + xsltQuoteUserParams (xsltTransformContextPtr ctxt, + const char **params); +XSLTPUBFUN int XSLTCALL + xsltEvalOneUserParam (xsltTransformContextPtr ctxt, + const xmlChar * name, + const xmlChar * value); +XSLTPUBFUN int XSLTCALL + xsltQuoteOneUserParam (xsltTransformContextPtr ctxt, + const xmlChar * name, + const xmlChar * value); + +XSLTPUBFUN void XSLTCALL + xsltParseGlobalVariable (xsltStylesheetPtr style, + xmlNodePtr cur); +XSLTPUBFUN void XSLTCALL + xsltParseGlobalParam (xsltStylesheetPtr style, + xmlNodePtr cur); +XSLTPUBFUN void XSLTCALL + xsltParseStylesheetVariable (xsltTransformContextPtr ctxt, + xmlNodePtr cur); +XSLTPUBFUN void XSLTCALL + xsltParseStylesheetParam (xsltTransformContextPtr ctxt, + xmlNodePtr cur); +XSLTPUBFUN xsltStackElemPtr XSLTCALL + xsltParseStylesheetCallerParam (xsltTransformContextPtr ctxt, + xmlNodePtr cur); +XSLTPUBFUN int XSLTCALL + xsltAddStackElemList (xsltTransformContextPtr ctxt, + xsltStackElemPtr elems); +XSLTPUBFUN void XSLTCALL + xsltFreeGlobalVariables (xsltTransformContextPtr ctxt); +XSLTPUBFUN xmlXPathObjectPtr XSLTCALL + xsltVariableLookup (xsltTransformContextPtr ctxt, + const xmlChar *name, + const xmlChar *ns_uri); +XSLTPUBFUN xmlXPathObjectPtr XSLTCALL + xsltXPathVariableLookup (void *ctxt, + const xmlChar *name, + const xmlChar *ns_uri); +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_VARIABLES_H__ */ + diff --git a/libxslt/win32config.h b/libxslt/win32config.h new file mode 100644 index 0000000..8fe7042 --- /dev/null +++ b/libxslt/win32config.h @@ -0,0 +1,129 @@ +/* + * Summary: Windows configuration header + * Description: Windows configuration header + * + * Copy: See Copyright for the status of this software. + * + * Author: Igor Zlatkovic + */ +#ifndef __LIBXSLT_WIN32_CONFIG__ +#define __LIBXSLT_WIN32_CONFIG__ + +#define HAVE_CTYPE_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STDARG_H 1 +#define HAVE_MALLOC_H 1 +#define HAVE_TIME_H 1 +#define HAVE_LOCALTIME 1 +#define HAVE_GMTIME 1 +#define HAVE_TIME 1 +#define HAVE_MATH_H 1 +#define HAVE_FCNTL_H 1 + +#include <io.h> + +#define HAVE_ISINF +#define HAVE_ISNAN + +#include <math.h> +#if defined _MSC_VER || defined __MINGW32__ +/* MS C-runtime has functions which can be used in order to determine if + a given floating-point variable contains NaN, (+-)INF. These are + preferred, because floating-point technology is considered propriatary + by MS and we can assume that their functions know more about their + oddities than we do. */ +#include <float.h> +/* Bjorn Reese figured a quite nice construct for isinf() using the + _fpclass() function. */ +#ifndef isinf +#define isinf(d) ((_fpclass(d) == _FPCLASS_PINF) ? 1 \ + : ((_fpclass(d) == _FPCLASS_NINF) ? -1 : 0)) +#endif +/* _isnan(x) returns nonzero if (x == NaN) and zero otherwise. */ +#ifndef isnan +#define isnan(d) (_isnan(d)) +#endif +#else /* _MSC_VER */ +static int isinf (double d) { + int expon = 0; + double val = frexp (d, &expon); + if (expon == 1025) { + if (val == 0.5) { + return 1; + } else if (val == -0.5) { + return -1; + } else { + return 0; + } + } else { + return 0; + } +} +static int isnan (double d) { + int expon = 0; + double val = frexp (d, &expon); + if (expon == 1025) { + if (val == 0.5) { + return 0; + } else if (val == -0.5) { + return 0; + } else { + return 1; + } + } else { + return 0; + } +} +#endif /* _MSC_VER */ + +#include <direct.h> + +/* snprintf emulation taken from http://stackoverflow.com/a/8712996/1956010 */ +#if defined(_MSC_VER) && _MSC_VER < 1900 + +#include <stdarg.h> +#include <stdio.h> + +#define snprintf c99_snprintf +#define vsnprintf c99_vsnprintf + +__inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) +{ + int count = -1; + + if (size != 0) + count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + + return count; +} + +__inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...) +{ + int count; + va_list ap; + + va_start(ap, format); + count = c99_vsnprintf(outBuf, size, format, ap); + va_end(ap); + + return count; +} + +#endif /* defined(_MSC_VER) && _MSC_VER < 1900 */ + +#define HAVE_SYS_STAT_H +#define HAVE__STAT +#define HAVE_STRING_H + +#include <libxml/xmlversion.h> + +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED +#endif + +#define _WINSOCKAPI_ + +#endif /* __LIBXSLT_WIN32_CONFIG__ */ + diff --git a/libxslt/xslt.c b/libxslt/xslt.c new file mode 100644 index 0000000..d41a98d --- /dev/null +++ b/libxslt/xslt.c @@ -0,0 +1,6990 @@ +/* + * xslt.c: Implemetation of an XSL Transformation 1.0 engine + * + * Reference: + * XSLT specification + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * Associating Style Sheets with XML documents + * http://www.w3.org/1999/06/REC-xml-stylesheet-19990629 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> + +#include <libxml/xmlmemory.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/valid.h> +#include <libxml/hash.h> +#include <libxml/uri.h> +#include <libxml/xmlerror.h> +#include <libxml/parserInternals.h> +#include <libxml/xpathInternals.h> +#include <libxml/xpath.h> +#include "xslt.h" +#include "xsltInternals.h" +#include "pattern.h" +#include "variables.h" +#include "namespaces.h" +#include "attributes.h" +#include "xsltutils.h" +#include "imports.h" +#include "keys.h" +#include "documents.h" +#include "extensions.h" +#include "preproc.h" +#include "extra.h" +#include "security.h" + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_PARSING +/* #define WITH_XSLT_DEBUG_BLANKS */ +#endif + +const char *xsltEngineVersion = LIBXSLT_VERSION_STRING LIBXSLT_VERSION_EXTRA; +const int xsltLibxsltVersion = LIBXSLT_VERSION; +const int xsltLibxmlVersion = LIBXML_VERSION; + +#ifdef XSLT_REFACTORED + +const xmlChar *xsltConstNamespaceNameXSLT = (const xmlChar *) XSLT_NAMESPACE; + +#define XSLT_ELEMENT_CATEGORY_XSLT 0 +#define XSLT_ELEMENT_CATEGORY_EXTENSION 1 +#define XSLT_ELEMENT_CATEGORY_LRE 2 + +/* +* xsltLiteralResultMarker: +* Marker for Literal result elements, in order to avoid multiple attempts +* to recognize such elements in the stylesheet's tree. +* This marker is set on node->psvi during the initial traversal +* of a stylesheet's node tree. +* +const xmlChar *xsltLiteralResultMarker = + (const xmlChar *) "Literal Result Element"; +*/ + +/* +* xsltXSLTTextMarker: +* Marker for xsl:text elements. Used to recognize xsl:text elements +* for post-processing of the stylesheet's tree, where those +* elements are removed from the tree. +*/ +const xmlChar *xsltXSLTTextMarker = (const xmlChar *) "XSLT Text Element"; + +/* +* xsltXSLTAttrMarker: +* Marker for XSLT attribute on Literal Result Elements. +*/ +const xmlChar *xsltXSLTAttrMarker = (const xmlChar *) "LRE XSLT Attr"; + +#endif + +#ifdef XSLT_LOCALE_WINAPI +extern xmlRMutexPtr xsltLocaleMutex; +#endif +/* + * Harmless but avoiding a problem when compiling against a + * libxml <= 2.3.11 without LIBXML_DEBUG_ENABLED + */ +#ifndef LIBXML_DEBUG_ENABLED +double xmlXPathStringEvalNumber(const xmlChar *str); +#endif +/* + * Useful macros + */ + +#ifdef IS_BLANK +#undef IS_BLANK +#endif +#define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \ + ((c) == 0x0D)) + +#ifdef IS_BLANK_NODE +#undef IS_BLANK_NODE +#endif +#define IS_BLANK_NODE(n) \ + (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content))) + +/** + * xsltParseContentError: + * + * @style: the stylesheet + * @node: the node where the error occured + * + * Compile-time error function. + */ +static void +xsltParseContentError(xsltStylesheetPtr style, + xmlNodePtr node) +{ + if ((style == NULL) || (node == NULL)) + return; + + if (IS_XSLT_ELEM(node)) + xsltTransformError(NULL, style, node, + "The XSLT-element '%s' is not allowed at this position.\n", + node->name); + else + xsltTransformError(NULL, style, node, + "The element '%s' is not allowed at this position.\n", + node->name); + style->errors++; +} + +#ifdef XSLT_REFACTORED +#else +/** + * exclPrefixPush: + * @style: the transformation stylesheet + * @value: the excluded namespace name to push on the stack + * + * Push an excluded namespace name on the stack + * + * Returns the new index in the stack or -1 if already present or + * in case of error + */ +static int +exclPrefixPush(xsltStylesheetPtr style, xmlChar * value) +{ + int i; + + if (style->exclPrefixMax == 0) { + style->exclPrefixMax = 4; + style->exclPrefixTab = + (xmlChar * *)xmlMalloc(style->exclPrefixMax * + sizeof(style->exclPrefixTab[0])); + if (style->exclPrefixTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); + return (-1); + } + } + /* do not push duplicates */ + for (i = 0;i < style->exclPrefixNr;i++) { + if (xmlStrEqual(style->exclPrefixTab[i], value)) + return(-1); + } + if (style->exclPrefixNr >= style->exclPrefixMax) { + style->exclPrefixMax *= 2; + style->exclPrefixTab = + (xmlChar * *)xmlRealloc(style->exclPrefixTab, + style->exclPrefixMax * + sizeof(style->exclPrefixTab[0])); + if (style->exclPrefixTab == NULL) { + xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); + return (-1); + } + } + style->exclPrefixTab[style->exclPrefixNr] = value; + style->exclPrefix = value; + return (style->exclPrefixNr++); +} +/** + * exclPrefixPop: + * @style: the transformation stylesheet + * + * Pop an excluded prefix value from the stack + * + * Returns the stored excluded prefix value + */ +static xmlChar * +exclPrefixPop(xsltStylesheetPtr style) +{ + xmlChar *ret; + + if (style->exclPrefixNr <= 0) + return (0); + style->exclPrefixNr--; + if (style->exclPrefixNr > 0) + style->exclPrefix = style->exclPrefixTab[style->exclPrefixNr - 1]; + else + style->exclPrefix = NULL; + ret = style->exclPrefixTab[style->exclPrefixNr]; + style->exclPrefixTab[style->exclPrefixNr] = 0; + return (ret); +} +#endif + +/************************************************************************ + * * + * Helper functions * + * * + ************************************************************************/ + +static int initialized = 0; +/** + * xsltInit: + * + * Initializes the processor (e.g. registers built-in extensions, + * etc.) + */ +void +xsltInit (void) { + if (initialized == 0) { + initialized = 1; +#ifdef XSLT_LOCALE_WINAPI + xsltLocaleMutex = xmlNewRMutex(); +#endif + xsltRegisterAllExtras(); + } +} + +/** + * xsltUninit: + * + * Uninitializes the processor. + */ +void +xsltUninit (void) { +#ifdef XSLT_LOCALE_WINAPI + xmlFreeRMutex(xsltLocaleMutex); + xsltLocaleMutex = NULL; +#endif + initialized = 0; +} + +/** + * xsltIsBlank: + * @str: a string + * + * Check if a string is ignorable + * + * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise + */ +int +xsltIsBlank(xmlChar *str) { + if (str == NULL) + return(1); + while (*str != 0) { + if (!(IS_BLANK(*str))) return(0); + str++; + } + return(1); +} + +/************************************************************************ + * * + * Routines to handle XSLT data structures * + * * + ************************************************************************/ +static xsltDecimalFormatPtr +xsltNewDecimalFormat(xmlChar *name) +{ + xsltDecimalFormatPtr self; + /* UTF-8 for 0x2030 */ + static const xmlChar permille[4] = {0xe2, 0x80, 0xb0, 0}; + + self = xmlMalloc(sizeof(xsltDecimalFormat)); + if (self != NULL) { + self->next = NULL; + self->name = name; + + /* Default values */ + self->digit = xmlStrdup(BAD_CAST("#")); + self->patternSeparator = xmlStrdup(BAD_CAST(";")); + self->decimalPoint = xmlStrdup(BAD_CAST(".")); + self->grouping = xmlStrdup(BAD_CAST(",")); + self->percent = xmlStrdup(BAD_CAST("%")); + self->permille = xmlStrdup(BAD_CAST(permille)); + self->zeroDigit = xmlStrdup(BAD_CAST("0")); + self->minusSign = xmlStrdup(BAD_CAST("-")); + self->infinity = xmlStrdup(BAD_CAST("Infinity")); + self->noNumber = xmlStrdup(BAD_CAST("NaN")); + } + return self; +} + +static void +xsltFreeDecimalFormat(xsltDecimalFormatPtr self) +{ + if (self != NULL) { + if (self->digit) + xmlFree(self->digit); + if (self->patternSeparator) + xmlFree(self->patternSeparator); + if (self->decimalPoint) + xmlFree(self->decimalPoint); + if (self->grouping) + xmlFree(self->grouping); + if (self->percent) + xmlFree(self->percent); + if (self->permille) + xmlFree(self->permille); + if (self->zeroDigit) + xmlFree(self->zeroDigit); + if (self->minusSign) + xmlFree(self->minusSign); + if (self->infinity) + xmlFree(self->infinity); + if (self->noNumber) + xmlFree(self->noNumber); + if (self->name) + xmlFree(self->name); + xmlFree(self); + } +} + +static void +xsltFreeDecimalFormatList(xsltStylesheetPtr self) +{ + xsltDecimalFormatPtr iter; + xsltDecimalFormatPtr tmp; + + if (self == NULL) + return; + + iter = self->decimalFormat; + while (iter != NULL) { + tmp = iter->next; + xsltFreeDecimalFormat(iter); + iter = tmp; + } +} + +/** + * xsltDecimalFormatGetByName: + * @style: the XSLT stylesheet + * @name: the decimal-format name to find + * + * Find decimal-format by name + * + * Returns the xsltDecimalFormatPtr + */ +xsltDecimalFormatPtr +xsltDecimalFormatGetByName(xsltStylesheetPtr style, xmlChar *name) +{ + xsltDecimalFormatPtr result = NULL; + + if (name == NULL) + return style->decimalFormat; + + while (style != NULL) { + for (result = style->decimalFormat->next; + result != NULL; + result = result->next) { + if (xmlStrEqual(name, result->name)) + return result; + } + style = xsltNextImport(style); + } + return result; +} + + +/** + * xsltNewTemplate: + * + * Create a new XSLT Template + * + * Returns the newly allocated xsltTemplatePtr or NULL in case of error + */ +static xsltTemplatePtr +xsltNewTemplate(void) { + xsltTemplatePtr cur; + + cur = (xsltTemplatePtr) xmlMalloc(sizeof(xsltTemplate)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewTemplate : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xsltTemplate)); + cur->priority = XSLT_PAT_NO_PRIORITY; + return(cur); +} + +/** + * xsltFreeTemplate: + * @template: an XSLT template + * + * Free up the memory allocated by @template + */ +static void +xsltFreeTemplate(xsltTemplatePtr template) { + if (template == NULL) + return; + if (template->match) xmlFree(template->match); +/* +* NOTE: @name and @nameURI are put into the string dict now. +* if (template->name) xmlFree(template->name); +* if (template->nameURI) xmlFree(template->nameURI); +*/ +/* + if (template->mode) xmlFree(template->mode); + if (template->modeURI) xmlFree(template->modeURI); + */ + if (template->inheritedNs) xmlFree(template->inheritedNs); + + /* free profiling data */ + if (template->templCalledTab) xmlFree(template->templCalledTab); + if (template->templCountTab) xmlFree(template->templCountTab); + + memset(template, -1, sizeof(xsltTemplate)); + xmlFree(template); +} + +/** + * xsltFreeTemplateList: + * @template: an XSLT template list + * + * Free up the memory allocated by all the elements of @template + */ +static void +xsltFreeTemplateList(xsltTemplatePtr template) { + xsltTemplatePtr cur; + + while (template != NULL) { + cur = template; + template = template->next; + xsltFreeTemplate(cur); + } +} + +#ifdef XSLT_REFACTORED + +static void +xsltFreeNsAliasList(xsltNsAliasPtr item) +{ + xsltNsAliasPtr tmp; + + while (item) { + tmp = item; + item = item->next; + xmlFree(tmp); + } + return; +} + +#ifdef XSLT_REFACTORED_XSLT_NSCOMP +static void +xsltFreeNamespaceMap(xsltNsMapPtr item) +{ + xsltNsMapPtr tmp; + + while (item) { + tmp = item; + item = item->next; + xmlFree(tmp); + } + return; +} + +static xsltNsMapPtr +xsltNewNamespaceMapItem(xsltCompilerCtxtPtr cctxt, + xmlDocPtr doc, + xmlNsPtr ns, + xmlNodePtr elem) +{ + xsltNsMapPtr ret; + + if ((cctxt == NULL) || (doc == NULL) || (ns == NULL)) + return(NULL); + + ret = (xsltNsMapPtr) xmlMalloc(sizeof(xsltNsMap)); + if (ret == NULL) { + xsltTransformError(NULL, cctxt->style, elem, + "Internal error: (xsltNewNamespaceMapItem) " + "memory allocation failed.\n"); + return(NULL); + } + memset(ret, 0, sizeof(xsltNsMap)); + ret->doc = doc; + ret->ns = ns; + ret->origNsName = ns->href; + /* + * Store the item at current stylesheet-level. + */ + if (cctxt->psData->nsMap != NULL) + ret->next = cctxt->psData->nsMap; + cctxt->psData->nsMap = ret; + + return(ret); +} +#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ + +/** + * xsltCompilerVarInfoFree: + * @cctxt: the compilation context + * + * Frees the list of information for vars/params. + */ +static void +xsltCompilerVarInfoFree(xsltCompilerCtxtPtr cctxt) +{ + xsltVarInfoPtr ivar = cctxt->ivars, ivartmp; + + while (ivar) { + ivartmp = ivar; + ivar = ivar->next; + xmlFree(ivartmp); + } +} + +/** + * xsltCompilerCtxtFree: + * + * Free an XSLT compiler context. + */ +static void +xsltCompilationCtxtFree(xsltCompilerCtxtPtr cctxt) +{ + if (cctxt == NULL) + return; +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "Freeing compilation context\n"); + xsltGenericDebug(xsltGenericDebugContext, + "### Max inodes: %d\n", cctxt->maxNodeInfos); + xsltGenericDebug(xsltGenericDebugContext, + "### Max LREs : %d\n", cctxt->maxLREs); +#endif + /* + * Free node-infos. + */ + if (cctxt->inodeList != NULL) { + xsltCompilerNodeInfoPtr tmp, cur = cctxt->inodeList; + while (cur != NULL) { + tmp = cur; + cur = cur->next; + xmlFree(tmp); + } + } + if (cctxt->tmpList != NULL) + xsltPointerListFree(cctxt->tmpList); +#ifdef XSLT_REFACTORED_XPATHCOMP + if (cctxt->xpathCtxt != NULL) + xmlXPathFreeContext(cctxt->xpathCtxt); +#endif + if (cctxt->nsAliases != NULL) + xsltFreeNsAliasList(cctxt->nsAliases); + + if (cctxt->ivars) + xsltCompilerVarInfoFree(cctxt); + + xmlFree(cctxt); +} + +/** + * xsltCompilerCreate: + * + * Creates an XSLT compiler context. + * + * Returns the pointer to the created xsltCompilerCtxt or + * NULL in case of an internal error. + */ +static xsltCompilerCtxtPtr +xsltCompilationCtxtCreate(xsltStylesheetPtr style) { + xsltCompilerCtxtPtr ret; + + ret = (xsltCompilerCtxtPtr) xmlMalloc(sizeof(xsltCompilerCtxt)); + if (ret == NULL) { + xsltTransformError(NULL, style, NULL, + "xsltCompilerCreate: allocation of compiler " + "context failed.\n"); + return(NULL); + } + memset(ret, 0, sizeof(xsltCompilerCtxt)); + + ret->errSeverity = XSLT_ERROR_SEVERITY_ERROR; + ret->tmpList = xsltPointerListCreate(20); + if (ret->tmpList == NULL) { + goto internal_err; + } +#ifdef XSLT_REFACTORED_XPATHCOMP + /* + * Create the XPath compilation context in order + * to speed up precompilation of XPath expressions. + */ + ret->xpathCtxt = xmlXPathNewContext(NULL); + if (ret->xpathCtxt == NULL) + goto internal_err; +#endif + + return(ret); + +internal_err: + xsltCompilationCtxtFree(ret); + return(NULL); +} + +static void +xsltLREEffectiveNsNodesFree(xsltEffectiveNsPtr first) +{ + xsltEffectiveNsPtr tmp; + + while (first != NULL) { + tmp = first; + first = first->nextInStore; + xmlFree(tmp); + } +} + +static void +xsltFreePrincipalStylesheetData(xsltPrincipalStylesheetDataPtr data) +{ + if (data == NULL) + return; + + if (data->inScopeNamespaces != NULL) { + int i; + xsltNsListContainerPtr nsi; + xsltPointerListPtr list = + (xsltPointerListPtr) data->inScopeNamespaces; + + for (i = 0; i < list->number; i++) { + /* + * REVISIT TODO: Free info of in-scope namespaces. + */ + nsi = (xsltNsListContainerPtr) list->items[i]; + if (nsi->list != NULL) + xmlFree(nsi->list); + xmlFree(nsi); + } + xsltPointerListFree(list); + data->inScopeNamespaces = NULL; + } + + if (data->exclResultNamespaces != NULL) { + int i; + xsltPointerListPtr list = (xsltPointerListPtr) + data->exclResultNamespaces; + + for (i = 0; i < list->number; i++) + xsltPointerListFree((xsltPointerListPtr) list->items[i]); + + xsltPointerListFree(list); + data->exclResultNamespaces = NULL; + } + + if (data->extElemNamespaces != NULL) { + xsltPointerListPtr list = (xsltPointerListPtr) + data->extElemNamespaces; + int i; + + for (i = 0; i < list->number; i++) + xsltPointerListFree((xsltPointerListPtr) list->items[i]); + + xsltPointerListFree(list); + data->extElemNamespaces = NULL; + } + if (data->effectiveNs) { + xsltLREEffectiveNsNodesFree(data->effectiveNs); + data->effectiveNs = NULL; + } +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + xsltFreeNamespaceMap(data->nsMap); +#endif + xmlFree(data); +} + +static xsltPrincipalStylesheetDataPtr +xsltNewPrincipalStylesheetData(void) +{ + xsltPrincipalStylesheetDataPtr ret; + + ret = (xsltPrincipalStylesheetDataPtr) + xmlMalloc(sizeof(xsltPrincipalStylesheetData)); + if (ret == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewPrincipalStylesheetData: memory allocation failed.\n"); + return(NULL); + } + memset(ret, 0, sizeof(xsltPrincipalStylesheetData)); + + /* + * Global list of in-scope namespaces. + */ + ret->inScopeNamespaces = xsltPointerListCreate(-1); + if (ret->inScopeNamespaces == NULL) + goto internal_err; + /* + * Global list of excluded result ns-decls. + */ + ret->exclResultNamespaces = xsltPointerListCreate(-1); + if (ret->exclResultNamespaces == NULL) + goto internal_err; + /* + * Global list of extension instruction namespace names. + */ + ret->extElemNamespaces = xsltPointerListCreate(-1); + if (ret->extElemNamespaces == NULL) + goto internal_err; + + return(ret); + +internal_err: + + return(NULL); +} + +#endif + +/** + * xsltNewStylesheet: + * + * Create a new XSLT Stylesheet + * + * Returns the newly allocated xsltStylesheetPtr or NULL in case of error + */ +xsltStylesheetPtr +xsltNewStylesheet(void) { + xsltStylesheetPtr ret = NULL; + + ret = (xsltStylesheetPtr) xmlMalloc(sizeof(xsltStylesheet)); + if (ret == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewStylesheet : malloc failed\n"); + goto internal_err; + } + memset(ret, 0, sizeof(xsltStylesheet)); + + ret->omitXmlDeclaration = -1; + ret->standalone = -1; + ret->decimalFormat = xsltNewDecimalFormat(NULL); + ret->indent = -1; + ret->errors = 0; + ret->warnings = 0; + ret->exclPrefixNr = 0; + ret->exclPrefixMax = 0; + ret->exclPrefixTab = NULL; + ret->extInfos = NULL; + ret->extrasNr = 0; + ret->internalized = 1; + ret->literal_result = 0; + ret->forwards_compatible = 0; + ret->dict = xmlDictCreate(); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "creating dictionary for stylesheet\n"); +#endif + + xsltInit(); + + return(ret); + +internal_err: + if (ret != NULL) + xsltFreeStylesheet(ret); + return(NULL); +} + +/** + * xsltAllocateExtra: + * @style: an XSLT stylesheet + * + * Allocate an extra runtime information slot statically while compiling + * the stylesheet and return its number + * + * Returns the number of the slot + */ +int +xsltAllocateExtra(xsltStylesheetPtr style) +{ + return(style->extrasNr++); +} + +/** + * xsltAllocateExtraCtxt: + * @ctxt: an XSLT transformation context + * + * Allocate an extra runtime information slot at run-time + * and return its number + * This make sure there is a slot ready in the transformation context + * + * Returns the number of the slot + */ +int +xsltAllocateExtraCtxt(xsltTransformContextPtr ctxt) +{ + if (ctxt->extrasNr >= ctxt->extrasMax) { + int i; + if (ctxt->extrasNr == 0) { + ctxt->extrasMax = 20; + ctxt->extras = (xsltRuntimeExtraPtr) + xmlMalloc(ctxt->extrasMax * sizeof(xsltRuntimeExtra)); + if (ctxt->extras == NULL) { + xsltTransformError(ctxt, NULL, NULL, + "xsltAllocateExtraCtxt: out of memory\n"); + return(0); + } + for (i = 0;i < ctxt->extrasMax;i++) { + ctxt->extras[i].info = NULL; + ctxt->extras[i].deallocate = NULL; + ctxt->extras[i].val.ptr = NULL; + } + + } else { + xsltRuntimeExtraPtr tmp; + + ctxt->extrasMax += 100; + tmp = (xsltRuntimeExtraPtr) xmlRealloc(ctxt->extras, + ctxt->extrasMax * sizeof(xsltRuntimeExtra)); + if (tmp == NULL) { + xsltTransformError(ctxt, NULL, NULL, + "xsltAllocateExtraCtxt: out of memory\n"); + return(0); + } + ctxt->extras = tmp; + for (i = ctxt->extrasNr;i < ctxt->extrasMax;i++) { + ctxt->extras[i].info = NULL; + ctxt->extras[i].deallocate = NULL; + ctxt->extras[i].val.ptr = NULL; + } + } + } + return(ctxt->extrasNr++); +} + +/** + * xsltFreeStylesheetList: + * @style: an XSLT stylesheet list + * + * Free up the memory allocated by the list @style + */ +static void +xsltFreeStylesheetList(xsltStylesheetPtr style) { + xsltStylesheetPtr next; + + while (style != NULL) { + next = style->next; + xsltFreeStylesheet(style); + style = next; + } +} + +/** + * xsltCleanupStylesheetTree: + * + * @doc: the document-node + * @node: the element where the stylesheet is rooted at + * + * Actually @node need not be the document-element, but + * currently Libxslt does not support embedded stylesheets. + * + * Returns 0 if OK, -1 on API or internal errors. + */ +static int +xsltCleanupStylesheetTree(xmlDocPtr doc ATTRIBUTE_UNUSED, + xmlNodePtr rootElem ATTRIBUTE_UNUSED) +{ +#if 0 /* TODO: Currently disabled, since probably not needed. */ + xmlNodePtr cur; + + if ((doc == NULL) || (rootElem == NULL) || + (rootElem->type != XML_ELEMENT_NODE) || + (doc != rootElem->doc)) + return(-1); + + /* + * Cleanup was suggested by Aleksey Sanin: + * Clear the PSVI field to avoid problems if the + * node-tree of the stylesheet is intended to be used for + * further processing by the user (e.g. for compiling it + * once again - although not recommended). + */ + + cur = rootElem; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + /* + * Clear the PSVI field. + */ + cur->psvi = NULL; + if (cur->children) { + cur = cur->children; + continue; + } + } + +leave_node: + if (cur == rootElem) + break; + if (cur->next != NULL) + cur = cur->next; + else { + cur = cur->parent; + if (cur == NULL) + break; + goto leave_node; + } + } +#endif /* #if 0 */ + return(0); +} + +/** + * xsltFreeStylesheet: + * @style: an XSLT stylesheet + * + * Free up the memory allocated by @style + */ +void +xsltFreeStylesheet(xsltStylesheetPtr style) +{ + if (style == NULL) + return; + +#ifdef XSLT_REFACTORED + /* + * Start with a cleanup of the main stylesheet's doc. + */ + if ((style->principal == style) && (style->doc)) + xsltCleanupStylesheetTree(style->doc, + xmlDocGetRootElement(style->doc)); +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + /* + * Restore changed ns-decls before freeing the document. + */ + if ((style->doc != NULL) && + XSLT_HAS_INTERNAL_NSMAP(style)) + { + xsltRestoreDocumentNamespaces(XSLT_GET_INTERNAL_NSMAP(style), + style->doc); + } +#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ +#else + /* + * Start with a cleanup of the main stylesheet's doc. + */ + if ((style->parent == NULL) && (style->doc)) + xsltCleanupStylesheetTree(style->doc, + xmlDocGetRootElement(style->doc)); +#endif /* XSLT_REFACTORED */ + + xsltFreeKeys(style); + xsltFreeExts(style); + xsltFreeTemplateHashes(style); + xsltFreeDecimalFormatList(style); + xsltFreeTemplateList(style->templates); + xsltFreeAttributeSetsHashes(style); + xsltFreeNamespaceAliasHashes(style); + xsltFreeStylePreComps(style); + /* + * Free documents of all included stylsheet modules of this + * stylesheet level. + */ + xsltFreeStyleDocuments(style); + /* + * TODO: Best time to shutdown extension stuff? + */ + xsltShutdownExts(style); + + if (style->variables != NULL) + xsltFreeStackElemList(style->variables); + if (style->cdataSection != NULL) + xmlHashFree(style->cdataSection, NULL); + if (style->stripSpaces != NULL) + xmlHashFree(style->stripSpaces, NULL); + if (style->nsHash != NULL) + xmlHashFree(style->nsHash, NULL); + if (style->exclPrefixTab != NULL) + xmlFree(style->exclPrefixTab); + if (style->method != NULL) + xmlFree(style->method); + if (style->methodURI != NULL) + xmlFree(style->methodURI); + if (style->version != NULL) + xmlFree(style->version); + if (style->encoding != NULL) + xmlFree(style->encoding); + if (style->doctypePublic != NULL) + xmlFree(style->doctypePublic); + if (style->doctypeSystem != NULL) + xmlFree(style->doctypeSystem); + if (style->mediaType != NULL) + xmlFree(style->mediaType); + if (style->attVTs) + xsltFreeAVTList(style->attVTs); + if (style->imports != NULL) + xsltFreeStylesheetList(style->imports); + +#ifdef XSLT_REFACTORED + /* + * If this is the principal stylesheet, then + * free its internal data. + */ + if (style->principal == style) { + if (style->principalData) { + xsltFreePrincipalStylesheetData(style->principalData); + style->principalData = NULL; + } + } +#endif + /* + * Better to free the main document of this stylesheet level + * at the end - so here. + */ + if (style->doc != NULL) { + xmlFreeDoc(style->doc); + } + +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "freeing dictionary from stylesheet\n"); +#endif + xmlDictFree(style->dict); + + memset(style, -1, sizeof(xsltStylesheet)); + xmlFree(style); +} + +/************************************************************************ + * * + * Parsing of an XSLT Stylesheet * + * * + ************************************************************************/ + +#ifdef XSLT_REFACTORED + /* + * This is now performed in an optimized way in xsltParseXSLTTemplate. + */ +#else +/** + * xsltGetInheritedNsList: + * @style: the stylesheet + * @template: the template + * @node: the current node + * + * Search all the namespace applying to a given element except the ones + * from excluded output prefixes currently in scope. Initialize the + * template inheritedNs list with it. + * + * Returns the number of entries found + */ +static int +xsltGetInheritedNsList(xsltStylesheetPtr style, + xsltTemplatePtr template, + xmlNodePtr node) +{ + xmlNsPtr cur; + xmlNsPtr *ret = NULL; + int nbns = 0; + int maxns = 10; + int i; + + if ((style == NULL) || (template == NULL) || (node == NULL) || + (template->inheritedNsNr != 0) || (template->inheritedNs != NULL)) + return(0); + while (node != NULL) { + if (node->type == XML_ELEMENT_NODE) { + cur = node->nsDef; + while (cur != NULL) { + if (xmlStrEqual(cur->href, XSLT_NAMESPACE)) + goto skip_ns; + + if ((cur->prefix != NULL) && + (xsltCheckExtPrefix(style, cur->prefix))) + goto skip_ns; + /* + * Check if this namespace was excluded. + * Note that at this point only the exclusions defined + * on the topmost stylesheet element are in the exclusion-list. + */ + for (i = 0;i < style->exclPrefixNr;i++) { + if (xmlStrEqual(cur->href, style->exclPrefixTab[i])) + goto skip_ns; + } + if (ret == NULL) { + ret = + (xmlNsPtr *) xmlMalloc((maxns + 1) * + sizeof(xmlNsPtr)); + if (ret == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltGetInheritedNsList : out of memory!\n"); + return(0); + } + ret[nbns] = NULL; + } + /* + * Skip shadowed namespace bindings. + */ + for (i = 0; i < nbns; i++) { + if ((cur->prefix == ret[i]->prefix) || + (xmlStrEqual(cur->prefix, ret[i]->prefix))) + break; + } + if (i >= nbns) { + if (nbns >= maxns) { + maxns *= 2; + ret = (xmlNsPtr *) xmlRealloc(ret, + (maxns + + 1) * + sizeof(xmlNsPtr)); + if (ret == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltGetInheritedNsList : realloc failed!\n"); + return(0); + } + } + ret[nbns++] = cur; + ret[nbns] = NULL; + } +skip_ns: + cur = cur->next; + } + } + node = node->parent; + } + if (nbns != 0) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "template has %d inherited namespaces\n", nbns); +#endif + template->inheritedNsNr = nbns; + template->inheritedNs = ret; + } + return (nbns); +} +#endif /* else of XSLT_REFACTORED */ + +/** + * xsltParseStylesheetOutput: + * @style: the XSLT stylesheet + * @cur: the "output" element + * + * parse an XSLT stylesheet output element and record + * information related to the stylesheet output + */ + +void +xsltParseStylesheetOutput(xsltStylesheetPtr style, xmlNodePtr cur) +{ + xmlChar *elements, + *prop; + xmlChar *element, + *end; + + if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) + return; + + prop = xmlGetNsProp(cur, (const xmlChar *) "version", NULL); + if (prop != NULL) { + if (style->version != NULL) + xmlFree(style->version); + style->version = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *) "encoding", NULL); + if (prop != NULL) { + if (style->encoding != NULL) + xmlFree(style->encoding); + style->encoding = prop; + } + + /* relaxed to support xt:document + * TODO KB: What does "relaxed to support xt:document" mean? + */ + prop = xmlGetNsProp(cur, (const xmlChar *) "method", NULL); + if (prop != NULL) { + const xmlChar *URI; + + if (style->method != NULL) + xmlFree(style->method); + style->method = NULL; + if (style->methodURI != NULL) + xmlFree(style->methodURI); + style->methodURI = NULL; + + /* + * TODO: Don't use xsltGetQNameURI(). + */ + URI = xsltGetQNameURI(cur, &prop); + if (prop == NULL) { + if (style != NULL) style->errors++; + } else if (URI == NULL) { + if ((xmlStrEqual(prop, (const xmlChar *) "xml")) || + (xmlStrEqual(prop, (const xmlChar *) "html")) || + (xmlStrEqual(prop, (const xmlChar *) "text"))) { + style->method = prop; + } else { + xsltTransformError(NULL, style, cur, + "invalid value for method: %s\n", prop); + if (style != NULL) style->warnings++; + } + } else { + style->method = prop; + style->methodURI = xmlStrdup(URI); + } + } + + prop = xmlGetNsProp(cur, (const xmlChar *) "doctype-system", NULL); + if (prop != NULL) { + if (style->doctypeSystem != NULL) + xmlFree(style->doctypeSystem); + style->doctypeSystem = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *) "doctype-public", NULL); + if (prop != NULL) { + if (style->doctypePublic != NULL) + xmlFree(style->doctypePublic); + style->doctypePublic = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *) "standalone", NULL); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *) "yes")) { + style->standalone = 1; + } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { + style->standalone = 0; + } else { + xsltTransformError(NULL, style, cur, + "invalid value for standalone: %s\n", prop); + style->errors++; + } + xmlFree(prop); + } + + prop = xmlGetNsProp(cur, (const xmlChar *) "indent", NULL); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *) "yes")) { + style->indent = 1; + } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { + style->indent = 0; + } else { + xsltTransformError(NULL, style, cur, + "invalid value for indent: %s\n", prop); + style->errors++; + } + xmlFree(prop); + } + + prop = xmlGetNsProp(cur, (const xmlChar *) "omit-xml-declaration", NULL); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *) "yes")) { + style->omitXmlDeclaration = 1; + } else if (xmlStrEqual(prop, (const xmlChar *) "no")) { + style->omitXmlDeclaration = 0; + } else { + xsltTransformError(NULL, style, cur, + "invalid value for omit-xml-declaration: %s\n", + prop); + style->errors++; + } + xmlFree(prop); + } + + elements = xmlGetNsProp(cur, (const xmlChar *) "cdata-section-elements", + NULL); + if (elements != NULL) { + if (style->cdataSection == NULL) + style->cdataSection = xmlHashCreate(10); + if (style->cdataSection == NULL) + return; + + element = elements; + while (*element != 0) { + while (IS_BLANK(*element)) + element++; + if (*element == 0) + break; + end = element; + while ((*end != 0) && (!IS_BLANK(*end))) + end++; + element = xmlStrndup(element, end - element); + if (element) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "add cdata section output element %s\n", + element); +#endif + if (xmlValidateQName(BAD_CAST element, 0) != 0) { + xsltTransformError(NULL, style, cur, + "Attribute 'cdata-section-elements': The value " + "'%s' is not a valid QName.\n", element); + xmlFree(element); + style->errors++; + } else { + const xmlChar *URI; + + /* + * TODO: Don't use xsltGetQNameURI(). + */ + URI = xsltGetQNameURI(cur, &element); + if (element == NULL) { + /* + * TODO: We'll report additionally an error + * via the stylesheet's error handling. + */ + xsltTransformError(NULL, style, cur, + "Attribute 'cdata-section-elements': The value " + "'%s' is not a valid QName.\n", element); + style->errors++; + } else { + xmlNsPtr ns; + + /* + * XSLT-1.0 "Each QName is expanded into an + * expanded-name using the namespace declarations in + * effect on the xsl:output element in which the QName + * occurs; if there is a default namespace, it is used + * for QNames that do not have a prefix" + * NOTE: Fix of bug #339570. + */ + if (URI == NULL) { + ns = xmlSearchNs(style->doc, cur, NULL); + if (ns != NULL) + URI = ns->href; + } + xmlHashAddEntry2(style->cdataSection, element, URI, + (void *) "cdata"); + xmlFree(element); + } + } + } + element = end; + } + xmlFree(elements); + } + + prop = xmlGetNsProp(cur, (const xmlChar *) "media-type", NULL); + if (prop != NULL) { + if (style->mediaType) + xmlFree(style->mediaType); + style->mediaType = prop; + } + if (cur->children != NULL) { + xsltParseContentError(style, cur->children); + } +} + +/** + * xsltParseStylesheetDecimalFormat: + * @style: the XSLT stylesheet + * @cur: the "decimal-format" element + * + * <!-- Category: top-level-element --> + * <xsl:decimal-format + * name = qname, decimal-separator = char, grouping-separator = char, + * infinity = string, minus-sign = char, NaN = string, percent = char + * per-mille = char, zero-digit = char, digit = char, + * pattern-separator = char /> + * + * parse an XSLT stylesheet decimal-format element and + * and record the formatting characteristics + */ +static void +xsltParseStylesheetDecimalFormat(xsltStylesheetPtr style, xmlNodePtr cur) +{ + xmlChar *prop; + xsltDecimalFormatPtr format; + xsltDecimalFormatPtr iter; + + if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) + return; + + format = style->decimalFormat; + + prop = xmlGetNsProp(cur, BAD_CAST("name"), NULL); + if (prop != NULL) { + format = xsltDecimalFormatGetByName(style, prop); + if (format != NULL) { + xsltTransformError(NULL, style, cur, + "xsltParseStylestyleDecimalFormat: %s already exists\n", prop); + if (style != NULL) style->warnings++; + return; + } + format = xsltNewDecimalFormat(prop); + if (format == NULL) { + xsltTransformError(NULL, style, cur, + "xsltParseStylestyleDecimalFormat: failed creating new decimal-format\n"); + if (style != NULL) style->errors++; + return; + } + /* Append new decimal-format structure */ + for (iter = style->decimalFormat; iter->next; iter = iter->next) + ; + if (iter) + iter->next = format; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"decimal-separator", NULL); + if (prop != NULL) { + if (format->decimalPoint != NULL) xmlFree(format->decimalPoint); + format->decimalPoint = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"grouping-separator", NULL); + if (prop != NULL) { + if (format->grouping != NULL) xmlFree(format->grouping); + format->grouping = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"infinity", NULL); + if (prop != NULL) { + if (format->infinity != NULL) xmlFree(format->infinity); + format->infinity = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"minus-sign", NULL); + if (prop != NULL) { + if (format->minusSign != NULL) xmlFree(format->minusSign); + format->minusSign = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"NaN", NULL); + if (prop != NULL) { + if (format->noNumber != NULL) xmlFree(format->noNumber); + format->noNumber = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"percent", NULL); + if (prop != NULL) { + if (format->percent != NULL) xmlFree(format->percent); + format->percent = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"per-mille", NULL); + if (prop != NULL) { + if (format->permille != NULL) xmlFree(format->permille); + format->permille = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"zero-digit", NULL); + if (prop != NULL) { + if (format->zeroDigit != NULL) xmlFree(format->zeroDigit); + format->zeroDigit = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"digit", NULL); + if (prop != NULL) { + if (format->digit != NULL) xmlFree(format->digit); + format->digit = prop; + } + + prop = xmlGetNsProp(cur, (const xmlChar *)"pattern-separator", NULL); + if (prop != NULL) { + if (format->patternSeparator != NULL) xmlFree(format->patternSeparator); + format->patternSeparator = prop; + } + if (cur->children != NULL) { + xsltParseContentError(style, cur->children); + } +} + +/** + * xsltParseStylesheetPreserveSpace: + * @style: the XSLT stylesheet + * @cur: the "preserve-space" element + * + * parse an XSLT stylesheet preserve-space element and record + * elements needing preserving + */ + +static void +xsltParseStylesheetPreserveSpace(xsltStylesheetPtr style, xmlNodePtr cur) { + xmlChar *elements; + xmlChar *element, *end; + + if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) + return; + + elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL); + if (elements == NULL) { + xsltTransformError(NULL, style, cur, + "xsltParseStylesheetPreserveSpace: missing elements attribute\n"); + if (style != NULL) style->warnings++; + return; + } + + if (style->stripSpaces == NULL) + style->stripSpaces = xmlHashCreate(10); + if (style->stripSpaces == NULL) + return; + + element = elements; + while (*element != 0) { + while (IS_BLANK(*element)) element++; + if (*element == 0) + break; + end = element; + while ((*end != 0) && (!IS_BLANK(*end))) end++; + element = xmlStrndup(element, end - element); + if (element) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "add preserved space element %s\n", element); +#endif + if (xmlStrEqual(element, (const xmlChar *)"*")) { + style->stripAll = -1; + } else { + const xmlChar *URI; + + /* + * TODO: Don't use xsltGetQNameURI(). + */ + URI = xsltGetQNameURI(cur, &element); + + xmlHashAddEntry2(style->stripSpaces, element, URI, + (xmlChar *) "preserve"); + } + xmlFree(element); + } + element = end; + } + xmlFree(elements); + if (cur->children != NULL) { + xsltParseContentError(style, cur->children); + } +} + +#ifdef XSLT_REFACTORED +#else +/** + * xsltParseStylesheetExtPrefix: + * @style: the XSLT stylesheet + * @template: the "extension-element-prefixes" prefix + * + * parse an XSLT stylesheet's "extension-element-prefix" attribute value + * and register the namespaces of extension instruction. + * SPEC "A namespace is designated as an extension namespace by using + * an extension-element-prefixes attribute on: + * 1) an xsl:stylesheet element + * 2) an xsl:extension-element-prefixes attribute on a + * literal result element + * 3) an extension instruction." + */ +static void +xsltParseStylesheetExtPrefix(xsltStylesheetPtr style, xmlNodePtr cur, + int isXsltElem) { + xmlChar *prefixes; + xmlChar *prefix, *end; + + if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) + return; + + if (isXsltElem) { + /* For xsl:stylesheet/xsl:transform. */ + prefixes = xmlGetNsProp(cur, + (const xmlChar *)"extension-element-prefixes", NULL); + } else { + /* For literal result elements and extension instructions. */ + prefixes = xmlGetNsProp(cur, + (const xmlChar *)"extension-element-prefixes", XSLT_NAMESPACE); + } + if (prefixes == NULL) { + return; + } + + prefix = prefixes; + while (*prefix != 0) { + while (IS_BLANK(*prefix)) prefix++; + if (*prefix == 0) + break; + end = prefix; + while ((*end != 0) && (!IS_BLANK(*end))) end++; + prefix = xmlStrndup(prefix, end - prefix); + if (prefix) { + xmlNsPtr ns; + + if (xmlStrEqual(prefix, (const xmlChar *)"#default")) + ns = xmlSearchNs(style->doc, cur, NULL); + else + ns = xmlSearchNs(style->doc, cur, prefix); + if (ns == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:extension-element-prefix : undefined namespace %s\n", + prefix); + if (style != NULL) style->warnings++; + } else { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "add extension prefix %s\n", prefix); +#endif + xsltRegisterExtPrefix(style, prefix, ns->href); + } + xmlFree(prefix); + } + prefix = end; + } + xmlFree(prefixes); +} +#endif /* else of XSLT_REFACTORED */ + +/** + * xsltParseStylesheetStripSpace: + * @style: the XSLT stylesheet + * @cur: the "strip-space" element + * + * parse an XSLT stylesheet's strip-space element and record + * the elements needing stripping + */ + +static void +xsltParseStylesheetStripSpace(xsltStylesheetPtr style, xmlNodePtr cur) { + xmlChar *elements; + xmlChar *element, *end; + + if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) + return; + + elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL); + if (elements == NULL) { + xsltTransformError(NULL, style, cur, + "xsltParseStylesheetStripSpace: missing elements attribute\n"); + if (style != NULL) style->warnings++; + return; + } + + if (style->stripSpaces == NULL) + style->stripSpaces = xmlHashCreate(10); + if (style->stripSpaces == NULL) + return; + + element = elements; + while (*element != 0) { + while (IS_BLANK(*element)) element++; + if (*element == 0) + break; + end = element; + while ((*end != 0) && (!IS_BLANK(*end))) end++; + element = xmlStrndup(element, end - element); + if (element) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "add stripped space element %s\n", element); +#endif + if (xmlStrEqual(element, (const xmlChar *)"*")) { + style->stripAll = 1; + } else { + const xmlChar *URI; + + /* + * TODO: Don't use xsltGetQNameURI(). + */ + URI = xsltGetQNameURI(cur, &element); + + xmlHashAddEntry2(style->stripSpaces, element, URI, + (xmlChar *) "strip"); + } + xmlFree(element); + } + element = end; + } + xmlFree(elements); + if (cur->children != NULL) { + xsltParseContentError(style, cur->children); + } +} + +#ifdef XSLT_REFACTORED +#else +/** + * xsltParseStylesheetExcludePrefix: + * @style: the XSLT stylesheet + * @cur: the current point in the stylesheet + * + * parse an XSLT stylesheet exclude prefix and record + * namespaces needing stripping + * + * Returns the number of Excluded prefixes added at that level + */ + +static int +xsltParseStylesheetExcludePrefix(xsltStylesheetPtr style, xmlNodePtr cur, + int isXsltElem) +{ + int nb = 0; + xmlChar *prefixes; + xmlChar *prefix, *end; + + if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) + return(0); + + if (isXsltElem) + prefixes = xmlGetNsProp(cur, + (const xmlChar *)"exclude-result-prefixes", NULL); + else + prefixes = xmlGetNsProp(cur, + (const xmlChar *)"exclude-result-prefixes", XSLT_NAMESPACE); + + if (prefixes == NULL) { + return(0); + } + + prefix = prefixes; + while (*prefix != 0) { + while (IS_BLANK(*prefix)) prefix++; + if (*prefix == 0) + break; + end = prefix; + while ((*end != 0) && (!IS_BLANK(*end))) end++; + prefix = xmlStrndup(prefix, end - prefix); + if (prefix) { + xmlNsPtr ns; + + if (xmlStrEqual(prefix, (const xmlChar *)"#default")) + ns = xmlSearchNs(style->doc, cur, NULL); + else + ns = xmlSearchNs(style->doc, cur, prefix); + if (ns == NULL) { + xsltTransformError(NULL, style, cur, + "xsl:exclude-result-prefixes : undefined namespace %s\n", + prefix); + if (style != NULL) style->warnings++; + } else { + if (exclPrefixPush(style, (xmlChar *) ns->href) >= 0) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "exclude result prefix %s\n", prefix); +#endif + nb++; + } + } + xmlFree(prefix); + } + prefix = end; + } + xmlFree(prefixes); + return(nb); +} +#endif /* else of XSLT_REFACTORED */ + +#ifdef XSLT_REFACTORED + +/* +* xsltTreeEnsureXMLDecl: +* @doc: the doc +* +* BIG NOTE: +* This was copy&pasted from Libxml2's xmlTreeEnsureXMLDecl() in "tree.c". +* Ensures that there is an XML namespace declaration on the doc. +* +* Returns the XML ns-struct or NULL on API and internal errors. +*/ +static xmlNsPtr +xsltTreeEnsureXMLDecl(xmlDocPtr doc) +{ + if (doc == NULL) + return (NULL); + if (doc->oldNs != NULL) + return (doc->oldNs); + { + xmlNsPtr ns; + ns = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); + if (ns == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltTreeEnsureXMLDecl: Failed to allocate " + "the XML namespace.\n"); + return (NULL); + } + memset(ns, 0, sizeof(xmlNs)); + ns->type = XML_LOCAL_NAMESPACE; + /* + * URGENT TODO: revisit this. + */ +#ifdef LIBXML_NAMESPACE_DICT + if (doc->dict) + ns->href = xmlDictLookup(doc->dict, XML_XML_NAMESPACE, -1); + else + ns->href = xmlStrdup(XML_XML_NAMESPACE); +#else + ns->href = xmlStrdup(XML_XML_NAMESPACE); +#endif + ns->prefix = xmlStrdup((const xmlChar *)"xml"); + doc->oldNs = ns; + return (ns); + } +} + +/* +* xsltTreeAcquireStoredNs: +* @doc: the doc +* @nsName: the namespace name +* @prefix: the prefix +* +* BIG NOTE: +* This was copy&pasted from Libxml2's xmlDOMWrapStoreNs() in "tree.c". +* Creates or reuses an xmlNs struct on doc->oldNs with +* the given prefix and namespace name. +* +* Returns the aquired ns struct or NULL in case of an API +* or internal error. +*/ +static xmlNsPtr +xsltTreeAcquireStoredNs(xmlDocPtr doc, + const xmlChar *nsName, + const xmlChar *prefix) +{ + xmlNsPtr ns; + + if (doc == NULL) + return (NULL); + if (doc->oldNs != NULL) + ns = doc->oldNs; + else + ns = xsltTreeEnsureXMLDecl(doc); + if (ns == NULL) + return (NULL); + if (ns->next != NULL) { + /* Reuse. */ + ns = ns->next; + while (ns != NULL) { + if ((ns->prefix == NULL) != (prefix == NULL)) { + /* NOP */ + } else if (prefix == NULL) { + if (xmlStrEqual(ns->href, nsName)) + return (ns); + } else { + if ((ns->prefix[0] == prefix[0]) && + xmlStrEqual(ns->prefix, prefix) && + xmlStrEqual(ns->href, nsName)) + return (ns); + + } + if (ns->next == NULL) + break; + ns = ns->next; + } + } + /* Create. */ + ns->next = xmlNewNs(NULL, nsName, prefix); + return (ns->next); +} + +/** + * xsltLREBuildEffectiveNs: + * + * Apply ns-aliasing on the namespace of the given @elem and + * its attributes. + */ +static int +xsltLREBuildEffectiveNs(xsltCompilerCtxtPtr cctxt, + xmlNodePtr elem) +{ + xmlNsPtr ns; + xsltNsAliasPtr alias; + + if ((cctxt == NULL) || (elem == NULL)) + return(-1); + if ((cctxt->nsAliases == NULL) || (! cctxt->hasNsAliases)) + return(0); + + alias = cctxt->nsAliases; + while (alias != NULL) { + if ( /* If both namespaces are NULL... */ + ( (elem->ns == NULL) && + ((alias->literalNs == NULL) || + (alias->literalNs->href == NULL)) ) || + /* ... or both namespace are equal */ + ( (elem->ns != NULL) && + (alias->literalNs != NULL) && + xmlStrEqual(elem->ns->href, alias->literalNs->href) ) ) + { + if ((alias->targetNs != NULL) && + (alias->targetNs->href != NULL)) + { + /* + * Convert namespace. + */ + if (elem->doc == alias->docOfTargetNs) { + /* + * This is the nice case: same docs. + * This will eventually assign a ns-decl which + * is shadowed, but this has no negative effect on + * the generation of the result tree. + */ + elem->ns = alias->targetNs; + } else { + /* + * This target xmlNs originates from a different + * stylesheet tree. Try to locate it in the + * in-scope namespaces. + * OPTIMIZE TODO: Use the compiler-node-info inScopeNs. + */ + ns = xmlSearchNs(elem->doc, elem, + alias->targetNs->prefix); + /* + * If no matching ns-decl found, then assign a + * ns-decl stored in xmlDoc. + */ + if ((ns == NULL) || + (! xmlStrEqual(ns->href, alias->targetNs->href))) + { + /* + * BIG NOTE: The use of xsltTreeAcquireStoredNs() + * is not very efficient, but currently I don't + * see an other way of *safely* changing a node's + * namespace, since the xmlNs struct in + * alias->targetNs might come from an other + * stylesheet tree. So we need to anchor it in the + * current document, without adding it to the tree, + * which would otherwise change the in-scope-ns + * semantic of the tree. + */ + ns = xsltTreeAcquireStoredNs(elem->doc, + alias->targetNs->href, + alias->targetNs->prefix); + + if (ns == NULL) { + xsltTransformError(NULL, cctxt->style, elem, + "Internal error in " + "xsltLREBuildEffectiveNs(): " + "failed to acquire a stored " + "ns-declaration.\n"); + cctxt->style->errors++; + return(-1); + + } + } + elem->ns = ns; + } + } else { + /* + * Move into or leave in the NULL namespace. + */ + elem->ns = NULL; + } + break; + } + alias = alias->next; + } + /* + * Same with attributes of literal result elements. + */ + if (elem->properties != NULL) { + xmlAttrPtr attr = elem->properties; + + while (attr != NULL) { + if (attr->ns == NULL) { + attr = attr->next; + continue; + } + alias = cctxt->nsAliases; + while (alias != NULL) { + if ( /* If both namespaces are NULL... */ + ( (elem->ns == NULL) && + ((alias->literalNs == NULL) || + (alias->literalNs->href == NULL)) ) || + /* ... or both namespace are equal */ + ( (elem->ns != NULL) && + (alias->literalNs != NULL) && + xmlStrEqual(elem->ns->href, alias->literalNs->href) ) ) + { + if ((alias->targetNs != NULL) && + (alias->targetNs->href != NULL)) + { + if (elem->doc == alias->docOfTargetNs) { + elem->ns = alias->targetNs; + } else { + ns = xmlSearchNs(elem->doc, elem, + alias->targetNs->prefix); + if ((ns == NULL) || + (! xmlStrEqual(ns->href, alias->targetNs->href))) + { + ns = xsltTreeAcquireStoredNs(elem->doc, + alias->targetNs->href, + alias->targetNs->prefix); + + if (ns == NULL) { + xsltTransformError(NULL, cctxt->style, elem, + "Internal error in " + "xsltLREBuildEffectiveNs(): " + "failed to acquire a stored " + "ns-declaration.\n"); + cctxt->style->errors++; + return(-1); + + } + } + elem->ns = ns; + } + } else { + /* + * Move into or leave in the NULL namespace. + */ + elem->ns = NULL; + } + break; + } + alias = alias->next; + } + + attr = attr->next; + } + } + return(0); +} + +/** + * xsltLREBuildEffectiveNsNodes: + * + * Computes the effective namespaces nodes for a literal result + * element. + * @effectiveNs is the set of effective ns-nodes + * on the literal result element, which will be added to the result + * element if not already existing in the result tree. + * This means that excluded namespaces (via exclude-result-prefixes, + * extension-element-prefixes and the XSLT namespace) not added + * to the set. + * Namespace-aliasing was applied on the @effectiveNs. + */ +static int +xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt, + xsltStyleItemLRElementInfoPtr item, + xmlNodePtr elem, + int isLRE) +{ + xmlNsPtr ns, tmpns; + xsltEffectiveNsPtr effNs, lastEffNs = NULL; + int i, j, holdByElem; + xsltPointerListPtr extElemNs = cctxt->inode->extElemNs; + xsltPointerListPtr exclResultNs = cctxt->inode->exclResultNs; + + if ((cctxt == NULL) || (cctxt->inode == NULL) || (elem == NULL) || + (item == NULL) || (item->effectiveNs != NULL)) + return(-1); + + if (item->inScopeNs == NULL) + return(0); + + extElemNs = cctxt->inode->extElemNs; + exclResultNs = cctxt->inode->exclResultNs; + + for (i = 0; i < item->inScopeNs->totalNumber; i++) { + ns = item->inScopeNs->list[i]; + /* + * Skip namespaces designated as excluded namespaces + * ------------------------------------------------- + * + * XSLT-20 TODO: In XSLT 2.0 we need to keep namespaces + * which are target namespaces of namespace-aliases + * regardless if designated as excluded. + * + * Exclude the XSLT namespace. + */ + if (xmlStrEqual(ns->href, XSLT_NAMESPACE)) + goto skip_ns; + + /* + * Apply namespace aliasing + * ------------------------ + * + * SPEC XSLT 2.0 + * "- A namespace node whose string value is a literal namespace + * URI is not copied to the result tree. + * - A namespace node whose string value is a target namespace URI + * is copied to the result tree, whether or not the URI + * identifies an excluded namespace." + * + * NOTE: The ns-aliasing machanism is non-cascading. + * (checked with Saxon, Xalan and MSXML .NET). + * URGENT TODO: is style->nsAliases the effective list of + * ns-aliases, or do we need to lookup the whole + * import-tree? + * TODO: Get rid of import-tree lookup. + */ + if (cctxt->hasNsAliases) { + xsltNsAliasPtr alias; + /* + * First check for being a target namespace. + */ + alias = cctxt->nsAliases; + do { + /* + * TODO: Is xmlns="" handled already? + */ + if ((alias->targetNs != NULL) && + (xmlStrEqual(alias->targetNs->href, ns->href))) + { + /* + * Recognized as a target namespace; use it regardless + * if excluded otherwise. + */ + goto add_effective_ns; + } + alias = alias->next; + } while (alias != NULL); + + alias = cctxt->nsAliases; + do { + /* + * TODO: Is xmlns="" handled already? + */ + if ((alias->literalNs != NULL) && + (xmlStrEqual(alias->literalNs->href, ns->href))) + { + /* + * Recognized as an namespace alias; do not use it. + */ + goto skip_ns; + } + alias = alias->next; + } while (alias != NULL); + } + + /* + * Exclude excluded result namespaces. + */ + if (exclResultNs) { + for (j = 0; j < exclResultNs->number; j++) + if (xmlStrEqual(ns->href, BAD_CAST exclResultNs->items[j])) + goto skip_ns; + } + /* + * Exclude extension-element namespaces. + */ + if (extElemNs) { + for (j = 0; j < extElemNs->number; j++) + if (xmlStrEqual(ns->href, BAD_CAST extElemNs->items[j])) + goto skip_ns; + } + +add_effective_ns: + /* + * OPTIMIZE TODO: This information may not be needed. + */ + if (isLRE && (elem->nsDef != NULL)) { + holdByElem = 0; + tmpns = elem->nsDef; + do { + if (tmpns == ns) { + holdByElem = 1; + break; + } + tmpns = tmpns->next; + } while (tmpns != NULL); + } else + holdByElem = 0; + + + /* + * Add the effective namespace declaration. + */ + effNs = (xsltEffectiveNsPtr) xmlMalloc(sizeof(xsltEffectiveNs)); + if (effNs == NULL) { + xsltTransformError(NULL, cctxt->style, elem, + "Internal error in xsltLREBuildEffectiveNs(): " + "failed to allocate memory.\n"); + cctxt->style->errors++; + return(-1); + } + if (cctxt->psData->effectiveNs == NULL) { + cctxt->psData->effectiveNs = effNs; + effNs->nextInStore = NULL; + } else { + effNs->nextInStore = cctxt->psData->effectiveNs; + cctxt->psData->effectiveNs = effNs; + } + + effNs->next = NULL; + effNs->prefix = ns->prefix; + effNs->nsName = ns->href; + effNs->holdByElem = holdByElem; + + if (lastEffNs == NULL) + item->effectiveNs = effNs; + else + lastEffNs->next = effNs; + lastEffNs = effNs; + +skip_ns: + {} + } + return(0); +} + + +/** + * xsltLREInfoCreate: + * + * @isLRE: indicates if the given @elem is a literal result element + * + * Creates a new info for a literal result element. + */ +static int +xsltLREInfoCreate(xsltCompilerCtxtPtr cctxt, + xmlNodePtr elem, + int isLRE) +{ + xsltStyleItemLRElementInfoPtr item; + + if ((cctxt == NULL) || (cctxt->inode == NULL)) + return(-1); + + item = (xsltStyleItemLRElementInfoPtr) + xmlMalloc(sizeof(xsltStyleItemLRElementInfo)); + if (item == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "Internal error in xsltLREInfoCreate(): " + "memory allocation failed.\n"); + cctxt->style->errors++; + return(-1); + } + memset(item, 0, sizeof(xsltStyleItemLRElementInfo)); + item->type = XSLT_FUNC_LITERAL_RESULT_ELEMENT; + /* + * Store it in the stylesheet. + */ + item->next = cctxt->style->preComps; + cctxt->style->preComps = (xsltElemPreCompPtr) item; + /* + * @inScopeNs are used for execution of XPath expressions + * in AVTs. + */ + item->inScopeNs = cctxt->inode->inScopeNs; + + if (elem) + xsltLREBuildEffectiveNsNodes(cctxt, item, elem, isLRE); + + cctxt->inode->litResElemInfo = item; + cctxt->inode->nsChanged = 0; + cctxt->maxLREs++; + return(0); +} + +/** + * xsltCompilerVarInfoPush: + * @cctxt: the compilation context + * + * Pushes a new var/param info onto the stack. + * + * Returns the acquired variable info. + */ +static xsltVarInfoPtr +xsltCompilerVarInfoPush(xsltCompilerCtxtPtr cctxt, + xmlNodePtr inst, + const xmlChar *name, + const xmlChar *nsName) +{ + xsltVarInfoPtr ivar; + + if ((cctxt->ivar != NULL) && (cctxt->ivar->next != NULL)) { + ivar = cctxt->ivar->next; + } else if ((cctxt->ivar == NULL) && (cctxt->ivars != NULL)) { + ivar = cctxt->ivars; + } else { + ivar = (xsltVarInfoPtr) xmlMalloc(sizeof(xsltVarInfo)); + if (ivar == NULL) { + xsltTransformError(NULL, cctxt->style, inst, + "xsltParseInScopeVarPush: xmlMalloc() failed!\n"); + cctxt->style->errors++; + return(NULL); + } + /* memset(retVar, 0, sizeof(xsltInScopeVar)); */ + if (cctxt->ivars == NULL) { + cctxt->ivars = ivar; + ivar->prev = NULL; + } else { + cctxt->ivar->next = ivar; + ivar->prev = cctxt->ivar; + } + cctxt->ivar = ivar; + ivar->next = NULL; + } + ivar->depth = cctxt->depth; + ivar->name = name; + ivar->nsName = nsName; + return(ivar); +} + +/** + * xsltCompilerVarInfoPop: + * @cctxt: the compilation context + * + * Pops all var/param infos from the stack, which + * have the current depth. + */ +static void +xsltCompilerVarInfoPop(xsltCompilerCtxtPtr cctxt) +{ + + while ((cctxt->ivar != NULL) && + (cctxt->ivar->depth > cctxt->depth)) + { + cctxt->ivar = cctxt->ivar->prev; + } +} + +/* +* xsltCompilerNodePush: +* +* @cctxt: the compilation context +* @node: the node to be pushed (this can also be the doc-node) +* +* +* +* Returns the current node info structure or +* NULL in case of an internal error. +*/ +static xsltCompilerNodeInfoPtr +xsltCompilerNodePush(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) +{ + xsltCompilerNodeInfoPtr inode, iprev; + + if ((cctxt->inode != NULL) && (cctxt->inode->next != NULL)) { + inode = cctxt->inode->next; + } else if ((cctxt->inode == NULL) && (cctxt->inodeList != NULL)) { + inode = cctxt->inodeList; + } else { + /* + * Create a new node-info. + */ + inode = (xsltCompilerNodeInfoPtr) + xmlMalloc(sizeof(xsltCompilerNodeInfo)); + if (inode == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "xsltCompilerNodePush: malloc failed.\n"); + return(NULL); + } + memset(inode, 0, sizeof(xsltCompilerNodeInfo)); + if (cctxt->inodeList == NULL) + cctxt->inodeList = inode; + else { + cctxt->inodeLast->next = inode; + inode->prev = cctxt->inodeLast; + } + cctxt->inodeLast = inode; + cctxt->maxNodeInfos++; + if (cctxt->inode == NULL) { + cctxt->inode = inode; + /* + * Create an initial literal result element info for + * the root of the stylesheet. + */ + xsltLREInfoCreate(cctxt, NULL, 0); + } + } + cctxt->depth++; + cctxt->inode = inode; + /* + * REVISIT TODO: Keep the reset always complete. + * NOTE: Be carefull with the @node, since it might be + * a doc-node. + */ + inode->node = node; + inode->depth = cctxt->depth; + inode->templ = NULL; + inode->category = XSLT_ELEMENT_CATEGORY_XSLT; + inode->type = 0; + inode->item = NULL; + inode->curChildType = 0; + inode->extContentHandled = 0; + inode->isRoot = 0; + + if (inode->prev != NULL) { + iprev = inode->prev; + /* + * Inherit the following information: + * --------------------------------- + * + * In-scope namespaces + */ + inode->inScopeNs = iprev->inScopeNs; + /* + * Info for literal result elements + */ + inode->litResElemInfo = iprev->litResElemInfo; + inode->nsChanged = iprev->nsChanged; + /* + * Excluded result namespaces + */ + inode->exclResultNs = iprev->exclResultNs; + /* + * Extension instruction namespaces + */ + inode->extElemNs = iprev->extElemNs; + /* + * Whitespace preservation + */ + inode->preserveWhitespace = iprev->preserveWhitespace; + /* + * Forwards-compatible mode + */ + inode->forwardsCompat = iprev->forwardsCompat; + } else { + inode->inScopeNs = NULL; + inode->exclResultNs = NULL; + inode->extElemNs = NULL; + inode->preserveWhitespace = 0; + inode->forwardsCompat = 0; + } + + return(inode); +} + +/* +* xsltCompilerNodePop: +* +* @cctxt: the compilation context +* @node: the node to be pushed (this can also be the doc-node) +* +* Pops the current node info. +*/ +static void +xsltCompilerNodePop(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) +{ + if (cctxt->inode == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltCompilerNodePop: Top-node mismatch.\n"); + return; + } + /* + * NOTE: Be carefull with the @node, since it might be + * a doc-node. + */ + if (cctxt->inode->node != node) { + xmlGenericError(xmlGenericErrorContext, + "xsltCompilerNodePop: Node mismatch.\n"); + goto mismatch; + } + if (cctxt->inode->depth != cctxt->depth) { + xmlGenericError(xmlGenericErrorContext, + "xsltCompilerNodePop: Depth mismatch.\n"); + goto mismatch; + } + cctxt->depth--; + /* + * Pop information of variables. + */ + if ((cctxt->ivar) && (cctxt->ivar->depth > cctxt->depth)) + xsltCompilerVarInfoPop(cctxt); + + cctxt->inode = cctxt->inode->prev; + if (cctxt->inode != NULL) + cctxt->inode->curChildType = 0; + return; + +mismatch: + { + const xmlChar *nsName = NULL, *name = NULL; + const xmlChar *infnsName = NULL, *infname = NULL; + + if (node) { + if (node->type == XML_ELEMENT_NODE) { + name = node->name; + if (node->ns != NULL) + nsName = node->ns->href; + else + nsName = BAD_CAST ""; + } else { + name = BAD_CAST "#document"; + nsName = BAD_CAST ""; + } + } else + name = BAD_CAST "Not given"; + + if (cctxt->inode->node) { + if (node->type == XML_ELEMENT_NODE) { + infname = cctxt->inode->node->name; + if (cctxt->inode->node->ns != NULL) + infnsName = cctxt->inode->node->ns->href; + else + infnsName = BAD_CAST ""; + } else { + infname = BAD_CAST "#document"; + infnsName = BAD_CAST ""; + } + } else + infname = BAD_CAST "Not given"; + + + xmlGenericError(xmlGenericErrorContext, + "xsltCompilerNodePop: Given : '%s' URI '%s'\n", + name, nsName); + xmlGenericError(xmlGenericErrorContext, + "xsltCompilerNodePop: Expected: '%s' URI '%s'\n", + infname, infnsName); + } +} + +/* +* xsltCompilerBuildInScopeNsList: +* +* Create and store the list of in-scope namespaces for the given +* node in the stylesheet. If there are no changes in the in-scope +* namespaces then the last ns-info of the ancestor axis will be returned. +* Compilation-time only. +* +* Returns the ns-info or NULL if there are no namespaces in scope. +*/ +static xsltNsListContainerPtr +xsltCompilerBuildInScopeNsList(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) +{ + xsltNsListContainerPtr nsi = NULL; + xmlNsPtr *list = NULL, ns; + int i, maxns = 5; + /* + * Create a new ns-list for this position in the node-tree. + * xmlGetNsList() will return NULL, if there are no ns-decls in the + * tree. Note that the ns-decl for the XML namespace is not added + * to the resulting list; the XPath module handles the XML namespace + * internally. + */ + while (node != NULL) { + if (node->type == XML_ELEMENT_NODE) { + ns = node->nsDef; + while (ns != NULL) { + if (nsi == NULL) { + nsi = (xsltNsListContainerPtr) + xmlMalloc(sizeof(xsltNsListContainer)); + if (nsi == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "xsltCompilerBuildInScopeNsList: " + "malloc failed!\n"); + goto internal_err; + } + memset(nsi, 0, sizeof(xsltNsListContainer)); + nsi->list = + (xmlNsPtr *) xmlMalloc(maxns * sizeof(xmlNsPtr)); + if (nsi->list == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "xsltCompilerBuildInScopeNsList: " + "malloc failed!\n"); + goto internal_err; + } + nsi->list[0] = NULL; + } + /* + * Skip shadowed namespace bindings. + */ + for (i = 0; i < nsi->totalNumber; i++) { + if ((ns->prefix == nsi->list[i]->prefix) || + (xmlStrEqual(ns->prefix, nsi->list[i]->prefix))) + break; + } + if (i >= nsi->totalNumber) { + if (nsi->totalNumber +1 >= maxns) { + maxns *= 2; + nsi->list = + (xmlNsPtr *) xmlRealloc(nsi->list, + maxns * sizeof(xmlNsPtr)); + if (nsi->list == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "xsltCompilerBuildInScopeNsList: " + "realloc failed!\n"); + goto internal_err; + } + } + nsi->list[nsi->totalNumber++] = ns; + nsi->list[nsi->totalNumber] = NULL; + } + + ns = ns->next; + } + } + node = node->parent; + } + if (nsi == NULL) + return(NULL); + /* + * Move the default namespace to last position. + */ + nsi->xpathNumber = nsi->totalNumber; + for (i = 0; i < nsi->totalNumber; i++) { + if (nsi->list[i]->prefix == NULL) { + ns = nsi->list[i]; + nsi->list[i] = nsi->list[nsi->totalNumber-1]; + nsi->list[nsi->totalNumber-1] = ns; + nsi->xpathNumber--; + break; + } + } + /* + * Store the ns-list in the stylesheet. + */ + if (xsltPointerListAddSize( + (xsltPointerListPtr)cctxt->psData->inScopeNamespaces, + (void *) nsi, 5) == -1) + { + xmlFree(nsi); + nsi = NULL; + xsltTransformError(NULL, cctxt->style, NULL, + "xsltCompilerBuildInScopeNsList: failed to add ns-info.\n"); + goto internal_err; + } + /* + * Notify of change in status wrt namespaces. + */ + if (cctxt->inode != NULL) + cctxt->inode->nsChanged = 1; + + return(nsi); + +internal_err: + if (list != NULL) + xmlFree(list); + cctxt->style->errors++; + return(NULL); +} + +static int +xsltParseNsPrefixList(xsltCompilerCtxtPtr cctxt, + xsltPointerListPtr list, + xmlNodePtr node, + const xmlChar *value) +{ + xmlChar *cur, *end; + xmlNsPtr ns; + + if ((cctxt == NULL) || (value == NULL) || (list == NULL)) + return(-1); + + list->number = 0; + + cur = (xmlChar *) value; + while (*cur != 0) { + while (IS_BLANK(*cur)) cur++; + if (*cur == 0) + break; + end = cur; + while ((*end != 0) && (!IS_BLANK(*end))) end++; + cur = xmlStrndup(cur, end - cur); + if (cur == NULL) { + cur = end; + continue; + } + /* + * TODO: Export and use xmlSearchNsByPrefixStrict() + * in Libxml2, tree.c, since xmlSearchNs() is in most + * cases not efficient and in some cases not correct. + * + * XSLT-2 TODO: XSLT 2.0 allows an additional "#all" value. + */ + if ((cur[0] == '#') && + xmlStrEqual(cur, (const xmlChar *)"#default")) + ns = xmlSearchNs(cctxt->style->doc, node, NULL); + else + ns = xmlSearchNs(cctxt->style->doc, node, cur); + + if (ns == NULL) { + /* + * TODO: Better to report the attr-node, otherwise + * the user won't know which attribute was invalid. + */ + xsltTransformError(NULL, cctxt->style, node, + "No namespace binding in scope for prefix '%s'.\n", cur); + /* + * XSLT-1.0: "It is an error if there is no namespace + * bound to the prefix on the element bearing the + * exclude-result-prefixes or xsl:exclude-result-prefixes + * attribute." + */ + cctxt->style->errors++; + } else { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "resolved prefix '%s'\n", cur); +#endif + /* + * Note that we put the namespace name into the dict. + */ + if (xsltPointerListAddSize(list, + (void *) xmlDictLookup(cctxt->style->dict, + ns->href, -1), 5) == -1) + { + xmlFree(cur); + goto internal_err; + } + } + xmlFree(cur); + + cur = end; + } + return(0); + +internal_err: + cctxt->style->errors++; + return(-1); +} + +/** + * xsltCompilerUtilsCreateMergedList: + * @dest: the destination list (optional) + * @first: the first list + * @second: the second list (optional) + * + * Appends the content of @second to @first into @destination. + * If @destination is NULL a new list will be created. + * + * Returns the merged list of items or NULL if there's nothing to merge. + */ +static xsltPointerListPtr +xsltCompilerUtilsCreateMergedList(xsltPointerListPtr first, + xsltPointerListPtr second) +{ + xsltPointerListPtr ret; + size_t num; + + if (first) + num = first->number; + else + num = 0; + if (second) + num += second->number; + if (num == 0) + return(NULL); + ret = xsltPointerListCreate(num); + if (ret == NULL) + return(NULL); + /* + * Copy contents. + */ + if ((first != NULL) && (first->number != 0)) { + memcpy(ret->items, first->items, + first->number * sizeof(void *)); + if ((second != NULL) && (second->number != 0)) + memcpy(ret->items + first->number, second->items, + second->number * sizeof(void *)); + } else if ((second != NULL) && (second->number != 0)) + memcpy(ret->items, (void *) second->items, + second->number * sizeof(void *)); + ret->number = num; + return(ret); +} + +/* +* xsltParseExclResultPrefixes: +* +* Create and store the list of in-scope namespaces for the given +* node in the stylesheet. If there are no changes in the in-scope +* namespaces then the last ns-info of the ancestor axis will be returned. +* Compilation-time only. +* +* Returns the ns-info or NULL if there are no namespaces in scope. +*/ +static xsltPointerListPtr +xsltParseExclResultPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, + xsltPointerListPtr def, + int instrCategory) +{ + xsltPointerListPtr list = NULL; + xmlChar *value; + xmlAttrPtr attr; + + if ((cctxt == NULL) || (node == NULL)) + return(NULL); + + if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT) + attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes", NULL); + else + attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes", + XSLT_NAMESPACE); + if (attr == NULL) + return(def); + + if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) { + /* + * Mark the XSLT attr. + */ + attr->psvi = (void *) xsltXSLTAttrMarker; + } + + if ((attr->children != NULL) && + (attr->children->content != NULL)) + value = attr->children->content; + else { + xsltTransformError(NULL, cctxt->style, node, + "Attribute 'exclude-result-prefixes': Invalid value.\n"); + cctxt->style->errors++; + return(def); + } + + if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node, + BAD_CAST value) != 0) + goto exit; + if (cctxt->tmpList->number == 0) + goto exit; + /* + * Merge the list with the inherited list. + */ + list = xsltCompilerUtilsCreateMergedList(def, cctxt->tmpList); + if (list == NULL) + goto exit; + /* + * Store the list in the stylesheet/compiler context. + */ + if (xsltPointerListAddSize( + cctxt->psData->exclResultNamespaces, list, 5) == -1) + { + xsltPointerListFree(list); + list = NULL; + goto exit; + } + /* + * Notify of change in status wrt namespaces. + */ + if (cctxt->inode != NULL) + cctxt->inode->nsChanged = 1; + +exit: + if (list != NULL) + return(list); + else + return(def); +} + +/* +* xsltParseExtElemPrefixes: +* +* Create and store the list of in-scope namespaces for the given +* node in the stylesheet. If there are no changes in the in-scope +* namespaces then the last ns-info of the ancestor axis will be returned. +* Compilation-time only. +* +* Returns the ns-info or NULL if there are no namespaces in scope. +*/ +static xsltPointerListPtr +xsltParseExtElemPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, + xsltPointerListPtr def, + int instrCategory) +{ + xsltPointerListPtr list = NULL; + xmlAttrPtr attr; + xmlChar *value; + int i; + + if ((cctxt == NULL) || (node == NULL)) + return(NULL); + + if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT) + attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes", NULL); + else + attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes", + XSLT_NAMESPACE); + if (attr == NULL) + return(def); + + if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) { + /* + * Mark the XSLT attr. + */ + attr->psvi = (void *) xsltXSLTAttrMarker; + } + + if ((attr->children != NULL) && + (attr->children->content != NULL)) + value = attr->children->content; + else { + xsltTransformError(NULL, cctxt->style, node, + "Attribute 'extension-element-prefixes': Invalid value.\n"); + cctxt->style->errors++; + return(def); + } + + + if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node, + BAD_CAST value) != 0) + goto exit; + + if (cctxt->tmpList->number == 0) + goto exit; + /* + * REVISIT: Register the extension namespaces. + */ + for (i = 0; i < cctxt->tmpList->number; i++) + xsltRegisterExtPrefix(cctxt->style, NULL, + BAD_CAST cctxt->tmpList->items[i]); + /* + * Merge the list with the inherited list. + */ + list = xsltCompilerUtilsCreateMergedList(def, cctxt->tmpList); + if (list == NULL) + goto exit; + /* + * Store the list in the stylesheet. + */ + if (xsltPointerListAddSize( + cctxt->psData->extElemNamespaces, list, 5) == -1) + { + xsltPointerListFree(list); + list = NULL; + goto exit; + } + /* + * Notify of change in status wrt namespaces. + */ + if (cctxt->inode != NULL) + cctxt->inode->nsChanged = 1; + +exit: + if (list != NULL) + return(list); + else + return(def); +} + +/* +* xsltParseAttrXSLTVersion: +* +* @cctxt: the compilation context +* @node: the element-node +* @isXsltElem: whether this is an XSLT element +* +* Parses the attribute xsl:version. +* +* Returns 1 if there was such an attribute, 0 if not and +* -1 if an internal or API error occured. +*/ +static int +xsltParseAttrXSLTVersion(xsltCompilerCtxtPtr cctxt, xmlNodePtr node, + int instrCategory) +{ + xmlChar *value; + xmlAttrPtr attr; + + if ((cctxt == NULL) || (node == NULL)) + return(-1); + + if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT) + attr = xmlHasNsProp(node, BAD_CAST "version", NULL); + else + attr = xmlHasNsProp(node, BAD_CAST "version", XSLT_NAMESPACE); + + if (attr == NULL) + return(0); + + attr->psvi = (void *) xsltXSLTAttrMarker; + + if ((attr->children != NULL) && + (attr->children->content != NULL)) + value = attr->children->content; + else { + xsltTransformError(NULL, cctxt->style, node, + "Attribute 'version': Invalid value.\n"); + cctxt->style->errors++; + return(1); + } + + if (! xmlStrEqual(value, (const xmlChar *)"1.0")) { + cctxt->inode->forwardsCompat = 1; + /* + * TODO: To what extent do we support the + * forwards-compatible mode? + */ + /* + * Report this only once per compilation episode. + */ + if (! cctxt->hasForwardsCompat) { + cctxt->hasForwardsCompat = 1; + cctxt->errSeverity = XSLT_ERROR_SEVERITY_WARNING; + xsltTransformError(NULL, cctxt->style, node, + "Warning: the attribute xsl:version specifies a value " + "different from '1.0'. Switching to forwards-compatible " + "mode. Only features of XSLT 1.0 are supported by this " + "processor.\n"); + cctxt->style->warnings++; + cctxt->errSeverity = XSLT_ERROR_SEVERITY_ERROR; + } + } else { + cctxt->inode->forwardsCompat = 0; + } + + if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) { + /* + * Set a marker on XSLT attributes. + */ + attr->psvi = (void *) xsltXSLTAttrMarker; + } + return(1); +} + +static int +xsltParsePreprocessStylesheetTree(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) +{ + xmlNodePtr deleteNode, cur, txt, textNode = NULL; + xmlDocPtr doc; + xsltStylesheetPtr style; + int internalize = 0, findSpaceAttr; + int xsltStylesheetElemDepth; + xmlAttrPtr attr; + xmlChar *value; + const xmlChar *name, *nsNameXSLT = NULL; + int strictWhitespace, inXSLText = 0; +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + xsltNsMapPtr nsMapItem; +#endif + + if ((cctxt == NULL) || (cctxt->style == NULL) || + (node == NULL) || (node->type != XML_ELEMENT_NODE)) + return(-1); + + doc = node->doc; + if (doc == NULL) + goto internal_err; + + style = cctxt->style; + if ((style->dict != NULL) && (doc->dict == style->dict)) + internalize = 1; + else + style->internalized = 0; + + /* + * Init value of xml:space. Since this might be an embedded + * stylesheet, this is needed to be performed on the element + * where the stylesheet is rooted at, taking xml:space of + * ancestors into account. + */ + if (! cctxt->simplified) + xsltStylesheetElemDepth = cctxt->depth +1; + else + xsltStylesheetElemDepth = 0; + + if (xmlNodeGetSpacePreserve(node) != 1) + cctxt->inode->preserveWhitespace = 0; + else + cctxt->inode->preserveWhitespace = 1; + + /* + * Eval if we should keep the old incorrect behaviour. + */ + strictWhitespace = (cctxt->strict != 0) ? 1 : 0; + + nsNameXSLT = xsltConstNamespaceNameXSLT; + + deleteNode = NULL; + cur = node; + while (cur != NULL) { + if (deleteNode != NULL) { + +#ifdef WITH_XSLT_DEBUG_BLANKS + xsltGenericDebug(xsltGenericDebugContext, + "xsltParsePreprocessStylesheetTree: removing node\n"); +#endif + xmlUnlinkNode(deleteNode); + xmlFreeNode(deleteNode); + deleteNode = NULL; + } + if (cur->type == XML_ELEMENT_NODE) { + + /* + * Clear the PSVI field. + */ + cur->psvi = NULL; + + xsltCompilerNodePush(cctxt, cur); + + inXSLText = 0; + textNode = NULL; + findSpaceAttr = 1; + cctxt->inode->stripWhitespace = 0; + /* + * TODO: I'd love to use a string pointer comparison here :-/ + */ + if (IS_XSLT_ELEM(cur)) { +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + if (cur->ns->href != nsNameXSLT) { + nsMapItem = xsltNewNamespaceMapItem(cctxt, + doc, cur->ns, cur); + if (nsMapItem == NULL) + goto internal_err; + cur->ns->href = nsNameXSLT; + } +#endif + + if (cur->name == NULL) + goto process_attributes; + /* + * Mark the XSLT element for later recognition. + * TODO: Using the marker is still too dangerous, since if + * the parsing mechanism leaves out an XSLT element, then + * this might hit the transformation-mechanism, which + * will break if it doesn't expect such a marker. + */ + /* cur->psvi = (void *) xsltXSLTElemMarker; */ + + /* + * XSLT 2.0: "Any whitespace text node whose parent is + * one of the following elements is removed from the " + * tree, regardless of any xml:space attributes:..." + * xsl:apply-imports, + * xsl:apply-templates, + * xsl:attribute-set, + * xsl:call-template, + * xsl:choose, + * xsl:stylesheet, xsl:transform. + * XSLT 2.0: xsl:analyze-string, + * xsl:character-map, + * xsl:next-match + * + * TODO: I'd love to use a string pointer comparison here :-/ + */ + name = cur->name; + switch (*name) { + case 't': + if ((name[0] == 't') && (name[1] == 'e') && + (name[2] == 'x') && (name[3] == 't') && + (name[4] == 0)) + { + /* + * Process the xsl:text element. + * ---------------------------- + * Mark it for later recognition. + */ + cur->psvi = (void *) xsltXSLTTextMarker; + /* + * For stylesheets, the set of + * whitespace-preserving element names + * consists of just xsl:text. + */ + findSpaceAttr = 0; + cctxt->inode->preserveWhitespace = 1; + inXSLText = 1; + } + break; + case 'c': + if (xmlStrEqual(name, BAD_CAST "choose") || + xmlStrEqual(name, BAD_CAST "call-template")) + cctxt->inode->stripWhitespace = 1; + break; + case 'a': + if (xmlStrEqual(name, BAD_CAST "apply-templates") || + xmlStrEqual(name, BAD_CAST "apply-imports") || + xmlStrEqual(name, BAD_CAST "attribute-set")) + + cctxt->inode->stripWhitespace = 1; + break; + default: + if (xsltStylesheetElemDepth == cctxt->depth) { + /* + * This is a xsl:stylesheet/xsl:transform. + */ + cctxt->inode->stripWhitespace = 1; + break; + } + + if ((cur->prev != NULL) && + (cur->prev->type == XML_TEXT_NODE)) + { + /* + * XSLT 2.0 : "Any whitespace text node whose + * following-sibling node is an xsl:param or + * xsl:sort element is removed from the tree, + * regardless of any xml:space attributes." + */ + if (((*name == 'p') || (*name == 's')) && + (xmlStrEqual(name, BAD_CAST "param") || + xmlStrEqual(name, BAD_CAST "sort"))) + { + do { + if (IS_BLANK_NODE(cur->prev)) { + txt = cur->prev; + xmlUnlinkNode(txt); + xmlFreeNode(txt); + } else { + /* + * This will result in a content + * error, when hitting the parsing + * functions. + */ + break; + } + } while (cur->prev); + } + } + break; + } + } + +process_attributes: + /* + * Process attributes. + * ------------------ + */ + if (cur->properties != NULL) { + if (cur->children == NULL) + findSpaceAttr = 0; + attr = cur->properties; + do { +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + if ((attr->ns) && (attr->ns->href != nsNameXSLT) && + xmlStrEqual(attr->ns->href, nsNameXSLT)) + { + nsMapItem = xsltNewNamespaceMapItem(cctxt, + doc, attr->ns, cur); + if (nsMapItem == NULL) + goto internal_err; + attr->ns->href = nsNameXSLT; + } +#endif + if (internalize) { + /* + * Internalize the attribute's value; the goal is to + * speed up operations and minimize used space by + * compiled stylesheets. + */ + txt = attr->children; + /* + * NOTE that this assumes only one + * text-node in the attribute's content. + */ + if ((txt != NULL) && (txt->content != NULL) && + (!xmlDictOwns(style->dict, txt->content))) + { + value = (xmlChar *) xmlDictLookup(style->dict, + txt->content, -1); + xmlNodeSetContent(txt, NULL); + txt->content = value; + } + } + /* + * Process xml:space attributes. + * ---------------------------- + */ + if ((findSpaceAttr != 0) && + (attr->ns != NULL) && + (attr->name != NULL) && + (attr->name[0] == 's') && + (attr->ns->prefix != NULL) && + (attr->ns->prefix[0] == 'x') && + (attr->ns->prefix[1] == 'm') && + (attr->ns->prefix[2] == 'l') && + (attr->ns->prefix[3] == 0)) + { + value = xmlGetNsProp(cur, BAD_CAST "space", + XML_XML_NAMESPACE); + if (value != NULL) { + if (xmlStrEqual(value, BAD_CAST "preserve")) { + cctxt->inode->preserveWhitespace = 1; + } else if (xmlStrEqual(value, BAD_CAST "default")) { + cctxt->inode->preserveWhitespace = 0; + } else { + /* Invalid value for xml:space. */ + xsltTransformError(NULL, style, cur, + "Attribute xml:space: Invalid value.\n"); + cctxt->style->warnings++; + } + findSpaceAttr = 0; + xmlFree(value); + } + + } + attr = attr->next; + } while (attr != NULL); + } + /* + * We'll descend into the children of element nodes only. + */ + if (cur->children != NULL) { + cur = cur->children; + continue; + } + } else if ((cur->type == XML_TEXT_NODE) || + (cur->type == XML_CDATA_SECTION_NODE)) + { + /* + * Merge adjacent text/CDATA-section-nodes + * --------------------------------------- + * In order to avoid breaking of existing stylesheets, + * if the old behaviour is wanted (strictWhitespace == 0), + * then we *won't* merge adjacent text-nodes + * (except in xsl:text); this will ensure that whitespace-only + * text nodes are (incorrectly) not stripped in some cases. + * + * Example: : <foo> <!-- bar -->zoo</foo> + * Corrent (strict) result: <foo> zoo</foo> + * Incorrect (old) result : <foo>zoo</foo> + * + * NOTE that we *will* merge adjacent text-nodes if + * they are in xsl:text. + * Example, the following: + * <xsl:text> <!-- bar -->zoo<xsl:text> + * will result in both cases in: + * <xsl:text> zoo<xsl:text> + */ + cur->type = XML_TEXT_NODE; + if ((strictWhitespace != 0) || (inXSLText != 0)) { + /* + * New behaviour; merge nodes. + */ + if (textNode == NULL) + textNode = cur; + else { + if (cur->content != NULL) + xmlNodeAddContent(textNode, cur->content); + deleteNode = cur; + } + if ((cur->next == NULL) || + (cur->next->type == XML_ELEMENT_NODE)) + goto end_of_text; + else + goto next_sibling; + } else { + /* + * Old behaviour. + */ + if (textNode == NULL) + textNode = cur; + goto end_of_text; + } + } else if ((cur->type == XML_COMMENT_NODE) || + (cur->type == XML_PI_NODE)) + { + /* + * Remove processing instructions and comments. + */ + deleteNode = cur; + if ((cur->next == NULL) || + (cur->next->type == XML_ELEMENT_NODE)) + goto end_of_text; + else + goto next_sibling; + } else { + textNode = NULL; + /* + * Invalid node-type for this data-model. + */ + xsltTransformError(NULL, style, cur, + "Invalid type of node for the XSLT data model.\n"); + cctxt->style->errors++; + goto next_sibling; + } + +end_of_text: + if (textNode) { + value = textNode->content; + /* + * At this point all adjacent text/CDATA-section nodes + * have been merged. + * + * Strip whitespace-only text-nodes. + * (cctxt->inode->stripWhitespace) + */ + if ((value == NULL) || (*value == 0) || + (((cctxt->inode->stripWhitespace) || + (! cctxt->inode->preserveWhitespace)) && + IS_BLANK(*value) && + xsltIsBlank(value))) + { + if (textNode != cur) { + xmlUnlinkNode(textNode); + xmlFreeNode(textNode); + } else + deleteNode = textNode; + textNode = NULL; + goto next_sibling; + } + /* + * Convert CDATA-section nodes to text-nodes. + * TODO: Can this produce problems? + */ + if (textNode->type != XML_TEXT_NODE) { + textNode->type = XML_TEXT_NODE; + textNode->name = xmlStringText; + } + if (internalize && + (textNode->content != NULL) && + (!xmlDictOwns(style->dict, textNode->content))) + { + /* + * Internalize the string. + */ + value = (xmlChar *) xmlDictLookup(style->dict, + textNode->content, -1); + xmlNodeSetContent(textNode, NULL); + textNode->content = value; + } + textNode = NULL; + /* + * Note that "disable-output-escaping" of the xsl:text + * element will be applied at a later level, when + * XSLT elements are processed. + */ + } + +next_sibling: + if (cur->type == XML_ELEMENT_NODE) { + xsltCompilerNodePop(cctxt, cur); + } + if (cur == node) + break; + if (cur->next != NULL) { + cur = cur->next; + } else { + cur = cur->parent; + inXSLText = 0; + goto next_sibling; + }; + } + if (deleteNode != NULL) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParsePreprocessStylesheetTree: removing node\n"); +#endif + xmlUnlinkNode(deleteNode); + xmlFreeNode(deleteNode); + } + return(0); + +internal_err: + return(-1); +} + +#endif /* XSLT_REFACTORED */ + +#ifdef XSLT_REFACTORED +#else +static void +xsltPreprocessStylesheet(xsltStylesheetPtr style, xmlNodePtr cur) +{ + xmlNodePtr deleteNode, styleelem; + int internalize = 0; + + if ((style == NULL) || (cur == NULL)) + return; + + if ((cur->doc != NULL) && (style->dict != NULL) && + (cur->doc->dict == style->dict)) + internalize = 1; + else + style->internalized = 0; + + if ((cur != NULL) && (IS_XSLT_ELEM(cur)) && + (IS_XSLT_NAME(cur, "stylesheet"))) { + styleelem = cur; + } else { + styleelem = NULL; + } + + /* + * This content comes from the stylesheet + * For stylesheets, the set of whitespace-preserving + * element names consists of just xsl:text. + */ + deleteNode = NULL; + while (cur != NULL) { + if (deleteNode != NULL) { +#ifdef WITH_XSLT_DEBUG_BLANKS + xsltGenericDebug(xsltGenericDebugContext, + "xsltPreprocessStylesheet: removing ignorable blank node\n"); +#endif + xmlUnlinkNode(deleteNode); + xmlFreeNode(deleteNode); + deleteNode = NULL; + } + if (cur->type == XML_ELEMENT_NODE) { + int exclPrefixes; + /* + * Internalize attributes values. + */ + if ((internalize) && (cur->properties != NULL)) { + xmlAttrPtr attr = cur->properties; + xmlNodePtr txt; + + while (attr != NULL) { + txt = attr->children; + if ((txt != NULL) && (txt->type == XML_TEXT_NODE) && + (txt->content != NULL) && + (!xmlDictOwns(style->dict, txt->content))) + { + xmlChar *tmp; + + /* + * internalize the text string, goal is to speed + * up operations and minimize used space by compiled + * stylesheets. + */ + tmp = (xmlChar *) xmlDictLookup(style->dict, + txt->content, -1); + if (tmp != txt->content) { + xmlNodeSetContent(txt, NULL); + txt->content = tmp; + } + } + attr = attr->next; + } + } + if (IS_XSLT_ELEM(cur)) { + exclPrefixes = 0; + if (IS_XSLT_NAME(cur, "text")) { + for (;exclPrefixes > 0;exclPrefixes--) + exclPrefixPop(style); + goto skip_children; + } + } else { + exclPrefixes = xsltParseStylesheetExcludePrefix(style, cur, 0); + } + + if ((cur->nsDef != NULL) && (style->exclPrefixNr > 0)) { + xmlNsPtr ns = cur->nsDef, prev = NULL, next; + xmlNodePtr root = NULL; + int i, moved; + + root = xmlDocGetRootElement(cur->doc); + if ((root != NULL) && (root != cur)) { + while (ns != NULL) { + moved = 0; + next = ns->next; + for (i = 0;i < style->exclPrefixNr;i++) { + if ((ns->prefix != NULL) && + (xmlStrEqual(ns->href, + style->exclPrefixTab[i]))) { + /* + * Move the namespace definition on the root + * element to avoid duplicating it without + * loosing it. + */ + if (prev == NULL) { + cur->nsDef = ns->next; + } else { + prev->next = ns->next; + } + ns->next = root->nsDef; + root->nsDef = ns; + moved = 1; + break; + } + } + if (moved == 0) + prev = ns; + ns = next; + } + } + } + /* + * If we have prefixes locally, recurse and pop them up when + * going back + */ + if (exclPrefixes > 0) { + xsltPreprocessStylesheet(style, cur->children); + for (;exclPrefixes > 0;exclPrefixes--) + exclPrefixPop(style); + goto skip_children; + } + } else if (cur->type == XML_TEXT_NODE) { + if (IS_BLANK_NODE(cur)) { + if (xmlNodeGetSpacePreserve(cur->parent) != 1) { + deleteNode = cur; + } + } else if ((cur->content != NULL) && (internalize) && + (!xmlDictOwns(style->dict, cur->content))) { + xmlChar *tmp; + + /* + * internalize the text string, goal is to speed + * up operations and minimize used space by compiled + * stylesheets. + */ + tmp = (xmlChar *) xmlDictLookup(style->dict, cur->content, -1); + xmlNodeSetContent(cur, NULL); + cur->content = tmp; + } + } else if ((cur->type != XML_ELEMENT_NODE) && + (cur->type != XML_CDATA_SECTION_NODE)) { + deleteNode = cur; + goto skip_children; + } + + /* + * Skip to next node. In case of a namespaced element children of + * the stylesheet and not in the XSLT namespace and not an extension + * element, ignore its content. + */ + if ((cur->type == XML_ELEMENT_NODE) && (cur->ns != NULL) && + (styleelem != NULL) && (cur->parent == styleelem) && + (!xmlStrEqual(cur->ns->href, XSLT_NAMESPACE)) && + (!xsltCheckExtURI(style, cur->ns->href))) { + goto skip_children; + } else if (cur->children != NULL) { + if ((cur->children->type != XML_ENTITY_DECL) && + (cur->children->type != XML_ENTITY_REF_NODE) && + (cur->children->type != XML_ENTITY_NODE)) { + cur = cur->children; + continue; + } + } + +skip_children: + if (cur->next != NULL) { + cur = cur->next; + continue; + } + do { + + cur = cur->parent; + if (cur == NULL) + break; + if (cur == (xmlNodePtr) style->doc) { + cur = NULL; + break; + } + if (cur->next != NULL) { + cur = cur->next; + break; + } + } while (cur != NULL); + } + if (deleteNode != NULL) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltPreprocessStylesheet: removing ignorable blank node\n"); +#endif + xmlUnlinkNode(deleteNode); + xmlFreeNode(deleteNode); + } +} +#endif /* end of else XSLT_REFACTORED */ + +/** + * xsltGatherNamespaces: + * @style: the XSLT stylesheet + * + * Browse the stylesheet and build the namspace hash table which + * will be used for XPath interpretation. If needed do a bit of normalization + */ + +static void +xsltGatherNamespaces(xsltStylesheetPtr style) { + xmlNodePtr cur; + const xmlChar *URI; + + if (style == NULL) + return; + /* + * TODO: basically if the stylesheet uses the same prefix for different + * patterns, well they may be in problem, hopefully they will get + * a warning first. + */ + /* + * TODO: Eliminate the use of the hash for XPath expressions. + * An expression should be evaluated in the context of the in-scope + * namespaces; eliminate the restriction of an XML document to contain + * no duplicate prefixes for different namespace names. + * + */ + cur = xmlDocGetRootElement(style->doc); + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + xmlNsPtr ns = cur->nsDef; + while (ns != NULL) { + if (ns->prefix != NULL) { + if (style->nsHash == NULL) { + style->nsHash = xmlHashCreate(10); + if (style->nsHash == NULL) { + xsltTransformError(NULL, style, cur, + "xsltGatherNamespaces: failed to create hash table\n"); + style->errors++; + return; + } + } + URI = xmlHashLookup(style->nsHash, ns->prefix); + if ((URI != NULL) && (!xmlStrEqual(URI, ns->href))) { + xsltTransformError(NULL, style, cur, + "Namespaces prefix %s used for multiple namespaces\n",ns->prefix); + style->warnings++; + } else if (URI == NULL) { + xmlHashUpdateEntry(style->nsHash, ns->prefix, + (void *) ns->href, (xmlHashDeallocator)xmlFree); + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "Added namespace: %s mapped to %s\n", ns->prefix, ns->href); +#endif + } + } + ns = ns->next; + } + } + + /* + * Skip to next node + */ + if (cur->children != NULL) { + if (cur->children->type != XML_ENTITY_DECL) { + cur = cur->children; + continue; + } + } + if (cur->next != NULL) { + cur = cur->next; + continue; + } + + do { + cur = cur->parent; + if (cur == NULL) + break; + if (cur == (xmlNodePtr) style->doc) { + cur = NULL; + break; + } + if (cur->next != NULL) { + cur = cur->next; + break; + } + } while (cur != NULL); + } +} + +#ifdef XSLT_REFACTORED + +static xsltStyleType +xsltGetXSLTElementTypeByNode(xsltCompilerCtxtPtr cctxt, + xmlNodePtr node) +{ + if ((node == NULL) || (node->type != XML_ELEMENT_NODE) || + (node->name == NULL)) + return(0); + + if (node->name[0] == 'a') { + if (IS_XSLT_NAME(node, "apply-templates")) + return(XSLT_FUNC_APPLYTEMPLATES); + else if (IS_XSLT_NAME(node, "attribute")) + return(XSLT_FUNC_ATTRIBUTE); + else if (IS_XSLT_NAME(node, "apply-imports")) + return(XSLT_FUNC_APPLYIMPORTS); + else if (IS_XSLT_NAME(node, "attribute-set")) + return(0); + + } else if (node->name[0] == 'c') { + if (IS_XSLT_NAME(node, "choose")) + return(XSLT_FUNC_CHOOSE); + else if (IS_XSLT_NAME(node, "copy")) + return(XSLT_FUNC_COPY); + else if (IS_XSLT_NAME(node, "copy-of")) + return(XSLT_FUNC_COPYOF); + else if (IS_XSLT_NAME(node, "call-template")) + return(XSLT_FUNC_CALLTEMPLATE); + else if (IS_XSLT_NAME(node, "comment")) + return(XSLT_FUNC_COMMENT); + + } else if (node->name[0] == 'd') { + if (IS_XSLT_NAME(node, "document")) + return(XSLT_FUNC_DOCUMENT); + else if (IS_XSLT_NAME(node, "decimal-format")) + return(0); + + } else if (node->name[0] == 'e') { + if (IS_XSLT_NAME(node, "element")) + return(XSLT_FUNC_ELEMENT); + + } else if (node->name[0] == 'f') { + if (IS_XSLT_NAME(node, "for-each")) + return(XSLT_FUNC_FOREACH); + else if (IS_XSLT_NAME(node, "fallback")) + return(XSLT_FUNC_FALLBACK); + + } else if (*(node->name) == 'i') { + if (IS_XSLT_NAME(node, "if")) + return(XSLT_FUNC_IF); + else if (IS_XSLT_NAME(node, "include")) + return(0); + else if (IS_XSLT_NAME(node, "import")) + return(0); + + } else if (*(node->name) == 'k') { + if (IS_XSLT_NAME(node, "key")) + return(0); + + } else if (*(node->name) == 'm') { + if (IS_XSLT_NAME(node, "message")) + return(XSLT_FUNC_MESSAGE); + + } else if (*(node->name) == 'n') { + if (IS_XSLT_NAME(node, "number")) + return(XSLT_FUNC_NUMBER); + else if (IS_XSLT_NAME(node, "namespace-alias")) + return(0); + + } else if (*(node->name) == 'o') { + if (IS_XSLT_NAME(node, "otherwise")) + return(XSLT_FUNC_OTHERWISE); + else if (IS_XSLT_NAME(node, "output")) + return(0); + + } else if (*(node->name) == 'p') { + if (IS_XSLT_NAME(node, "param")) + return(XSLT_FUNC_PARAM); + else if (IS_XSLT_NAME(node, "processing-instruction")) + return(XSLT_FUNC_PI); + else if (IS_XSLT_NAME(node, "preserve-space")) + return(0); + + } else if (*(node->name) == 's') { + if (IS_XSLT_NAME(node, "sort")) + return(XSLT_FUNC_SORT); + else if (IS_XSLT_NAME(node, "strip-space")) + return(0); + else if (IS_XSLT_NAME(node, "stylesheet")) + return(0); + + } else if (node->name[0] == 't') { + if (IS_XSLT_NAME(node, "text")) + return(XSLT_FUNC_TEXT); + else if (IS_XSLT_NAME(node, "template")) + return(0); + else if (IS_XSLT_NAME(node, "transform")) + return(0); + + } else if (*(node->name) == 'v') { + if (IS_XSLT_NAME(node, "value-of")) + return(XSLT_FUNC_VALUEOF); + else if (IS_XSLT_NAME(node, "variable")) + return(XSLT_FUNC_VARIABLE); + + } else if (*(node->name) == 'w') { + if (IS_XSLT_NAME(node, "when")) + return(XSLT_FUNC_WHEN); + if (IS_XSLT_NAME(node, "with-param")) + return(XSLT_FUNC_WITHPARAM); + } + return(0); +} + +/** + * xsltParseAnyXSLTElem: + * + * @cctxt: the compilation context + * @elem: the element node of the XSLT instruction + * + * Parses, validates the content models and compiles XSLT instructions. + * + * Returns 0 if everything's fine; + * -1 on API or internal errors. + */ +int +xsltParseAnyXSLTElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr elem) +{ + if ((cctxt == NULL) || (elem == NULL) || + (elem->type != XML_ELEMENT_NODE)) + return(-1); + + elem->psvi = NULL; + + if (! (IS_XSLT_ELEM_FAST(elem))) + return(-1); + /* + * Detection of handled content of extension instructions. + */ + if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { + cctxt->inode->extContentHandled = 1; + } + + xsltCompilerNodePush(cctxt, elem); + /* + * URGENT TODO: Find a way to speed up this annoying redundant + * textual node-name and namespace comparison. + */ + if (cctxt->inode->prev->curChildType != 0) + cctxt->inode->type = cctxt->inode->prev->curChildType; + else + cctxt->inode->type = xsltGetXSLTElementTypeByNode(cctxt, elem); + /* + * Update the in-scope namespaces if needed. + */ + if (elem->nsDef != NULL) + cctxt->inode->inScopeNs = + xsltCompilerBuildInScopeNsList(cctxt, elem); + /* + * xsltStylePreCompute(): + * This will compile the information found on the current + * element's attributes. NOTE that this won't process the + * children of the instruction. + */ + xsltStylePreCompute(cctxt->style, elem); + /* + * TODO: How to react on errors in xsltStylePreCompute() ? + */ + + /* + * Validate the content model of the XSLT-element. + */ + switch (cctxt->inode->type) { + case XSLT_FUNC_APPLYIMPORTS: + /* EMPTY */ + goto empty_content; + case XSLT_FUNC_APPLYTEMPLATES: + /* <!-- Content: (xsl:sort | xsl:with-param)* --> */ + goto apply_templates; + case XSLT_FUNC_ATTRIBUTE: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_CALLTEMPLATE: + /* <!-- Content: xsl:with-param* --> */ + goto call_template; + case XSLT_FUNC_CHOOSE: + /* <!-- Content: (xsl:when+, xsl:otherwise?) --> */ + goto choose; + case XSLT_FUNC_COMMENT: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_COPY: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_COPYOF: + /* EMPTY */ + goto empty_content; + case XSLT_FUNC_DOCUMENT: /* Extra one */ + /* ?? template ?? */ + goto sequence_constructor; + case XSLT_FUNC_ELEMENT: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_FALLBACK: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_FOREACH: + /* <!-- Content: (xsl:sort*, template) --> */ + goto for_each; + case XSLT_FUNC_IF: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_OTHERWISE: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_MESSAGE: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_NUMBER: + /* EMPTY */ + goto empty_content; + case XSLT_FUNC_PARAM: + /* + * Check for redefinition. + */ + if ((elem->psvi != NULL) && (cctxt->ivar != NULL)) { + xsltVarInfoPtr ivar = cctxt->ivar; + + do { + if ((ivar->name == + ((xsltStyleItemParamPtr) elem->psvi)->name) && + (ivar->nsName == + ((xsltStyleItemParamPtr) elem->psvi)->ns)) + { + elem->psvi = NULL; + xsltTransformError(NULL, cctxt->style, elem, + "Redefinition of variable or parameter '%s'.\n", + ivar->name); + cctxt->style->errors++; + goto error; + } + ivar = ivar->prev; + } while (ivar != NULL); + } + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_PI: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_SORT: + /* EMPTY */ + goto empty_content; + case XSLT_FUNC_TEXT: + /* <!-- Content: #PCDATA --> */ + goto text; + case XSLT_FUNC_VALUEOF: + /* EMPTY */ + goto empty_content; + case XSLT_FUNC_VARIABLE: + /* + * Check for redefinition. + */ + if ((elem->psvi != NULL) && (cctxt->ivar != NULL)) { + xsltVarInfoPtr ivar = cctxt->ivar; + + do { + if ((ivar->name == + ((xsltStyleItemVariablePtr) elem->psvi)->name) && + (ivar->nsName == + ((xsltStyleItemVariablePtr) elem->psvi)->ns)) + { + elem->psvi = NULL; + xsltTransformError(NULL, cctxt->style, elem, + "Redefinition of variable or parameter '%s'.\n", + ivar->name); + cctxt->style->errors++; + goto error; + } + ivar = ivar->prev; + } while (ivar != NULL); + } + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_WHEN: + /* <!-- Content: template --> */ + goto sequence_constructor; + case XSLT_FUNC_WITHPARAM: + /* <!-- Content: template --> */ + goto sequence_constructor; + default: +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseXSLTNode: Unhandled XSLT element '%s'.\n", + elem->name); +#endif + xsltTransformError(NULL, cctxt->style, elem, + "xsltParseXSLTNode: Internal error; " + "unhandled XSLT element '%s'.\n", elem->name); + cctxt->style->errors++; + goto internal_err; + } + +apply_templates: + /* <!-- Content: (xsl:sort | xsl:with-param)* --> */ + if (elem->children != NULL) { + xmlNodePtr child = elem->children; + do { + if (child->type == XML_ELEMENT_NODE) { + if (IS_XSLT_ELEM_FAST(child)) { + if (xmlStrEqual(child->name, BAD_CAST "with-param")) { + cctxt->inode->curChildType = XSLT_FUNC_WITHPARAM; + xsltParseAnyXSLTElem(cctxt, child); + } else if (xmlStrEqual(child->name, BAD_CAST "sort")) { + cctxt->inode->curChildType = XSLT_FUNC_SORT; + xsltParseAnyXSLTElem(cctxt, child); + } else + xsltParseContentError(cctxt->style, child); + } else + xsltParseContentError(cctxt->style, child); + } + child = child->next; + } while (child != NULL); + } + goto exit; + +call_template: + /* <!-- Content: xsl:with-param* --> */ + if (elem->children != NULL) { + xmlNodePtr child = elem->children; + do { + if (child->type == XML_ELEMENT_NODE) { + if (IS_XSLT_ELEM_FAST(child)) { + xsltStyleType type; + + type = xsltGetXSLTElementTypeByNode(cctxt, child); + if (type == XSLT_FUNC_WITHPARAM) { + cctxt->inode->curChildType = XSLT_FUNC_WITHPARAM; + xsltParseAnyXSLTElem(cctxt, child); + } else { + xsltParseContentError(cctxt->style, child); + } + } else + xsltParseContentError(cctxt->style, child); + } + child = child->next; + } while (child != NULL); + } + goto exit; + +text: + if (elem->children != NULL) { + xmlNodePtr child = elem->children; + do { + if ((child->type != XML_TEXT_NODE) && + (child->type != XML_CDATA_SECTION_NODE)) + { + xsltTransformError(NULL, cctxt->style, elem, + "The XSLT 'text' element must have only character " + "data as content.\n"); + } + child = child->next; + } while (child != NULL); + } + goto exit; + +empty_content: + if (elem->children != NULL) { + xmlNodePtr child = elem->children; + /* + * Relaxed behaviour: we will allow whitespace-only text-nodes. + */ + do { + if (((child->type != XML_TEXT_NODE) && + (child->type != XML_CDATA_SECTION_NODE)) || + (! IS_BLANK_NODE(child))) + { + xsltTransformError(NULL, cctxt->style, elem, + "This XSLT element must have no content.\n"); + cctxt->style->errors++; + break; + } + child = child->next; + } while (child != NULL); + } + goto exit; + +choose: + /* <!-- Content: (xsl:when+, xsl:otherwise?) --> */ + /* + * TODO: text-nodes in between are *not* allowed in XSLT 1.0. + * The old behaviour did not check this. + * NOTE: In XSLT 2.0 they are stripped beforehand + * if whitespace-only (regardless of xml:space). + */ + if (elem->children != NULL) { + xmlNodePtr child = elem->children; + int nbWhen = 0, nbOtherwise = 0, err = 0; + do { + if (child->type == XML_ELEMENT_NODE) { + if (IS_XSLT_ELEM_FAST(child)) { + xsltStyleType type; + + type = xsltGetXSLTElementTypeByNode(cctxt, child); + if (type == XSLT_FUNC_WHEN) { + nbWhen++; + if (nbOtherwise) { + xsltParseContentError(cctxt->style, child); + err = 1; + break; + } + cctxt->inode->curChildType = XSLT_FUNC_WHEN; + xsltParseAnyXSLTElem(cctxt, child); + } else if (type == XSLT_FUNC_OTHERWISE) { + if (! nbWhen) { + xsltParseContentError(cctxt->style, child); + err = 1; + break; + } + if (nbOtherwise) { + xsltTransformError(NULL, cctxt->style, elem, + "The XSLT 'choose' element must not contain " + "more than one XSLT 'otherwise' element.\n"); + cctxt->style->errors++; + err = 1; + break; + } + nbOtherwise++; + cctxt->inode->curChildType = XSLT_FUNC_OTHERWISE; + xsltParseAnyXSLTElem(cctxt, child); + } else + xsltParseContentError(cctxt->style, child); + } else + xsltParseContentError(cctxt->style, child); + } + /* + else + xsltParseContentError(cctxt, child); + */ + child = child->next; + } while (child != NULL); + if ((! err) && (! nbWhen)) { + xsltTransformError(NULL, cctxt->style, elem, + "The XSLT element 'choose' must contain at least one " + "XSLT element 'when'.\n"); + cctxt->style->errors++; + } + } + goto exit; + +for_each: + /* <!-- Content: (xsl:sort*, template) --> */ + /* + * NOTE: Text-nodes before xsl:sort are *not* allowed in XSLT 1.0. + * The old behaviour did not allow this, but it catched this + * only at transformation-time. + * In XSLT 2.0 they are stripped beforehand if whitespace-only + * (regardless of xml:space). + */ + if (elem->children != NULL) { + xmlNodePtr child = elem->children; + /* + * Parse xsl:sort first. + */ + do { + if ((child->type == XML_ELEMENT_NODE) && + IS_XSLT_ELEM_FAST(child)) + { + if (xsltGetXSLTElementTypeByNode(cctxt, child) == + XSLT_FUNC_SORT) + { + cctxt->inode->curChildType = XSLT_FUNC_SORT; + xsltParseAnyXSLTElem(cctxt, child); + } else + break; + } else + break; + child = child->next; + } while (child != NULL); + /* + * Parse the sequece constructor. + */ + if (child != NULL) + xsltParseSequenceConstructor(cctxt, child); + } + goto exit; + +sequence_constructor: + /* + * Parse the sequence constructor. + */ + if (elem->children != NULL) + xsltParseSequenceConstructor(cctxt, elem->children); + + /* + * Register information for vars/params. Only needed if there + * are any following siblings. + */ + if ((elem->next != NULL) && + ((cctxt->inode->type == XSLT_FUNC_VARIABLE) || + (cctxt->inode->type == XSLT_FUNC_PARAM))) + { + if ((elem->psvi != NULL) && + (((xsltStyleBasicItemVariablePtr) elem->psvi)->name)) + { + xsltCompilerVarInfoPush(cctxt, elem, + ((xsltStyleBasicItemVariablePtr) elem->psvi)->name, + ((xsltStyleBasicItemVariablePtr) elem->psvi)->ns); + } + } + +error: +exit: + xsltCompilerNodePop(cctxt, elem); + return(0); + +internal_err: + xsltCompilerNodePop(cctxt, elem); + return(-1); +} + +/** + * xsltForwardsCompatUnkownItemCreate: + * + * @cctxt: the compilation context + * + * Creates a compiled representation of the unknown + * XSLT instruction. + * + * Returns the compiled representation. + */ +static xsltStyleItemUknownPtr +xsltForwardsCompatUnkownItemCreate(xsltCompilerCtxtPtr cctxt) +{ + xsltStyleItemUknownPtr item; + + item = (xsltStyleItemUknownPtr) xmlMalloc(sizeof(xsltStyleItemUknown)); + if (item == NULL) { + xsltTransformError(NULL, cctxt->style, NULL, + "Internal error in xsltForwardsCompatUnkownItemCreate(): " + "Failed to allocate memory.\n"); + cctxt->style->errors++; + return(NULL); + } + memset(item, 0, sizeof(xsltStyleItemUknown)); + item->type = XSLT_FUNC_UNKOWN_FORWARDS_COMPAT; + /* + * Store it in the stylesheet. + */ + item->next = cctxt->style->preComps; + cctxt->style->preComps = (xsltElemPreCompPtr) item; + return(item); +} + +/** + * xsltParseUnknownXSLTElem: + * + * @cctxt: the compilation context + * @node: the element of the unknown XSLT instruction + * + * Parses an unknown XSLT element. + * If forwards compatible mode is enabled this will allow + * such an unknown XSLT and; otherwise it is rejected. + * + * Returns 1 in the unknown XSLT instruction is rejected, + * 0 if everything's fine and + * -1 on API or internal errors. + */ +static int +xsltParseUnknownXSLTElem(xsltCompilerCtxtPtr cctxt, + xmlNodePtr node) +{ + if ((cctxt == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE)) + return(-1); + + /* + * Detection of handled content of extension instructions. + */ + if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { + cctxt->inode->extContentHandled = 1; + } + if (cctxt->inode->forwardsCompat == 0) { + /* + * We are not in forwards-compatible mode, so raise an error. + */ + xsltTransformError(NULL, cctxt->style, node, + "Unknown XSLT element '%s'.\n", node->name); + cctxt->style->errors++; + return(1); + } + /* + * Forwards-compatible mode. + * ------------------------ + * + * Parse/compile xsl:fallback elements. + * + * QUESTION: Do we have to raise an error if there's no xsl:fallback? + * ANSWER: No, since in the stylesheet the fallback behaviour might + * also be provided by using the XSLT function "element-available". + */ + if (cctxt->unknownItem == NULL) { + /* + * Create a singleton for all unknown XSLT instructions. + */ + cctxt->unknownItem = xsltForwardsCompatUnkownItemCreate(cctxt); + if (cctxt->unknownItem == NULL) { + node->psvi = NULL; + return(-1); + } + } + node->psvi = cctxt->unknownItem; + if (node->children == NULL) + return(0); + else { + xmlNodePtr child = node->children; + + xsltCompilerNodePush(cctxt, node); + /* + * Update the in-scope namespaces if needed. + */ + if (node->nsDef != NULL) + cctxt->inode->inScopeNs = + xsltCompilerBuildInScopeNsList(cctxt, node); + /* + * Parse all xsl:fallback children. + */ + do { + if ((child->type == XML_ELEMENT_NODE) && + IS_XSLT_ELEM_FAST(child) && + IS_XSLT_NAME(child, "fallback")) + { + cctxt->inode->curChildType = XSLT_FUNC_FALLBACK; + xsltParseAnyXSLTElem(cctxt, child); + } + child = child->next; + } while (child != NULL); + + xsltCompilerNodePop(cctxt, node); + } + return(0); +} + +/** + * xsltParseSequenceConstructor: + * + * @cctxt: the compilation context + * @cur: the start-node of the content to be parsed + * + * Parses a "template" content (or "sequence constructor" in XSLT 2.0 terms). + * This will additionally remove xsl:text elements from the tree. + */ +void +xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur) +{ + xsltStyleType type; + xmlNodePtr deleteNode = NULL; + + if (cctxt == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xsltParseSequenceConstructor: Bad arguments\n"); + cctxt->style->errors++; + return; + } + /* + * Detection of handled content of extension instructions. + */ + if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { + cctxt->inode->extContentHandled = 1; + } + if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) + return; + /* + * This is the content reffered to as a "template". + * E.g. an xsl:element has such content model: + * <xsl:element + * name = { qname } + * namespace = { uri-reference } + * use-attribute-sets = qnames> + * <!-- Content: template --> + * + * NOTE that in XSLT-2 the term "template" was abandoned due to + * confusion with xsl:template and the term "sequence constructor" + * was introduced instead. + * + * The following XSLT-instructions are allowed to appear: + * xsl:apply-templates, xsl:call-template, xsl:apply-imports, + * xsl:for-each, xsl:value-of, xsl:copy-of, xsl:number, + * xsl:choose, xsl:if, xsl:text, xsl:copy, xsl:variable, + * xsl:message, xsl:fallback, + * xsl:processing-instruction, xsl:comment, xsl:element + * xsl:attribute. + * Additional allowed content: + * 1) extension instructions + * 2) literal result elements + * 3) PCDATA + * + * NOTE that this content model does *not* allow xsl:param. + */ + while (cur != NULL) { + if (deleteNode != NULL) { +#ifdef WITH_XSLT_DEBUG_BLANKS + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseSequenceConstructor: removing xsl:text element\n"); +#endif + xmlUnlinkNode(deleteNode); + xmlFreeNode(deleteNode); + deleteNode = NULL; + } + if (cur->type == XML_ELEMENT_NODE) { + + if (cur->psvi == xsltXSLTTextMarker) { + /* + * xsl:text elements + * -------------------------------------------------------- + */ + xmlNodePtr tmp; + + cur->psvi = NULL; + /* + * Mark the xsl:text element for later deletion. + */ + deleteNode = cur; + /* + * Validate content. + */ + tmp = cur->children; + if (tmp) { + /* + * We don't expect more than one text-node in the + * content, since we already merged adjacent + * text/CDATA-nodes and eliminated PI/comment-nodes. + */ + if ((tmp->type == XML_TEXT_NODE) || + (tmp->next == NULL)) + { + /* + * Leave the contained text-node in the tree. + */ + xmlUnlinkNode(tmp); + xmlAddPrevSibling(cur, tmp); + } else { + tmp = NULL; + xsltTransformError(NULL, cctxt->style, cur, + "Element 'xsl:text': Invalid type " + "of node found in content.\n"); + cctxt->style->errors++; + } + } + if (cur->properties) { + xmlAttrPtr attr; + /* + * TODO: We need to report errors for + * invalid attrs. + */ + attr = cur->properties; + do { + if ((attr->ns == NULL) && + (attr->name != NULL) && + (attr->name[0] == 'd') && + xmlStrEqual(attr->name, + BAD_CAST "disable-output-escaping")) + { + /* + * Attr "disable-output-escaping". + * XSLT-2: This attribute is deprecated. + */ + if ((attr->children != NULL) && + xmlStrEqual(attr->children->content, + BAD_CAST "yes")) + { + /* + * Disable output escaping for this + * text node. + */ + if (tmp) + tmp->name = xmlStringTextNoenc; + } else if ((attr->children == NULL) || + (attr->children->content == NULL) || + (!xmlStrEqual(attr->children->content, + BAD_CAST "no"))) + { + xsltTransformError(NULL, cctxt->style, + cur, + "Attribute 'disable-output-escaping': " + "Invalid value. Expected is " + "'yes' or 'no'.\n"); + cctxt->style->errors++; + } + break; + } + attr = attr->next; + } while (attr != NULL); + } + } else if (IS_XSLT_ELEM_FAST(cur)) { + /* + * TODO: Using the XSLT-marker is still not stable yet. + */ + /* if (cur->psvi == xsltXSLTElemMarker) { */ + /* + * XSLT instructions + * -------------------------------------------------------- + */ + cur->psvi = NULL; + type = xsltGetXSLTElementTypeByNode(cctxt, cur); + switch (type) { + case XSLT_FUNC_APPLYIMPORTS: + case XSLT_FUNC_APPLYTEMPLATES: + case XSLT_FUNC_ATTRIBUTE: + case XSLT_FUNC_CALLTEMPLATE: + case XSLT_FUNC_CHOOSE: + case XSLT_FUNC_COMMENT: + case XSLT_FUNC_COPY: + case XSLT_FUNC_COPYOF: + case XSLT_FUNC_DOCUMENT: /* Extra one */ + case XSLT_FUNC_ELEMENT: + case XSLT_FUNC_FALLBACK: + case XSLT_FUNC_FOREACH: + case XSLT_FUNC_IF: + case XSLT_FUNC_MESSAGE: + case XSLT_FUNC_NUMBER: + case XSLT_FUNC_PI: + case XSLT_FUNC_TEXT: + case XSLT_FUNC_VALUEOF: + case XSLT_FUNC_VARIABLE: + /* + * Parse the XSLT element. + */ + cctxt->inode->curChildType = type; + xsltParseAnyXSLTElem(cctxt, cur); + break; + default: + xsltParseUnknownXSLTElem(cctxt, cur); + cur = cur->next; + continue; + } + } else { + /* + * Non-XSLT elements + * ----------------- + */ + xsltCompilerNodePush(cctxt, cur); + /* + * Update the in-scope namespaces if needed. + */ + if (cur->nsDef != NULL) + cctxt->inode->inScopeNs = + xsltCompilerBuildInScopeNsList(cctxt, cur); + /* + * The current element is either a literal result element + * or an extension instruction. + * + * Process attr "xsl:extension-element-prefixes". + * FUTURE TODO: IIRC in XSLT 2.0 this attribute must be + * processed by the implementor of the extension function; + * i.e., it won't be handled by the XSLT processor. + */ + /* SPEC 1.0: + * "exclude-result-prefixes" is only allowed on literal + * result elements and "xsl:exclude-result-prefixes" + * on xsl:stylesheet/xsl:transform. + * SPEC 2.0: + * "There are a number of standard attributes + * that may appear on any XSLT element: specifically + * version, exclude-result-prefixes, + * extension-element-prefixes, xpath-default-namespace, + * default-collation, and use-when." + * + * SPEC 2.0: + * For literal result elements: + * "xsl:version, xsl:exclude-result-prefixes, + * xsl:extension-element-prefixes, + * xsl:xpath-default-namespace, + * xsl:default-collation, or xsl:use-when." + */ + if (cur->properties) + cctxt->inode->extElemNs = + xsltParseExtElemPrefixes(cctxt, + cur, cctxt->inode->extElemNs, + XSLT_ELEMENT_CATEGORY_LRE); + /* + * Eval if we have an extension instruction here. + */ + if ((cur->ns != NULL) && + (cctxt->inode->extElemNs != NULL) && + (xsltCheckExtPrefix(cctxt->style, cur->ns->href) == 1)) + { + /* + * Extension instructions + * ---------------------------------------------------- + * Mark the node information. + */ + cctxt->inode->category = XSLT_ELEMENT_CATEGORY_EXTENSION; + cctxt->inode->extContentHandled = 0; + if (cur->psvi != NULL) { + cur->psvi = NULL; + /* + * TODO: Temporary sanity check. + */ + xsltTransformError(NULL, cctxt->style, cur, + "Internal error in xsltParseSequenceConstructor(): " + "Occupied PSVI field.\n"); + cctxt->style->errors++; + cur = cur->next; + continue; + } + cur->psvi = (void *) + xsltPreComputeExtModuleElement(cctxt->style, cur); + + if (cur->psvi == NULL) { + /* + * OLD COMMENT: "Unknown element, maybe registered + * at the context level. Mark it for later + * recognition." + * QUESTION: What does the xsltExtMarker mean? + * ANSWER: It is used in + * xsltApplySequenceConstructor() at + * transformation-time to look out for extension + * registered in the transformation context. + */ + cur->psvi = (void *) xsltExtMarker; + } + /* + * BIG NOTE: Now the ugly part. In previous versions + * of Libxslt (until 1.1.16), all the content of an + * extension instruction was processed and compiled without + * the need of the extension-author to explicitely call + * such a processing;.We now need to mimic this old + * behaviour in order to avoid breaking old code + * on the extension-author's side. + * The mechanism: + * 1) If the author does *not* set the + * compile-time-flag @extContentHandled, then we'll + * parse the content assuming that it's a "template" + * (or "sequence constructor in XSLT 2.0 terms). + * NOTE: If the extension is registered at + * transformation-time only, then there's no way of + * knowing that content shall be valid, and we'll + * process the content the same way. + * 2) If the author *does* set the flag, then we'll assume + * that the author has handled the parsing him/herself + * (e.g. called xsltParseSequenceConstructor(), etc. + * explicitely in his/her code). + */ + if ((cur->children != NULL) && + (cctxt->inode->extContentHandled == 0)) + { + /* + * Default parsing of the content using the + * sequence-constructor model. + */ + xsltParseSequenceConstructor(cctxt, cur->children); + } + } else { + /* + * Literal result element + * ---------------------------------------------------- + * Allowed XSLT attributes: + * xsl:extension-element-prefixes CDATA #IMPLIED + * xsl:exclude-result-prefixes CDATA #IMPLIED + * TODO: xsl:use-attribute-sets %qnames; #IMPLIED + * xsl:version NMTOKEN #IMPLIED + */ + cur->psvi = NULL; + cctxt->inode->category = XSLT_ELEMENT_CATEGORY_LRE; + if (cur->properties != NULL) { + xmlAttrPtr attr = cur->properties; + /* + * Attribute "xsl:exclude-result-prefixes". + */ + cctxt->inode->exclResultNs = + xsltParseExclResultPrefixes(cctxt, cur, + cctxt->inode->exclResultNs, + XSLT_ELEMENT_CATEGORY_LRE); + /* + * Attribute "xsl:version". + */ + xsltParseAttrXSLTVersion(cctxt, cur, + XSLT_ELEMENT_CATEGORY_LRE); + /* + * Report invalid XSLT attributes. + * For XSLT 1.0 only xsl:use-attribute-sets is allowed + * next to xsl:version, xsl:exclude-result-prefixes and + * xsl:extension-element-prefixes. + * + * Mark all XSLT attributes, in order to skip such + * attributes when instantiating the LRE. + */ + do { + if ((attr->psvi != xsltXSLTAttrMarker) && + IS_XSLT_ATTR_FAST(attr)) + { + if (! xmlStrEqual(attr->name, + BAD_CAST "use-attribute-sets")) + { + xsltTransformError(NULL, cctxt->style, + cur, + "Unknown XSLT attribute '%s'.\n", + attr->name); + cctxt->style->errors++; + } else { + /* + * XSLT attr marker. + */ + attr->psvi = (void *) xsltXSLTAttrMarker; + } + } + attr = attr->next; + } while (attr != NULL); + } + /* + * Create/reuse info for the literal result element. + */ + if (cctxt->inode->nsChanged) + xsltLREInfoCreate(cctxt, cur, 1); + cur->psvi = cctxt->inode->litResElemInfo; + /* + * Apply ns-aliasing on the element and on its attributes. + */ + if (cctxt->hasNsAliases) + xsltLREBuildEffectiveNs(cctxt, cur); + /* + * Compile attribute value templates (AVT). + */ + if (cur->properties) { + xmlAttrPtr attr = cur->properties; + + while (attr != NULL) { + xsltCompileAttr(cctxt->style, attr); + attr = attr->next; + } + } + /* + * Parse the content, which is defined to be a "template" + * (or "sequence constructor" in XSLT 2.0 terms). + */ + if (cur->children != NULL) { + xsltParseSequenceConstructor(cctxt, cur->children); + } + } + /* + * Leave the non-XSLT element. + */ + xsltCompilerNodePop(cctxt, cur); + } + } + cur = cur->next; + } + if (deleteNode != NULL) { +#ifdef WITH_XSLT_DEBUG_BLANKS + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseSequenceConstructor: removing xsl:text element\n"); +#endif + xmlUnlinkNode(deleteNode); + xmlFreeNode(deleteNode); + deleteNode = NULL; + } +} + +/** + * xsltParseTemplateContent: + * @style: the XSLT stylesheet + * @templ: the node containing the content to be parsed + * + * Parses and compiles the content-model of an xsl:template element. + * Note that this is *not* the "template" content model (or "sequence + * constructor" in XSLT 2.0); it it allows addional xsl:param + * elements as immediate children of @templ. + * + * Called by: + * exsltFuncFunctionComp() (EXSLT, functions.c) + * So this is intended to be called from extension functions. + */ +void +xsltParseTemplateContent(xsltStylesheetPtr style, xmlNodePtr templ) { + if ((style == NULL) || (templ == NULL) || + (templ->type == XML_NAMESPACE_DECL)) + return; + + /* + * Detection of handled content of extension instructions. + */ + if (XSLT_CCTXT(style)->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) { + XSLT_CCTXT(style)->inode->extContentHandled = 1; + } + + if (templ->children != NULL) { + xmlNodePtr child = templ->children; + /* + * Process xsl:param elements, which can only occur as the + * immediate children of xsl:template (well, and of any + * user-defined extension instruction if needed). + */ + do { + if ((child->type == XML_ELEMENT_NODE) && + IS_XSLT_ELEM_FAST(child) && + IS_XSLT_NAME(child, "param")) + { + XSLT_CCTXT(style)->inode->curChildType = XSLT_FUNC_PARAM; + xsltParseAnyXSLTElem(XSLT_CCTXT(style), child); + } else + break; + child = child->next; + } while (child != NULL); + /* + * Parse the content and register the pattern. + */ + xsltParseSequenceConstructor(XSLT_CCTXT(style), child); + } +} + +#else /* XSLT_REFACTORED */ + +/** + * xsltParseTemplateContent: + * @style: the XSLT stylesheet + * @templ: the container node (can be a document for literal results) + * + * parse a template content-model + * Clean-up the template content from unwanted ignorable blank nodes + * and process xslt:text + */ +void +xsltParseTemplateContent(xsltStylesheetPtr style, xmlNodePtr templ) { + xmlNodePtr cur, delete; + + if ((style == NULL) || (templ == NULL) || + (templ->type == XML_NAMESPACE_DECL)) return; + + /* + * This content comes from the stylesheet + * For stylesheets, the set of whitespace-preserving + * element names consists of just xsl:text. + */ + cur = templ->children; + delete = NULL; + while (cur != NULL) { + if (delete != NULL) { +#ifdef WITH_XSLT_DEBUG_BLANKS + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseTemplateContent: removing text\n"); +#endif + xmlUnlinkNode(delete); + xmlFreeNode(delete); + delete = NULL; + } + if (IS_XSLT_ELEM(cur)) { + xsltStylePreCompute(style, cur); + + if (IS_XSLT_NAME(cur, "text")) { + /* + * TODO: Processing of xsl:text should be moved to + * xsltPreprocessStylesheet(), since otherwise this + * will be performed for every multiply included + * stylesheet; i.e. this here is not skipped with + * the use of the style->nopreproc flag. + */ + if (cur->children != NULL) { + xmlChar *prop; + xmlNodePtr text = cur->children, next; + int noesc = 0; + + prop = xmlGetNsProp(cur, + (const xmlChar *)"disable-output-escaping", + NULL); + if (prop != NULL) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "Disable escaping: %s\n", text->content); +#endif + if (xmlStrEqual(prop, (const xmlChar *)"yes")) { + noesc = 1; + } else if (!xmlStrEqual(prop, + (const xmlChar *)"no")){ + xsltTransformError(NULL, style, cur, + "xsl:text: disable-output-escaping allows only yes or no\n"); + style->warnings++; + + } + xmlFree(prop); + } + + while (text != NULL) { + if (text->type == XML_COMMENT_NODE) { + text = text->next; + continue; + } + if ((text->type != XML_TEXT_NODE) && + (text->type != XML_CDATA_SECTION_NODE)) { + xsltTransformError(NULL, style, cur, + "xsltParseTemplateContent: xslt:text content problem\n"); + style->errors++; + break; + } + if ((noesc) && (text->type != XML_CDATA_SECTION_NODE)) + text->name = xmlStringTextNoenc; + text = text->next; + } + + /* + * replace xsl:text by the list of childs + */ + if (text == NULL) { + text = cur->children; + while (text != NULL) { + if ((style->internalized) && + (text->content != NULL) && + (!xmlDictOwns(style->dict, text->content))) { + + /* + * internalize the text string + */ + if (text->doc->dict != NULL) { + const xmlChar *tmp; + + tmp = xmlDictLookup(text->doc->dict, + text->content, -1); + if (tmp != text->content) { + xmlNodeSetContent(text, NULL); + text->content = (xmlChar *) tmp; + } + } + } + + next = text->next; + xmlUnlinkNode(text); + xmlAddPrevSibling(cur, text); + text = next; + } + } + } + delete = cur; + goto skip_children; + } + } + else if ((cur->ns != NULL) && (style->nsDefs != NULL) && + (xsltCheckExtPrefix(style, cur->ns->prefix))) + { + /* + * okay this is an extension element compile it too + */ + xsltStylePreCompute(style, cur); + } + else if (cur->type == XML_ELEMENT_NODE) + { + /* + * This is an element which will be output as part of the + * template exectution, precompile AVT if found. + */ + if ((cur->ns == NULL) && (style->defaultAlias != NULL)) { + cur->ns = xmlSearchNsByHref(cur->doc, cur, + style->defaultAlias); + } + if (cur->properties != NULL) { + xmlAttrPtr attr = cur->properties; + + while (attr != NULL) { + xsltCompileAttr(style, attr); + attr = attr->next; + } + } + } + /* + * Skip to next node + */ + if (cur->children != NULL) { + if (cur->children->type != XML_ENTITY_DECL) { + cur = cur->children; + continue; + } + } +skip_children: + if (cur->next != NULL) { + cur = cur->next; + continue; + } + + do { + cur = cur->parent; + if (cur == NULL) + break; + if (cur == templ) { + cur = NULL; + break; + } + if (cur->next != NULL) { + cur = cur->next; + break; + } + } while (cur != NULL); + } + if (delete != NULL) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseTemplateContent: removing text\n"); +#endif + xmlUnlinkNode(delete); + xmlFreeNode(delete); + delete = NULL; + } + + /* + * Skip the first params + */ + cur = templ->children; + while (cur != NULL) { + if ((IS_XSLT_ELEM(cur)) && (!(IS_XSLT_NAME(cur, "param")))) + break; + cur = cur->next; + } + + /* + * Browse the remainder of the template + */ + while (cur != NULL) { + if ((IS_XSLT_ELEM(cur)) && (IS_XSLT_NAME(cur, "param"))) { + xmlNodePtr param = cur; + + xsltTransformError(NULL, style, cur, + "xsltParseTemplateContent: ignoring misplaced param element\n"); + if (style != NULL) style->warnings++; + cur = cur->next; + xmlUnlinkNode(param); + xmlFreeNode(param); + } else + break; + } +} + +#endif /* else XSLT_REFACTORED */ + +/** + * xsltParseStylesheetKey: + * @style: the XSLT stylesheet + * @key: the "key" element + * + * <!-- Category: top-level-element --> + * <xsl:key name = qname, match = pattern, use = expression /> + * + * parse an XSLT stylesheet key definition and register it + */ + +static void +xsltParseStylesheetKey(xsltStylesheetPtr style, xmlNodePtr key) { + xmlChar *prop = NULL; + xmlChar *use = NULL; + xmlChar *match = NULL; + xmlChar *name = NULL; + xmlChar *nameURI = NULL; + + if ((style == NULL) || (key == NULL) || (key->type != XML_ELEMENT_NODE)) + return; + + /* + * Get arguments + */ + prop = xmlGetNsProp(key, (const xmlChar *)"name", NULL); + if (prop != NULL) { + const xmlChar *URI; + + /* + * TODO: Don't use xsltGetQNameURI(). + */ + URI = xsltGetQNameURI(key, &prop); + if (prop == NULL) { + if (style != NULL) style->errors++; + goto error; + } else { + name = prop; + if (URI != NULL) + nameURI = xmlStrdup(URI); + } +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseStylesheetKey: name %s\n", name); +#endif + } else { + xsltTransformError(NULL, style, key, + "xsl:key : error missing name\n"); + if (style != NULL) style->errors++; + goto error; + } + + match = xmlGetNsProp(key, (const xmlChar *)"match", NULL); + if (match == NULL) { + xsltTransformError(NULL, style, key, + "xsl:key : error missing match\n"); + if (style != NULL) style->errors++; + goto error; + } + + use = xmlGetNsProp(key, (const xmlChar *)"use", NULL); + if (use == NULL) { + xsltTransformError(NULL, style, key, + "xsl:key : error missing use\n"); + if (style != NULL) style->errors++; + goto error; + } + + /* + * register the keys + */ + xsltAddKey(style, name, nameURI, match, use, key); + + +error: + if (use != NULL) + xmlFree(use); + if (match != NULL) + xmlFree(match); + if (name != NULL) + xmlFree(name); + if (nameURI != NULL) + xmlFree(nameURI); + + if (key->children != NULL) { + xsltParseContentError(style, key->children); + } +} + +#ifdef XSLT_REFACTORED +/** + * xsltParseXSLTTemplate: + * @style: the XSLT stylesheet + * @template: the "template" element + * + * parse an XSLT stylesheet template building the associated structures + * TODO: Is @style ever expected to be NULL? + * + * Called from: + * xsltParseXSLTStylesheet() + * xsltParseStylesheetTop() + */ + +static void +xsltParseXSLTTemplate(xsltCompilerCtxtPtr cctxt, xmlNodePtr templNode) { + xsltTemplatePtr templ; + xmlChar *prop; + double priority; + + if ((cctxt == NULL) || (templNode == NULL) || + (templNode->type != XML_ELEMENT_NODE)) + return; + + /* + * Create and link the structure + */ + templ = xsltNewTemplate(); + if (templ == NULL) + return; + + xsltCompilerNodePush(cctxt, templNode); + if (templNode->nsDef != NULL) + cctxt->inode->inScopeNs = + xsltCompilerBuildInScopeNsList(cctxt, templNode); + + templ->next = cctxt->style->templates; + cctxt->style->templates = templ; + templ->style = cctxt->style; + + /* + * Attribute "mode". + */ + prop = xmlGetNsProp(templNode, (const xmlChar *)"mode", NULL); + if (prop != NULL) { + const xmlChar *modeURI; + + /* + * TODO: We need a standardized function for extraction + * of namespace names and local names from QNames. + * Don't use xsltGetQNameURI() as it cannot channe� + * reports through the context. + */ + modeURI = xsltGetQNameURI(templNode, &prop); + if (prop == NULL) { + cctxt->style->errors++; + goto error; + } + templ->mode = xmlDictLookup(cctxt->style->dict, prop, -1); + xmlFree(prop); + prop = NULL; + if (xmlValidateNCName(templ->mode, 0)) { + xsltTransformError(NULL, cctxt->style, templNode, + "xsl:template: Attribute 'mode': The local part '%s' " + "of the value is not a valid NCName.\n", templ->name); + cctxt->style->errors++; + goto error; + } + if (modeURI != NULL) + templ->modeURI = xmlDictLookup(cctxt->style->dict, modeURI, -1); +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseXSLTTemplate: mode %s\n", templ->mode); +#endif + } + /* + * Attribute "match". + */ + prop = xmlGetNsProp(templNode, (const xmlChar *)"match", NULL); + if (prop != NULL) { + templ->match = prop; + prop = NULL; + } + /* + * Attribute "priority". + */ + prop = xmlGetNsProp(templNode, (const xmlChar *)"priority", NULL); + if (prop != NULL) { + priority = xmlXPathStringEvalNumber(prop); + templ->priority = (float) priority; + xmlFree(prop); + prop = NULL; + } + /* + * Attribute "name". + */ + prop = xmlGetNsProp(templNode, (const xmlChar *)"name", NULL); + if (prop != NULL) { + const xmlChar *nameURI; + xsltTemplatePtr curTempl; + + /* + * TODO: Don't use xsltGetQNameURI(). + */ + nameURI = xsltGetQNameURI(templNode, &prop); + if (prop == NULL) { + cctxt->style->errors++; + goto error; + } + templ->name = xmlDictLookup(cctxt->style->dict, prop, -1); + xmlFree(prop); + prop = NULL; + if (xmlValidateNCName(templ->name, 0)) { + xsltTransformError(NULL, cctxt->style, templNode, + "xsl:template: Attribute 'name': The local part '%s' of " + "the value is not a valid NCName.\n", templ->name); + cctxt->style->errors++; + goto error; + } + if (nameURI != NULL) + templ->nameURI = xmlDictLookup(cctxt->style->dict, nameURI, -1); + curTempl = templ->next; + while (curTempl != NULL) { + if ((nameURI != NULL && xmlStrEqual(curTempl->name, templ->name) && + xmlStrEqual(curTempl->nameURI, nameURI) ) || + (nameURI == NULL && curTempl->nameURI == NULL && + xmlStrEqual(curTempl->name, templ->name))) + { + xsltTransformError(NULL, cctxt->style, templNode, + "xsl:template: error duplicate name '%s'\n", templ->name); + cctxt->style->errors++; + goto error; + } + curTempl = curTempl->next; + } + } + if (templNode->children != NULL) { + xsltParseTemplateContent(cctxt->style, templNode); + /* + * MAYBE TODO: Custom behaviour: In order to stay compatible with + * Xalan and MSXML(.NET), we could allow whitespace + * to appear before an xml:param element; this whitespace + * will additionally become part of the "template". + * NOTE that this is totally deviates from the spec, but + * is the de facto behaviour of Xalan and MSXML(.NET). + * Personally I wouldn't allow this, since if we have: + * <xsl:template ...xml:space="preserve"> + * <xsl:param name="foo"/> + * <xsl:param name="bar"/> + * <xsl:param name="zoo"/> + * ... the whitespace between every xsl:param would be + * added to the result tree. + */ + } + + templ->elem = templNode; + templ->content = templNode->children; + xsltAddTemplate(cctxt->style, templ, templ->mode, templ->modeURI); + +error: + xsltCompilerNodePop(cctxt, templNode); + return; +} + +#else /* XSLT_REFACTORED */ + +/** + * xsltParseStylesheetTemplate: + * @style: the XSLT stylesheet + * @template: the "template" element + * + * parse an XSLT stylesheet template building the associated structures + */ + +static void +xsltParseStylesheetTemplate(xsltStylesheetPtr style, xmlNodePtr template) { + xsltTemplatePtr ret; + xmlChar *prop; + xmlChar *mode = NULL; + xmlChar *modeURI = NULL; + double priority; + + if ((style == NULL) || (template == NULL) || + (template->type != XML_ELEMENT_NODE)) + return; + + /* + * Create and link the structure + */ + ret = xsltNewTemplate(); + if (ret == NULL) + return; + ret->next = style->templates; + style->templates = ret; + ret->style = style; + + /* + * Get inherited namespaces + */ + /* + * TODO: Apply the optimized in-scope-namespace mechanism + * as for the other XSLT instructions. + */ + xsltGetInheritedNsList(style, ret, template); + + /* + * Get arguments + */ + prop = xmlGetNsProp(template, (const xmlChar *)"mode", NULL); + if (prop != NULL) { + const xmlChar *URI; + + /* + * TODO: Don't use xsltGetQNameURI(). + */ + URI = xsltGetQNameURI(template, &prop); + if (prop == NULL) { + if (style != NULL) style->errors++; + goto error; + } else { + mode = prop; + if (URI != NULL) + modeURI = xmlStrdup(URI); + } + ret->mode = xmlDictLookup(style->dict, mode, -1); + ret->modeURI = xmlDictLookup(style->dict, modeURI, -1); +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseStylesheetTemplate: mode %s\n", mode); +#endif + if (mode != NULL) xmlFree(mode); + if (modeURI != NULL) xmlFree(modeURI); + } + prop = xmlGetNsProp(template, (const xmlChar *)"match", NULL); + if (prop != NULL) { + if (ret->match != NULL) xmlFree(ret->match); + ret->match = prop; + } + + prop = xmlGetNsProp(template, (const xmlChar *)"priority", NULL); + if (prop != NULL) { + priority = xmlXPathStringEvalNumber(prop); + ret->priority = (float) priority; + xmlFree(prop); + } + + prop = xmlGetNsProp(template, (const xmlChar *)"name", NULL); + if (prop != NULL) { + const xmlChar *URI; + + /* + * TODO: Don't use xsltGetQNameURI(). + */ + URI = xsltGetQNameURI(template, &prop); + if (prop == NULL) { + if (style != NULL) style->errors++; + goto error; + } else { + if (xmlValidateNCName(prop,0)) { + xsltTransformError(NULL, style, template, + "xsl:template : error invalid name '%s'\n", prop); + if (style != NULL) style->errors++; + goto error; + } + ret->name = xmlDictLookup(style->dict, BAD_CAST prop, -1); + xmlFree(prop); + prop = NULL; + if (URI != NULL) + ret->nameURI = xmlDictLookup(style->dict, BAD_CAST URI, -1); + else + ret->nameURI = NULL; + } + } + + /* + * parse the content and register the pattern + */ + xsltParseTemplateContent(style, template); + ret->elem = template; + ret->content = template->children; + xsltAddTemplate(style, ret, ret->mode, ret->modeURI); + +error: + return; +} + +#endif /* else XSLT_REFACTORED */ + +#ifdef XSLT_REFACTORED + +/** + * xsltIncludeComp: + * @cctxt: the compilation contenxt + * @node: the xsl:include node + * + * Process the xslt include node on the source node + */ +static xsltStyleItemIncludePtr +xsltCompileXSLTIncludeElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) { + xsltStyleItemIncludePtr item; + + if ((cctxt == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE)) + return(NULL); + + node->psvi = NULL; + item = (xsltStyleItemIncludePtr) xmlMalloc(sizeof(xsltStyleItemInclude)); + if (item == NULL) { + xsltTransformError(NULL, cctxt->style, node, + "xsltIncludeComp : malloc failed\n"); + cctxt->style->errors++; + return(NULL); + } + memset(item, 0, sizeof(xsltStyleItemInclude)); + + node->psvi = item; + item->inst = node; + item->type = XSLT_FUNC_INCLUDE; + + item->next = cctxt->style->preComps; + cctxt->style->preComps = (xsltElemPreCompPtr) item; + + return(item); +} + +/** + * xsltParseFindTopLevelElem: + */ +static int +xsltParseFindTopLevelElem(xsltCompilerCtxtPtr cctxt, + xmlNodePtr cur, + const xmlChar *name, + const xmlChar *namespaceURI, + int breakOnOtherElem, + xmlNodePtr *resultNode) +{ + if (name == NULL) + return(-1); + + *resultNode = NULL; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if ((cur->ns != NULL) && (cur->name != NULL)) { + if ((*(cur->name) == *name) && + xmlStrEqual(cur->name, name) && + xmlStrEqual(cur->ns->href, namespaceURI)) + { + *resultNode = cur; + return(1); + } + } + if (breakOnOtherElem) + break; + } + cur = cur->next; + } + *resultNode = cur; + return(0); +} + +static int +xsltParseTopLevelXSLTElem(xsltCompilerCtxtPtr cctxt, + xmlNodePtr node, + xsltStyleType type) +{ + int ret = 0; + + /* + * TODO: The reason why this function exists: + * due to historical reasons some of the + * top-level declarations are processed by functions + * in other files. Since we need still to set + * up the node-info and generate information like + * in-scope namespaces, this is a wrapper around + * those old parsing functions. + */ + xsltCompilerNodePush(cctxt, node); + if (node->nsDef != NULL) + cctxt->inode->inScopeNs = + xsltCompilerBuildInScopeNsList(cctxt, node); + cctxt->inode->type = type; + + switch (type) { + case XSLT_FUNC_INCLUDE: + { + int oldIsInclude; + + if (xsltCompileXSLTIncludeElem(cctxt, node) == NULL) + goto exit; + /* + * Mark this stylesheet tree as being currently included. + */ + oldIsInclude = cctxt->isInclude; + cctxt->isInclude = 1; + + if (xsltParseStylesheetInclude(cctxt->style, node) != 0) { + cctxt->style->errors++; + } + cctxt->isInclude = oldIsInclude; + } + break; + case XSLT_FUNC_PARAM: + xsltStylePreCompute(cctxt->style, node); + xsltParseGlobalParam(cctxt->style, node); + break; + case XSLT_FUNC_VARIABLE: + xsltStylePreCompute(cctxt->style, node); + xsltParseGlobalVariable(cctxt->style, node); + break; + case XSLT_FUNC_ATTRSET: + xsltParseStylesheetAttributeSet(cctxt->style, node); + break; + default: + xsltTransformError(NULL, cctxt->style, node, + "Internal error: (xsltParseTopLevelXSLTElem) " + "Cannot handle this top-level declaration.\n"); + cctxt->style->errors++; + ret = -1; + } + +exit: + xsltCompilerNodePop(cctxt, node); + + return(ret); +} + +#if 0 +static int +xsltParseRemoveWhitespace(xmlNodePtr node) +{ + if ((node == NULL) || (node->children == NULL)) + return(0); + else { + xmlNodePtr delNode = NULL, child = node->children; + + do { + if (delNode) { + xmlUnlinkNode(delNode); + xmlFreeNode(delNode); + delNode = NULL; + } + if (((child->type == XML_TEXT_NODE) || + (child->type == XML_CDATA_SECTION_NODE)) && + (IS_BLANK_NODE(child))) + delNode = child; + child = child->next; + } while (child != NULL); + if (delNode) { + xmlUnlinkNode(delNode); + xmlFreeNode(delNode); + delNode = NULL; + } + } + return(0); +} +#endif + +static int +xsltParseXSLTStylesheetElemCore(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) +{ +#ifdef WITH_XSLT_DEBUG_PARSING + int templates = 0; +#endif + xmlNodePtr cur, start = NULL; + xsltStylesheetPtr style; + + if ((cctxt == NULL) || (node == NULL) || + (node->type != XML_ELEMENT_NODE)) + return(-1); + + style = cctxt->style; + /* + * At this stage all import declarations of all stylesheet modules + * with the same stylesheet level have been processed. + * Now we can safely parse the rest of the declarations. + */ + if (IS_XSLT_ELEM_FAST(node) && IS_XSLT_NAME(node, "include")) + { + xsltDocumentPtr include; + /* + * URGENT TODO: Make this work with simplified stylesheets! + * I.e., when we won't find an xsl:stylesheet element. + */ + /* + * This is as include declaration. + */ + include = ((xsltStyleItemIncludePtr) node->psvi)->include; + if (include == NULL) { + /* TODO: raise error? */ + return(-1); + } + /* + * TODO: Actually an xsl:include should locate an embedded + * stylesheet as well; so the document-element won't always + * be the element where the actual stylesheet is rooted at. + * But such embedded stylesheets are not supported by Libxslt yet. + */ + node = xmlDocGetRootElement(include->doc); + if (node == NULL) { + return(-1); + } + } + + if (node->children == NULL) + return(0); + /* + * Push the xsl:stylesheet/xsl:transform element. + */ + xsltCompilerNodePush(cctxt, node); + cctxt->inode->isRoot = 1; + cctxt->inode->nsChanged = 0; + /* + * Start with the naked dummy info for literal result elements. + */ + cctxt->inode->litResElemInfo = cctxt->inodeList->litResElemInfo; + + /* + * In every case, we need to have + * the in-scope namespaces of the element, where the + * stylesheet is rooted at, regardless if it's an XSLT + * instruction or a literal result instruction (or if + * this is an embedded stylesheet). + */ + cctxt->inode->inScopeNs = + xsltCompilerBuildInScopeNsList(cctxt, node); + + /* + * Process attributes of xsl:stylesheet/xsl:transform. + * -------------------------------------------------- + * Allowed are: + * id = id + * extension-element-prefixes = tokens + * exclude-result-prefixes = tokens + * version = number (mandatory) + */ + if (xsltParseAttrXSLTVersion(cctxt, node, + XSLT_ELEMENT_CATEGORY_XSLT) == 0) + { + /* + * Attribute "version". + * XSLT 1.0: "An xsl:stylesheet element *must* have a version + * attribute, indicating the version of XSLT that the + * stylesheet requires". + * The root element of a simplified stylesheet must also have + * this attribute. + */ +#ifdef XSLT_REFACTORED_MANDATORY_VERSION + if (isXsltElem) + xsltTransformError(NULL, cctxt->style, node, + "The attribute 'version' is missing.\n"); + cctxt->style->errors++; +#else + /* OLD behaviour. */ + xsltTransformError(NULL, cctxt->style, node, + "xsl:version is missing: document may not be a stylesheet\n"); + cctxt->style->warnings++; +#endif + } + /* + * The namespaces declared by the attributes + * "extension-element-prefixes" and + * "exclude-result-prefixes" are local to *this* + * stylesheet tree; i.e., they are *not* visible to + * other stylesheet-modules, whether imported or included. + * + * Attribute "extension-element-prefixes". + */ + cctxt->inode->extElemNs = + xsltParseExtElemPrefixes(cctxt, node, NULL, + XSLT_ELEMENT_CATEGORY_XSLT); + /* + * Attribute "exclude-result-prefixes". + */ + cctxt->inode->exclResultNs = + xsltParseExclResultPrefixes(cctxt, node, NULL, + XSLT_ELEMENT_CATEGORY_XSLT); + /* + * Create/reuse info for the literal result element. + */ + if (cctxt->inode->nsChanged) + xsltLREInfoCreate(cctxt, node, 0); + /* + * Processed top-level elements: + * ---------------------------- + * xsl:variable, xsl:param (QName, in-scope ns, + * expression (vars allowed)) + * xsl:attribute-set (QName, in-scope ns) + * xsl:strip-space, xsl:preserve-space (XPath NameTests, + * in-scope ns) + * I *think* global scope, merge with includes + * xsl:output (QName, in-scope ns) + * xsl:key (QName, in-scope ns, pattern, + * expression (vars *not* allowed)) + * xsl:decimal-format (QName, needs in-scope ns) + * xsl:namespace-alias (in-scope ns) + * global scope, merge with includes + * xsl:template (last, QName, pattern) + * + * (whitespace-only text-nodes have *not* been removed + * yet; this will be done in xsltParseSequenceConstructor) + * + * Report misplaced child-nodes first. + */ + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_TEXT_NODE) { + xsltTransformError(NULL, style, cur, + "Misplaced text node (content: '%s').\n", + (cur->content != NULL) ? cur->content : BAD_CAST ""); + style->errors++; + } else if (cur->type != XML_ELEMENT_NODE) { + xsltTransformError(NULL, style, cur, "Misplaced node.\n"); + style->errors++; + } + cur = cur->next; + } + /* + * Skip xsl:import elements; they have been processed + * already. + */ + cur = node->children; + while ((cur != NULL) && xsltParseFindTopLevelElem(cctxt, cur, + BAD_CAST "import", XSLT_NAMESPACE, 1, &cur) == 1) + cur = cur->next; + if (cur == NULL) + goto exit; + + start = cur; + /* + * Process all top-level xsl:param elements. + */ + while ((cur != NULL) && + xsltParseFindTopLevelElem(cctxt, cur, + BAD_CAST "param", XSLT_NAMESPACE, 0, &cur) == 1) + { + xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_PARAM); + cur = cur->next; + } + /* + * Process all top-level xsl:variable elements. + */ + cur = start; + while ((cur != NULL) && + xsltParseFindTopLevelElem(cctxt, cur, + BAD_CAST "variable", XSLT_NAMESPACE, 0, &cur) == 1) + { + xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_VARIABLE); + cur = cur->next; + } + /* + * Process all the rest of top-level elements. + */ + cur = start; + while (cur != NULL) { + /* + * Process element nodes. + */ + if (cur->type == XML_ELEMENT_NODE) { + if (cur->ns == NULL) { + xsltTransformError(NULL, style, cur, + "Unexpected top-level element in no namespace.\n"); + style->errors++; + cur = cur->next; + continue; + } + /* + * Process all XSLT elements. + */ + if (IS_XSLT_ELEM_FAST(cur)) { + /* + * xsl:import is only allowed at the beginning. + */ + if (IS_XSLT_NAME(cur, "import")) { + xsltTransformError(NULL, style, cur, + "Misplaced xsl:import element.\n"); + style->errors++; + cur = cur->next; + continue; + } + /* + * TODO: Change the return type of the parsing functions + * to int. + */ + if (IS_XSLT_NAME(cur, "template")) { +#ifdef WITH_XSLT_DEBUG_PARSING + templates++; +#endif + /* + * TODO: Is the position of xsl:template in the + * tree significant? If not it would be easier to + * parse them at a later stage. + */ + xsltParseXSLTTemplate(cctxt, cur); + } else if (IS_XSLT_NAME(cur, "variable")) { + /* NOP; done already */ + } else if (IS_XSLT_NAME(cur, "param")) { + /* NOP; done already */ + } else if (IS_XSLT_NAME(cur, "include")) { + if (cur->psvi != NULL) + xsltParseXSLTStylesheetElemCore(cctxt, cur); + else { + xsltTransformError(NULL, style, cur, + "Internal error: " + "(xsltParseXSLTStylesheetElemCore) " + "The xsl:include element was not compiled.\n"); + style->errors++; + } + } else if (IS_XSLT_NAME(cur, "strip-space")) { + /* No node info needed. */ + xsltParseStylesheetStripSpace(style, cur); + } else if (IS_XSLT_NAME(cur, "preserve-space")) { + /* No node info needed. */ + xsltParseStylesheetPreserveSpace(style, cur); + } else if (IS_XSLT_NAME(cur, "output")) { + /* No node-info needed. */ + xsltParseStylesheetOutput(style, cur); + } else if (IS_XSLT_NAME(cur, "key")) { + /* TODO: node-info needed for expressions ? */ + xsltParseStylesheetKey(style, cur); + } else if (IS_XSLT_NAME(cur, "decimal-format")) { + /* No node-info needed. */ + xsltParseStylesheetDecimalFormat(style, cur); + } else if (IS_XSLT_NAME(cur, "attribute-set")) { + xsltParseTopLevelXSLTElem(cctxt, cur, + XSLT_FUNC_ATTRSET); + } else if (IS_XSLT_NAME(cur, "namespace-alias")) { + /* NOP; done already */ + } else { + if (cctxt->inode->forwardsCompat) { + /* + * Forwards-compatible mode: + * + * XSLT-1: "if it is a top-level element and + * XSLT 1.0 does not allow such elements as top-level + * elements, then the element must be ignored along + * with its content;" + */ + /* + * TODO: I don't think we should generate a warning. + */ + xsltTransformError(NULL, style, cur, + "Forwards-compatible mode: Ignoring unknown XSLT " + "element '%s'.\n", cur->name); + style->warnings++; + } else { + xsltTransformError(NULL, style, cur, + "Unknown XSLT element '%s'.\n", cur->name); + style->errors++; + } + } + } else { + xsltTopLevelFunction function; + + /* + * Process non-XSLT elements, which are in a + * non-NULL namespace. + */ + /* + * QUESTION: What does xsltExtModuleTopLevelLookup() + * do exactly? + */ + function = xsltExtModuleTopLevelLookup(cur->name, + cur->ns->href); + if (function != NULL) + function(style, cur); +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseXSLTStylesheetElemCore : User-defined " + "data element '%s'.\n", cur->name); +#endif + } + } + cur = cur->next; + } + +exit: + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "### END of parsing top-level elements of doc '%s'.\n", + node->doc->URL); + xsltGenericDebug(xsltGenericDebugContext, + "### Templates: %d\n", templates); +#ifdef XSLT_REFACTORED + xsltGenericDebug(xsltGenericDebugContext, + "### Max inodes: %d\n", cctxt->maxNodeInfos); + xsltGenericDebug(xsltGenericDebugContext, + "### Max LREs : %d\n", cctxt->maxLREs); +#endif /* XSLT_REFACTORED */ +#endif /* WITH_XSLT_DEBUG_PARSING */ + + xsltCompilerNodePop(cctxt, node); + return(0); +} + +/** + * xsltParseXSLTStylesheet: + * @cctxt: the compiler context + * @node: the xsl:stylesheet/xsl:transform element-node + * + * Parses the xsl:stylesheet and xsl:transform element. + * + * <xsl:stylesheet + * id = id + * extension-element-prefixes = tokens + * exclude-result-prefixes = tokens + * version = number> + * <!-- Content: (xsl:import*, top-level-elements) --> + * </xsl:stylesheet> + * + * BIG TODO: The xsl:include stuff. + * + * Called by xsltParseStylesheetTree() + * + * Returns 0 on success, a positive result on errors and + * -1 on API or internal errors. + */ +static int +xsltParseXSLTStylesheetElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) +{ + xmlNodePtr cur, start; + + if ((cctxt == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE)) + return(-1); + + if (node->children == NULL) + goto exit; + + /* + * Process top-level elements: + * xsl:import (must be first) + * xsl:include (this is just a pre-processing) + */ + cur = node->children; + /* + * Process xsl:import elements. + * XSLT 1.0: "The xsl:import element children must precede all + * other element children of an xsl:stylesheet element, + * including any xsl:include element children." + */ + while ((cur != NULL) && + xsltParseFindTopLevelElem(cctxt, cur, + BAD_CAST "import", XSLT_NAMESPACE, 1, &cur) == 1) + { + if (xsltParseStylesheetImport(cctxt->style, cur) != 0) { + cctxt->style->errors++; + } + cur = cur->next; + } + if (cur == NULL) + goto exit; + start = cur; + /* + * Pre-process all xsl:include elements. + */ + cur = start; + while ((cur != NULL) && + xsltParseFindTopLevelElem(cctxt, cur, + BAD_CAST "include", XSLT_NAMESPACE, 0, &cur) == 1) + { + xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_INCLUDE); + cur = cur->next; + } + /* + * Pre-process all xsl:namespace-alias elements. + * URGENT TODO: This won't work correctly: the order of included + * aliases and aliases defined here is significant. + */ + cur = start; + while ((cur != NULL) && + xsltParseFindTopLevelElem(cctxt, cur, + BAD_CAST "namespace-alias", XSLT_NAMESPACE, 0, &cur) == 1) + { + xsltNamespaceAlias(cctxt->style, cur); + cur = cur->next; + } + + if (cctxt->isInclude) { + /* + * If this stylesheet is intended for inclusion, then + * we will process only imports and includes. + */ + goto exit; + } + /* + * Now parse the rest of the top-level elements. + */ + xsltParseXSLTStylesheetElemCore(cctxt, node); +exit: + + return(0); +} + +#else /* XSLT_REFACTORED */ + +/** + * xsltParseStylesheetTop: + * @style: the XSLT stylesheet + * @top: the top level "stylesheet" or "transform" element + * + * scan the top level elements of an XSL stylesheet + */ +static void +xsltParseStylesheetTop(xsltStylesheetPtr style, xmlNodePtr top) { + xmlNodePtr cur; + xmlChar *prop; +#ifdef WITH_XSLT_DEBUG_PARSING + int templates = 0; +#endif + + if ((top == NULL) || (top->type != XML_ELEMENT_NODE)) + return; + + prop = xmlGetNsProp(top, (const xmlChar *)"version", NULL); + if (prop == NULL) { + xsltTransformError(NULL, style, top, + "xsl:version is missing: document may not be a stylesheet\n"); + if (style != NULL) style->warnings++; + } else { + if ((!xmlStrEqual(prop, (const xmlChar *)"1.0")) && + (!xmlStrEqual(prop, (const xmlChar *)"1.1"))) { + xsltTransformError(NULL, style, top, + "xsl:version: only 1.1 features are supported\n"); + if (style != NULL) { + style->forwards_compatible = 1; + style->warnings++; + } + } + xmlFree(prop); + } + + /* + * process xsl:import elements + */ + cur = top->children; + while (cur != NULL) { + if (IS_BLANK_NODE(cur)) { + cur = cur->next; + continue; + } + if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "import")) { + if (xsltParseStylesheetImport(style, cur) != 0) + if (style != NULL) style->errors++; + } else + break; + cur = cur->next; + } + + /* + * process other top-level elements + */ + while (cur != NULL) { + if (IS_BLANK_NODE(cur)) { + cur = cur->next; + continue; + } + if (cur->type == XML_TEXT_NODE) { + if (cur->content != NULL) { + xsltTransformError(NULL, style, cur, + "misplaced text node: '%s'\n", cur->content); + } + if (style != NULL) style->errors++; + cur = cur->next; + continue; + } + if ((cur->type == XML_ELEMENT_NODE) && (cur->ns == NULL)) { + xsltGenericError(xsltGenericErrorContext, + "Found a top-level element %s with null namespace URI\n", + cur->name); + if (style != NULL) style->errors++; + cur = cur->next; + continue; + } + if ((cur->type == XML_ELEMENT_NODE) && (!(IS_XSLT_ELEM(cur)))) { + xsltTopLevelFunction function; + + function = xsltExtModuleTopLevelLookup(cur->name, + cur->ns->href); + if (function != NULL) + function(style, cur); + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseStylesheetTop : found foreign element %s\n", + cur->name); +#endif + cur = cur->next; + continue; + } + if (IS_XSLT_NAME(cur, "import")) { + xsltTransformError(NULL, style, cur, + "xsltParseStylesheetTop: ignoring misplaced import element\n"); + if (style != NULL) style->errors++; + } else if (IS_XSLT_NAME(cur, "include")) { + if (xsltParseStylesheetInclude(style, cur) != 0) + if (style != NULL) style->errors++; + } else if (IS_XSLT_NAME(cur, "strip-space")) { + xsltParseStylesheetStripSpace(style, cur); + } else if (IS_XSLT_NAME(cur, "preserve-space")) { + xsltParseStylesheetPreserveSpace(style, cur); + } else if (IS_XSLT_NAME(cur, "output")) { + xsltParseStylesheetOutput(style, cur); + } else if (IS_XSLT_NAME(cur, "key")) { + xsltParseStylesheetKey(style, cur); + } else if (IS_XSLT_NAME(cur, "decimal-format")) { + xsltParseStylesheetDecimalFormat(style, cur); + } else if (IS_XSLT_NAME(cur, "attribute-set")) { + xsltParseStylesheetAttributeSet(style, cur); + } else if (IS_XSLT_NAME(cur, "variable")) { + xsltParseGlobalVariable(style, cur); + } else if (IS_XSLT_NAME(cur, "param")) { + xsltParseGlobalParam(style, cur); + } else if (IS_XSLT_NAME(cur, "template")) { +#ifdef WITH_XSLT_DEBUG_PARSING + templates++; +#endif + xsltParseStylesheetTemplate(style, cur); + } else if (IS_XSLT_NAME(cur, "namespace-alias")) { + xsltNamespaceAlias(style, cur); + } else { + if ((style != NULL) && (style->forwards_compatible == 0)) { + xsltTransformError(NULL, style, cur, + "xsltParseStylesheetTop: unknown %s element\n", + cur->name); + if (style != NULL) style->errors++; + } + } + cur = cur->next; + } +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "parsed %d templates\n", templates); +#endif +} + +#endif /* else of XSLT_REFACTORED */ + +#ifdef XSLT_REFACTORED +/** + * xsltParseSimplifiedStylesheetTree: + * + * @style: the stylesheet (TODO: Change this to the compiler context) + * @doc: the document containing the stylesheet. + * @node: the node where the stylesheet is rooted at + * + * Returns 0 in case of success, a positive result if an error occurred + * and -1 on API and internal errors. + */ +static int +xsltParseSimplifiedStylesheetTree(xsltCompilerCtxtPtr cctxt, + xmlDocPtr doc, + xmlNodePtr node) +{ + xsltTemplatePtr templ; + + if ((cctxt == NULL) || (node == NULL)) + return(-1); + + if (xsltParseAttrXSLTVersion(cctxt, node, 0) == XSLT_ELEMENT_CATEGORY_LRE) + { + /* + * TODO: Adjust report, since this might be an + * embedded stylesheet. + */ + xsltTransformError(NULL, cctxt->style, node, + "The attribute 'xsl:version' is missing; cannot identify " + "this document as an XSLT stylesheet document.\n"); + cctxt->style->errors++; + return(1); + } + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseSimplifiedStylesheetTree: document is stylesheet\n"); +#endif + + /* + * Create and link the template + */ + templ = xsltNewTemplate(); + if (templ == NULL) { + return(-1); + } + templ->next = cctxt->style->templates; + cctxt->style->templates = templ; + templ->match = xmlStrdup(BAD_CAST "/"); + + /* + * Note that we push the document-node in this special case. + */ + xsltCompilerNodePush(cctxt, (xmlNodePtr) doc); + /* + * In every case, we need to have + * the in-scope namespaces of the element, where the + * stylesheet is rooted at, regardless if it's an XSLT + * instruction or a literal result instruction (or if + * this is an embedded stylesheet). + */ + cctxt->inode->inScopeNs = + xsltCompilerBuildInScopeNsList(cctxt, node); + /* + * Parse the content and register the match-pattern. + */ + xsltParseSequenceConstructor(cctxt, node); + xsltCompilerNodePop(cctxt, (xmlNodePtr) doc); + + templ->elem = (xmlNodePtr) doc; + templ->content = node; + xsltAddTemplate(cctxt->style, templ, NULL, NULL); + cctxt->style->literal_result = 1; + return(0); +} + +#ifdef XSLT_REFACTORED_XSLT_NSCOMP +/** + * xsltRestoreDocumentNamespaces: + * @ns: map of namespaces + * @doc: the document + * + * Restore the namespaces for the document + * + * Returns 0 in case of success, -1 in case of failure + */ +int +xsltRestoreDocumentNamespaces(xsltNsMapPtr ns, xmlDocPtr doc) +{ + if (doc == NULL) + return(-1); + /* + * Revert the changes we have applied to the namespace-URIs of + * ns-decls. + */ + while (ns != NULL) { + if ((ns->doc == doc) && (ns->ns != NULL)) { + ns->ns->href = ns->origNsName; + ns->origNsName = NULL; + ns->ns = NULL; + } + ns = ns->next; + } + return(0); +} +#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ + +/** + * xsltParseStylesheetProcess: + * @style: the XSLT stylesheet (the current stylesheet-level) + * @doc: and xmlDoc parsed XML + * + * Parses an XSLT stylesheet, adding the associated structures. + * Called by: + * xsltParseStylesheetImportedDoc() (xslt.c) + * xsltParseStylesheetInclude() (imports.c) + * + * Returns the value of the @style parameter if everything + * went right, NULL if something went amiss. + */ +xsltStylesheetPtr +xsltParseStylesheetProcess(xsltStylesheetPtr style, xmlDocPtr doc) +{ + xsltCompilerCtxtPtr cctxt; + xmlNodePtr cur; + int oldIsSimplifiedStylesheet; + + xsltInitGlobals(); + + if ((style == NULL) || (doc == NULL)) + return(NULL); + + cctxt = XSLT_CCTXT(style); + + cur = xmlDocGetRootElement(doc); + if (cur == NULL) { + xsltTransformError(NULL, style, (xmlNodePtr) doc, + "xsltParseStylesheetProcess : empty stylesheet\n"); + return(NULL); + } + oldIsSimplifiedStylesheet = cctxt->simplified; + + if ((IS_XSLT_ELEM(cur)) && + ((IS_XSLT_NAME(cur, "stylesheet")) || + (IS_XSLT_NAME(cur, "transform")))) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseStylesheetProcess : found stylesheet\n"); +#endif + cctxt->simplified = 0; + style->literal_result = 0; + } else { + cctxt->simplified = 1; + style->literal_result = 1; + } + /* + * Pre-process the stylesheet if not already done before. + * This will remove PIs and comments, merge adjacent + * text nodes, internalize strings, etc. + */ + if (! style->nopreproc) + xsltParsePreprocessStylesheetTree(cctxt, cur); + /* + * Parse and compile the stylesheet. + */ + if (style->literal_result == 0) { + if (xsltParseXSLTStylesheetElem(cctxt, cur) != 0) + return(NULL); + } else { + if (xsltParseSimplifiedStylesheetTree(cctxt, doc, cur) != 0) + return(NULL); + } + + cctxt->simplified = oldIsSimplifiedStylesheet; + + return(style); +} + +#else /* XSLT_REFACTORED */ + +/** + * xsltParseStylesheetProcess: + * @ret: the XSLT stylesheet (the current stylesheet-level) + * @doc: and xmlDoc parsed XML + * + * Parses an XSLT stylesheet, adding the associated structures. + * Called by: + * xsltParseStylesheetImportedDoc() (xslt.c) + * xsltParseStylesheetInclude() (imports.c) + * + * Returns the value of the @style parameter if everything + * went right, NULL if something went amiss. + */ +xsltStylesheetPtr +xsltParseStylesheetProcess(xsltStylesheetPtr ret, xmlDocPtr doc) { + xmlNodePtr cur; + + xsltInitGlobals(); + + if (doc == NULL) + return(NULL); + if (ret == NULL) + return(ret); + + /* + * First steps, remove blank nodes, + * locate the xsl:stylesheet element and the + * namespace declaration. + */ + cur = xmlDocGetRootElement(doc); + if (cur == NULL) { + xsltTransformError(NULL, ret, (xmlNodePtr) doc, + "xsltParseStylesheetProcess : empty stylesheet\n"); + return(NULL); + } + + if ((IS_XSLT_ELEM(cur)) && + ((IS_XSLT_NAME(cur, "stylesheet")) || + (IS_XSLT_NAME(cur, "transform")))) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseStylesheetProcess : found stylesheet\n"); +#endif + ret->literal_result = 0; + xsltParseStylesheetExcludePrefix(ret, cur, 1); + xsltParseStylesheetExtPrefix(ret, cur, 1); + } else { + xsltParseStylesheetExcludePrefix(ret, cur, 0); + xsltParseStylesheetExtPrefix(ret, cur, 0); + ret->literal_result = 1; + } + if (!ret->nopreproc) { + xsltPreprocessStylesheet(ret, cur); + } + if (ret->literal_result == 0) { + xsltParseStylesheetTop(ret, cur); + } else { + xmlChar *prop; + xsltTemplatePtr template; + + /* + * the document itself might be the template, check xsl:version + */ + prop = xmlGetNsProp(cur, (const xmlChar *)"version", XSLT_NAMESPACE); + if (prop == NULL) { + xsltTransformError(NULL, ret, cur, + "xsltParseStylesheetProcess : document is not a stylesheet\n"); + return(NULL); + } + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseStylesheetProcess : document is stylesheet\n"); +#endif + + if ((!xmlStrEqual(prop, (const xmlChar *)"1.0")) && + (!xmlStrEqual(prop, (const xmlChar *)"1.1"))) { + xsltTransformError(NULL, ret, cur, + "xsl:version: only 1.1 features are supported\n"); + ret->forwards_compatible = 1; + ret->warnings++; + } + xmlFree(prop); + + /* + * Create and link the template + */ + template = xsltNewTemplate(); + if (template == NULL) { + return(NULL); + } + template->next = ret->templates; + ret->templates = template; + template->match = xmlStrdup((const xmlChar *)"/"); + + /* + * parse the content and register the pattern + */ + xsltParseTemplateContent(ret, (xmlNodePtr) doc); + template->elem = (xmlNodePtr) doc; + template->content = doc->children; + xsltAddTemplate(ret, template, NULL, NULL); + ret->literal_result = 1; + } + + return(ret); +} + +#endif /* else of XSLT_REFACTORED */ + +/** + * xsltParseStylesheetImportedDoc: + * @doc: an xmlDoc parsed XML + * @parentStyle: pointer to the parent stylesheet (if it exists) + * + * parse an XSLT stylesheet building the associated structures + * except the processing not needed for imported documents. + * + * Returns a new XSLT stylesheet structure. + */ + +xsltStylesheetPtr +xsltParseStylesheetImportedDoc(xmlDocPtr doc, + xsltStylesheetPtr parentStyle) { + xsltStylesheetPtr retStyle; + + if (doc == NULL) + return(NULL); + + retStyle = xsltNewStylesheet(); + if (retStyle == NULL) + return(NULL); + /* + * Set the importing stylesheet module; also used to detect recursion. + */ + retStyle->parent = parentStyle; + /* + * Adjust the string dict. + */ + if (doc->dict != NULL) { + xmlDictFree(retStyle->dict); + retStyle->dict = doc->dict; +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing dictionary from %s for stylesheet\n", + doc->URL); +#endif + xmlDictReference(retStyle->dict); + } + + /* + * TODO: Eliminate xsltGatherNamespaces(); we must not restrict + * the stylesheet to containt distinct namespace prefixes. + */ + xsltGatherNamespaces(retStyle); + +#ifdef XSLT_REFACTORED + { + xsltCompilerCtxtPtr cctxt; + xsltStylesheetPtr oldCurSheet; + + if (parentStyle == NULL) { + xsltPrincipalStylesheetDataPtr principalData; + /* + * Principal stylesheet + * -------------------- + */ + retStyle->principal = retStyle; + /* + * Create extra data for the principal stylesheet. + */ + principalData = xsltNewPrincipalStylesheetData(); + if (principalData == NULL) { + xsltFreeStylesheet(retStyle); + return(NULL); + } + retStyle->principalData = principalData; + /* + * Create the compilation context + * ------------------------------ + * (only once; for the principal stylesheet). + * This is currently the only function where the + * compilation context is created. + */ + cctxt = xsltCompilationCtxtCreate(retStyle); + if (cctxt == NULL) { + xsltFreeStylesheet(retStyle); + return(NULL); + } + retStyle->compCtxt = (void *) cctxt; + cctxt->style = retStyle; + cctxt->dict = retStyle->dict; + cctxt->psData = principalData; + /* + * Push initial dummy node info. + */ + cctxt->depth = -1; + xsltCompilerNodePush(cctxt, (xmlNodePtr) doc); + } else { + /* + * Imported stylesheet. + */ + retStyle->principal = parentStyle->principal; + cctxt = parentStyle->compCtxt; + retStyle->compCtxt = cctxt; + } + /* + * Save the old and set the current stylesheet structure in the + * compilation context. + */ + oldCurSheet = cctxt->style; + cctxt->style = retStyle; + + retStyle->doc = doc; + xsltParseStylesheetProcess(retStyle, doc); + + cctxt->style = oldCurSheet; + if (parentStyle == NULL) { + /* + * Pop the initial dummy node info. + */ + xsltCompilerNodePop(cctxt, (xmlNodePtr) doc); + } else { + /* + * Clear the compilation context of imported + * stylesheets. + * TODO: really? + */ + /* retStyle->compCtxt = NULL; */ + } + /* + * Free the stylesheet if there were errors. + */ + if (retStyle != NULL) { + if (retStyle->errors != 0) { +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + /* + * Restore all changes made to namespace URIs of ns-decls. + */ + if (cctxt->psData->nsMap) + xsltRestoreDocumentNamespaces(cctxt->psData->nsMap, doc); +#endif + /* + * Detach the doc from the stylesheet; otherwise the doc + * will be freed in xsltFreeStylesheet(). + */ + retStyle->doc = NULL; + /* + * Cleanup the doc if its the main stylesheet. + */ + if (parentStyle == NULL) { + xsltCleanupStylesheetTree(doc, xmlDocGetRootElement(doc)); + if (retStyle->compCtxt != NULL) { + xsltCompilationCtxtFree(retStyle->compCtxt); + retStyle->compCtxt = NULL; + } + } + + xsltFreeStylesheet(retStyle); + retStyle = NULL; + } + } + } + +#else /* XSLT_REFACTORED */ + /* + * Old behaviour. + */ + retStyle->doc = doc; + if (xsltParseStylesheetProcess(retStyle, doc) == NULL) { + retStyle->doc = NULL; + xsltFreeStylesheet(retStyle); + retStyle = NULL; + } + if (retStyle != NULL) { + if (retStyle->errors != 0) { + retStyle->doc = NULL; + if (parentStyle == NULL) + xsltCleanupStylesheetTree(doc, + xmlDocGetRootElement(doc)); + xsltFreeStylesheet(retStyle); + retStyle = NULL; + } + } +#endif /* else of XSLT_REFACTORED */ + + return(retStyle); +} + +/** + * xsltParseStylesheetDoc: + * @doc: and xmlDoc parsed XML + * + * parse an XSLT stylesheet, building the associated structures. doc + * is kept as a reference within the returned stylesheet, so changes + * to doc after the parsing will be reflected when the stylesheet + * is applied, and the doc is automatically freed when the + * stylesheet is closed. + * + * Returns a new XSLT stylesheet structure. + */ + +xsltStylesheetPtr +xsltParseStylesheetDoc(xmlDocPtr doc) { + xsltStylesheetPtr ret; + + xsltInitGlobals(); + + ret = xsltParseStylesheetImportedDoc(doc, NULL); + if (ret == NULL) + return(NULL); + + xsltResolveStylesheetAttributeSet(ret); +#ifdef XSLT_REFACTORED + /* + * Free the compilation context. + * TODO: Check if it's better to move this cleanup to + * xsltParseStylesheetImportedDoc(). + */ + if (ret->compCtxt != NULL) { + xsltCompilationCtxtFree(XSLT_CCTXT(ret)); + ret->compCtxt = NULL; + } +#endif + return(ret); +} + +/** + * xsltParseStylesheetFile: + * @filename: the filename/URL to the stylesheet + * + * Load and parse an XSLT stylesheet + * + * Returns a new XSLT stylesheet structure. + */ + +xsltStylesheetPtr +xsltParseStylesheetFile(const xmlChar* filename) { + xsltSecurityPrefsPtr sec; + xsltStylesheetPtr ret; + xmlDocPtr doc; + + xsltInitGlobals(); + + if (filename == NULL) + return(NULL); + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltParseStylesheetFile : parse %s\n", filename); +#endif + + /* + * Security framework check + */ + sec = xsltGetDefaultSecurityPrefs(); + if (sec != NULL) { + int res; + + res = xsltCheckRead(sec, NULL, filename); + if (res == 0) { + xsltTransformError(NULL, NULL, NULL, + "xsltParseStylesheetFile: read rights for %s denied\n", + filename); + return(NULL); + } + } + + doc = xsltDocDefaultLoader(filename, NULL, XSLT_PARSE_OPTIONS, + NULL, XSLT_LOAD_START); + if (doc == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltParseStylesheetFile : cannot parse %s\n", filename); + return(NULL); + } + ret = xsltParseStylesheetDoc(doc); + if (ret == NULL) { + xmlFreeDoc(doc); + return(NULL); + } + + return(ret); +} + +/************************************************************************ + * * + * Handling of Stylesheet PI * + * * + ************************************************************************/ + +#define CUR (*cur) +#define SKIP(val) cur += (val) +#define NXT(val) cur[(val)] +#define SKIP_BLANKS \ + while (IS_BLANK(CUR)) NEXT +#define NEXT ((*cur) ? cur++ : cur) + +/** + * xsltParseStylesheetPI: + * @value: the value of the PI + * + * This function checks that the type is text/xml and extracts + * the URI-Reference for the stylesheet + * + * Returns the URI-Reference for the stylesheet or NULL (it need to + * be freed by the caller) + */ +static xmlChar * +xsltParseStylesheetPI(const xmlChar *value) { + const xmlChar *cur; + const xmlChar *start; + xmlChar *val; + xmlChar tmp; + xmlChar *href = NULL; + int isXml = 0; + + if (value == NULL) + return(NULL); + + cur = value; + while (CUR != 0) { + SKIP_BLANKS; + if ((CUR == 't') && (NXT(1) == 'y') && (NXT(2) == 'p') && + (NXT(3) == 'e')) { + SKIP(4); + SKIP_BLANKS; + if (CUR != '=') + continue; + NEXT; + if ((CUR != '\'') && (CUR != '"')) + continue; + tmp = CUR; + NEXT; + start = cur; + while ((CUR != 0) && (CUR != tmp)) + NEXT; + if (CUR != tmp) + continue; + val = xmlStrndup(start, cur - start); + NEXT; + if (val == NULL) + return(NULL); + if ((xmlStrcasecmp(val, BAD_CAST "text/xml")) && + (xmlStrcasecmp(val, BAD_CAST "text/xsl"))) { + xmlFree(val); + break; + } + isXml = 1; + xmlFree(val); + } else if ((CUR == 'h') && (NXT(1) == 'r') && (NXT(2) == 'e') && + (NXT(3) == 'f')) { + SKIP(4); + SKIP_BLANKS; + if (CUR != '=') + continue; + NEXT; + if ((CUR != '\'') && (CUR != '"')) + continue; + tmp = CUR; + NEXT; + start = cur; + while ((CUR != 0) && (CUR != tmp)) + NEXT; + if (CUR != tmp) + continue; + if (href == NULL) + href = xmlStrndup(start, cur - start); + NEXT; + } else { + while ((CUR != 0) && (!IS_BLANK(CUR))) + NEXT; + } + + } + + if (!isXml) { + if (href != NULL) + xmlFree(href); + href = NULL; + } + return(href); +} + +/** + * xsltLoadStylesheetPI: + * @doc: a document to process + * + * This function tries to locate the stylesheet PI in the given document + * If found, and if contained within the document, it will extract + * that subtree to build the stylesheet to process @doc (doc itself will + * be modified). If found but referencing an external document it will + * attempt to load it and generate a stylesheet from it. In both cases, + * the resulting stylesheet and the document need to be freed once the + * transformation is done. + * + * Returns a new XSLT stylesheet structure or NULL if not found. + */ +xsltStylesheetPtr +xsltLoadStylesheetPI(xmlDocPtr doc) { + xmlNodePtr child; + xsltStylesheetPtr ret = NULL; + xmlChar *href = NULL; + xmlURIPtr URI; + + xsltInitGlobals(); + + if (doc == NULL) + return(NULL); + + /* + * Find the text/xml stylesheet PI id any before the root + */ + child = doc->children; + while ((child != NULL) && (child->type != XML_ELEMENT_NODE)) { + if ((child->type == XML_PI_NODE) && + (xmlStrEqual(child->name, BAD_CAST "xml-stylesheet"))) { + href = xsltParseStylesheetPI(child->content); + if (href != NULL) + break; + } + child = child->next; + } + + /* + * If found check the href to select processing + */ + if (href != NULL) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltLoadStylesheetPI : found PI href=%s\n", href); +#endif + URI = xmlParseURI((const char *) href); + if (URI == NULL) { + xsltTransformError(NULL, NULL, child, + "xml-stylesheet : href %s is not valid\n", href); + xmlFree(href); + return(NULL); + } + if ((URI->fragment != NULL) && (URI->scheme == NULL) && + (URI->opaque == NULL) && (URI->authority == NULL) && + (URI->server == NULL) && (URI->user == NULL) && + (URI->path == NULL) && (URI->query == NULL)) { + xmlAttrPtr ID; + +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltLoadStylesheetPI : Reference to ID %s\n", href); +#endif + if (URI->fragment[0] == '#') + ID = xmlGetID(doc, (const xmlChar *) &(URI->fragment[1])); + else + ID = xmlGetID(doc, (const xmlChar *) URI->fragment); + if (ID == NULL) { + xsltTransformError(NULL, NULL, child, + "xml-stylesheet : no ID %s found\n", URI->fragment); + } else { + xmlDocPtr fake; + xmlNodePtr subtree, newtree; + xmlNsPtr ns; + +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "creating new document from %s for embedded stylesheet\n", + doc->URL); +#endif + /* + * move the subtree in a new document passed to + * the stylesheet analyzer + */ + subtree = ID->parent; + fake = xmlNewDoc(NULL); + if (fake != NULL) { + /* + * Should the dictionary still be shared even though + * the nodes are being copied rather than moved? + */ + fake->dict = doc->dict; + xmlDictReference(doc->dict); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "reusing dictionary from %s for embedded stylesheet\n", + doc->URL); +#endif + + newtree = xmlDocCopyNode(subtree, fake, 1); + + fake->URL = xmlNodeGetBase(doc, subtree->parent); +#ifdef WITH_XSLT_DEBUG + xsltGenericDebug(xsltGenericDebugContext, + "set base URI for embedded stylesheet as %s\n", + fake->URL); +#endif + + /* + * Add all namespaces in scope of embedded stylesheet to + * root element of newly created stylesheet document + */ + while ((subtree = subtree->parent) != (xmlNodePtr)doc) { + for (ns = subtree->ns; ns; ns = ns->next) { + xmlNewNs(newtree, ns->href, ns->prefix); + } + } + + xmlAddChild((xmlNodePtr)fake, newtree); + ret = xsltParseStylesheetDoc(fake); + if (ret == NULL) + xmlFreeDoc(fake); + } + } + } else { + xmlChar *URL, *base; + + /* + * Reference to an external stylesheet + */ + + base = xmlNodeGetBase(doc, (xmlNodePtr) doc); + URL = xmlBuildURI(href, base); + if (URL != NULL) { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltLoadStylesheetPI : fetching %s\n", URL); +#endif + ret = xsltParseStylesheetFile(URL); + xmlFree(URL); + } else { +#ifdef WITH_XSLT_DEBUG_PARSING + xsltGenericDebug(xsltGenericDebugContext, + "xsltLoadStylesheetPI : fetching %s\n", href); +#endif + ret = xsltParseStylesheetFile(href); + } + if (base != NULL) + xmlFree(base); + } + xmlFreeURI(URI); + xmlFree(href); + } + return(ret); +} diff --git a/libxslt/xslt.h b/libxslt/xslt.h new file mode 100644 index 0000000..02f491a --- /dev/null +++ b/libxslt/xslt.h @@ -0,0 +1,110 @@ +/* + * Summary: Interfaces, constants and types related to the XSLT engine + * Description: Interfaces, constants and types related to the XSLT engine + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_H__ +#define __XML_XSLT_H__ + +#include <libxml/tree.h> +#include "xsltexports.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * XSLT_DEFAULT_VERSION: + * + * The default version of XSLT supported. + */ +#define XSLT_DEFAULT_VERSION "1.0" + +/** + * XSLT_DEFAULT_VENDOR: + * + * The XSLT "vendor" string for this processor. + */ +#define XSLT_DEFAULT_VENDOR "libxslt" + +/** + * XSLT_DEFAULT_URL: + * + * The XSLT "vendor" URL for this processor. + */ +#define XSLT_DEFAULT_URL "http://xmlsoft.org/XSLT/" + +/** + * XSLT_NAMESPACE: + * + * The XSLT specification namespace. + */ +#define XSLT_NAMESPACE ((const xmlChar *)"http://www.w3.org/1999/XSL/Transform") + +/** + * XSLT_PARSE_OPTIONS: + * + * The set of options to pass to an xmlReadxxx when loading files for + * XSLT consumption. + */ +#define XSLT_PARSE_OPTIONS \ + XML_PARSE_NOENT | XML_PARSE_DTDLOAD | XML_PARSE_DTDATTR | XML_PARSE_NOCDATA + +/** + * xsltMaxDepth: + * + * This value is used to detect templates loops. + */ +XSLTPUBVAR int xsltMaxDepth; + +/** + * * xsltMaxVars: + * * + * * This value is used to detect templates loops. + * */ +XSLTPUBVAR int xsltMaxVars; + +/** + * xsltEngineVersion: + * + * The version string for libxslt. + */ +XSLTPUBVAR const char *xsltEngineVersion; + +/** + * xsltLibxsltVersion: + * + * The version of libxslt compiled. + */ +XSLTPUBVAR const int xsltLibxsltVersion; + +/** + * xsltLibxmlVersion: + * + * The version of libxml libxslt was compiled against. + */ +XSLTPUBVAR const int xsltLibxmlVersion; + +/* + * Global initialization function. + */ + +XSLTPUBFUN void XSLTCALL + xsltInit (void); + +/* + * Global cleanup function. + */ +XSLTPUBFUN void XSLTCALL + xsltCleanupGlobals (void); + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_H__ */ + diff --git a/libxslt/xsltInternals.h b/libxslt/xsltInternals.h new file mode 100644 index 0000000..060b178 --- /dev/null +++ b/libxslt/xsltInternals.h @@ -0,0 +1,1977 @@ +/* + * Summary: internal data structures, constants and functions + * Description: Internal data structures, constants and functions used + * by the XSLT engine. + * They are not part of the API or ABI, i.e. they can change + * without prior notice, use carefully. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLT_INTERNALS_H__ +#define __XML_XSLT_INTERNALS_H__ + +#include <libxml/tree.h> +#include <libxml/hash.h> +#include <libxml/xpath.h> +#include <libxml/xmlerror.h> +#include <libxml/dict.h> +#include <libxml/xmlstring.h> +#include <libxslt/xslt.h> +#include "xsltexports.h" +#include "xsltlocale.h" +#include "numbersInternals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* #define XSLT_DEBUG_PROFILE_CACHE */ + +/** + * XSLT_IS_TEXT_NODE: + * + * check if the argument is a text node + */ +#define XSLT_IS_TEXT_NODE(n) ((n != NULL) && \ + (((n)->type == XML_TEXT_NODE) || \ + ((n)->type == XML_CDATA_SECTION_NODE))) + + +/** + * XSLT_MARK_RES_TREE_FRAG: + * + * internal macro to set up tree fragments + */ +#define XSLT_MARK_RES_TREE_FRAG(n) \ + (n)->name = (char *) xmlStrdup(BAD_CAST " fake node libxslt"); + +/** + * XSLT_IS_RES_TREE_FRAG: + * + * internal macro to test tree fragments + */ +#define XSLT_IS_RES_TREE_FRAG(n) \ + ((n != NULL) && ((n)->type == XML_DOCUMENT_NODE) && \ + ((n)->name != NULL) && ((n)->name[0] == ' ')) + +/** + * XSLT_REFACTORED_KEYCOMP: + * + * Internal define to enable on-demand xsl:key computation. + * That's the only mode now but the define is kept for compatibility + */ +#define XSLT_REFACTORED_KEYCOMP + +/** + * XSLT_FAST_IF: + * + * Internal define to enable usage of xmlXPathCompiledEvalToBoolean() + * for XSLT "tests"; e.g. in <xsl:if test="/foo/bar"> + */ +#define XSLT_FAST_IF + +/** + * XSLT_REFACTORED: + * + * Internal define to enable the refactored parts of Libxslt. + */ +/* #define XSLT_REFACTORED */ +/* ==================================================================== */ + +/** + * XSLT_REFACTORED_VARS: + * + * Internal define to enable the refactored variable part of libxslt + */ +#define XSLT_REFACTORED_VARS + +#ifdef XSLT_REFACTORED + +extern const xmlChar *xsltXSLTAttrMarker; + + +/* TODO: REMOVE: #define XSLT_REFACTORED_EXCLRESNS */ + +/* TODO: REMOVE: #define XSLT_REFACTORED_NSALIAS */ + +/** + * XSLT_REFACTORED_XSLT_NSCOMP + * + * Internal define to enable the pointer-comparison of + * namespaces of XSLT elements. + */ +/* #define XSLT_REFACTORED_XSLT_NSCOMP */ + +/** + * XSLT_REFACTORED_XPATHCOMP: + * + * Internal define to enable the optimization of the + * compilation of XPath expressions. + */ +#define XSLT_REFACTORED_XPATHCOMP + +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + +extern const xmlChar *xsltConstNamespaceNameXSLT; + +/** + * IS_XSLT_ELEM_FAST: + * + * quick test to detect XSLT elements + */ +#define IS_XSLT_ELEM_FAST(n) \ + (((n) != NULL) && ((n)->ns != NULL) && \ + ((n)->ns->href == xsltConstNamespaceNameXSLT)) + +/** + * IS_XSLT_ATTR_FAST: + * + * quick test to detect XSLT attributes + */ +#define IS_XSLT_ATTR_FAST(a) \ + (((a) != NULL) && ((a)->ns != NULL) && \ + ((a)->ns->href == xsltConstNamespaceNameXSLT)) + +/** + * XSLT_HAS_INTERNAL_NSMAP: + * + * check for namespace mapping + */ +#define XSLT_HAS_INTERNAL_NSMAP(s) \ + (((s) != NULL) && ((s)->principal) && \ + ((s)->principal->principalData) && \ + ((s)->principal->principalData->nsMap)) + +/** + * XSLT_GET_INTERNAL_NSMAP: + * + * get pointer to namespace map + */ +#define XSLT_GET_INTERNAL_NSMAP(s) ((s)->principal->principalData->nsMap) + +#else /* XSLT_REFACTORED_XSLT_NSCOMP */ + +/** + * IS_XSLT_ELEM_FAST: + * + * quick check whether this is an xslt element + */ +#define IS_XSLT_ELEM_FAST(n) \ + (((n) != NULL) && ((n)->ns != NULL) && \ + (xmlStrEqual((n)->ns->href, XSLT_NAMESPACE))) + +/** + * IS_XSLT_ATTR_FAST: + * + * quick check for xslt namespace attribute + */ +#define IS_XSLT_ATTR_FAST(a) \ + (((a) != NULL) && ((a)->ns != NULL) && \ + (xmlStrEqual((a)->ns->href, XSLT_NAMESPACE))) + + +#endif /* XSLT_REFACTORED_XSLT_NSCOMP */ + + +/** + * XSLT_REFACTORED_MANDATORY_VERSION: + * + * TODO: Currently disabled to surpress regression test failures, since + * the old behaviour was that a missing version attribute + * produced a only a warning and not an error, which was incerrect. + * So the regression tests need to be fixed if this is enabled. + */ +/* #define XSLT_REFACTORED_MANDATORY_VERSION */ + +/** + * xsltPointerList: + * + * Pointer-list for various purposes. + */ +typedef struct _xsltPointerList xsltPointerList; +typedef xsltPointerList *xsltPointerListPtr; +struct _xsltPointerList { + void **items; + int number; + int size; +}; + +#endif + +/** + * XSLT_REFACTORED_PARSING: + * + * Internal define to enable the refactored parts of Libxslt + * related to parsing. + */ +/* #define XSLT_REFACTORED_PARSING */ + +/** + * XSLT_MAX_SORT: + * + * Max number of specified xsl:sort on an element. + */ +#define XSLT_MAX_SORT 15 + +/** + * XSLT_PAT_NO_PRIORITY: + * + * Specific value for pattern without priority expressed. + */ +#define XSLT_PAT_NO_PRIORITY -12345789 + +/** + * xsltRuntimeExtra: + * + * Extra information added to the transformation context. + */ +typedef struct _xsltRuntimeExtra xsltRuntimeExtra; +typedef xsltRuntimeExtra *xsltRuntimeExtraPtr; +struct _xsltRuntimeExtra { + void *info; /* pointer to the extra data */ + xmlFreeFunc deallocate; /* pointer to the deallocation routine */ + union { /* dual-purpose field */ + void *ptr; /* data not needing deallocation */ + int ival; /* integer value storage */ + } val; +}; + +/** + * XSLT_RUNTIME_EXTRA_LST: + * @ctxt: the transformation context + * @nr: the index + * + * Macro used to access extra information stored in the context + */ +#define XSLT_RUNTIME_EXTRA_LST(ctxt, nr) (ctxt)->extras[(nr)].info +/** + * XSLT_RUNTIME_EXTRA_FREE: + * @ctxt: the transformation context + * @nr: the index + * + * Macro used to free extra information stored in the context + */ +#define XSLT_RUNTIME_EXTRA_FREE(ctxt, nr) (ctxt)->extras[(nr)].deallocate +/** + * XSLT_RUNTIME_EXTRA: + * @ctxt: the transformation context + * @nr: the index + * + * Macro used to define extra information stored in the context + */ +#define XSLT_RUNTIME_EXTRA(ctxt, nr, typ) (ctxt)->extras[(nr)].val.typ + +/** + * xsltTemplate: + * + * The in-memory structure corresponding to an XSLT Template. + */ +typedef struct _xsltTemplate xsltTemplate; +typedef xsltTemplate *xsltTemplatePtr; +struct _xsltTemplate { + struct _xsltTemplate *next;/* chained list sorted by priority */ + struct _xsltStylesheet *style;/* the containing stylesheet */ + xmlChar *match; /* the matching string */ + float priority; /* as given from the stylesheet, not computed */ + const xmlChar *name; /* the local part of the name QName */ + const xmlChar *nameURI; /* the URI part of the name QName */ + const xmlChar *mode;/* the local part of the mode QName */ + const xmlChar *modeURI;/* the URI part of the mode QName */ + xmlNodePtr content; /* the template replacement value */ + xmlNodePtr elem; /* the source element */ + + /* + * TODO: @inheritedNsNr and @inheritedNs won't be used in the + * refactored code. + */ + int inheritedNsNr; /* number of inherited namespaces */ + xmlNsPtr *inheritedNs;/* inherited non-excluded namespaces */ + + /* Profiling informations */ + int nbCalls; /* the number of time the template was called */ + unsigned long time; /* the time spent in this template */ + void *params; /* xsl:param instructions */ + + int templNr; /* Nb of templates in the stack */ + int templMax; /* Size of the templtes stack */ + xsltTemplatePtr *templCalledTab; /* templates called */ + int *templCountTab; /* .. and how often */ +}; + +/** + * xsltDecimalFormat: + * + * Data structure of decimal-format. + */ +typedef struct _xsltDecimalFormat xsltDecimalFormat; +typedef xsltDecimalFormat *xsltDecimalFormatPtr; +struct _xsltDecimalFormat { + struct _xsltDecimalFormat *next; /* chained list */ + xmlChar *name; + /* Used for interpretation of pattern */ + xmlChar *digit; + xmlChar *patternSeparator; + /* May appear in result */ + xmlChar *minusSign; + xmlChar *infinity; + xmlChar *noNumber; /* Not-a-number */ + /* Used for interpretation of pattern and may appear in result */ + xmlChar *decimalPoint; + xmlChar *grouping; + xmlChar *percent; + xmlChar *permille; + xmlChar *zeroDigit; +}; + +/** + * xsltDocument: + * + * Data structure associated to a parsed document. + */ +typedef struct _xsltDocument xsltDocument; +typedef xsltDocument *xsltDocumentPtr; +struct _xsltDocument { + struct _xsltDocument *next; /* documents are kept in a chained list */ + int main; /* is this the main document */ + xmlDocPtr doc; /* the parsed document */ + void *keys; /* key tables storage */ + struct _xsltDocument *includes; /* subsidiary includes */ + int preproc; /* pre-processing already done */ + int nbKeysComputed; +}; + +/** + * xsltKeyDef: + * + * Representation of an xsl:key. + */ +typedef struct _xsltKeyDef xsltKeyDef; +typedef xsltKeyDef *xsltKeyDefPtr; +struct _xsltKeyDef { + struct _xsltKeyDef *next; + xmlNodePtr inst; + xmlChar *name; + xmlChar *nameURI; + xmlChar *match; + xmlChar *use; + xmlXPathCompExprPtr comp; + xmlXPathCompExprPtr usecomp; + xmlNsPtr *nsList; /* the namespaces in scope */ + int nsNr; /* the number of namespaces in scope */ +}; + +/** + * xsltKeyTable: + * + * Holds the computed keys for key definitions of the same QName. + * Is owned by an xsltDocument. + */ +typedef struct _xsltKeyTable xsltKeyTable; +typedef xsltKeyTable *xsltKeyTablePtr; +struct _xsltKeyTable { + struct _xsltKeyTable *next; + xmlChar *name; + xmlChar *nameURI; + xmlHashTablePtr keys; +}; + +/* + * The in-memory structure corresponding to an XSLT Stylesheet. + * NOTE: most of the content is simply linked from the doc tree + * structure, no specific allocation is made. + */ +typedef struct _xsltStylesheet xsltStylesheet; +typedef xsltStylesheet *xsltStylesheetPtr; + +typedef struct _xsltTransformContext xsltTransformContext; +typedef xsltTransformContext *xsltTransformContextPtr; + +/** + * xsltElemPreComp: + * + * The in-memory structure corresponding to element precomputed data, + * designed to be extended by extension implementors. + */ +typedef struct _xsltElemPreComp xsltElemPreComp; +typedef xsltElemPreComp *xsltElemPreCompPtr; + +/** + * xsltTransformFunction: + * @ctxt: the XSLT transformation context + * @node: the input node + * @inst: the stylesheet node + * @comp: the compiled information from the stylesheet + * + * Signature of the function associated to elements part of the + * stylesheet language like xsl:if or xsl:apply-templates. + */ +typedef void (*xsltTransformFunction) (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst, + xsltElemPreCompPtr comp); + +/** + * xsltSortFunc: + * @ctxt: a transformation context + * @sorts: the node-set to sort + * @nbsorts: the number of sorts + * + * Signature of the function to use during sorting + */ +typedef void (*xsltSortFunc) (xsltTransformContextPtr ctxt, xmlNodePtr *sorts, + int nbsorts); + +typedef enum { + XSLT_FUNC_COPY=1, + XSLT_FUNC_SORT, + XSLT_FUNC_TEXT, + XSLT_FUNC_ELEMENT, + XSLT_FUNC_ATTRIBUTE, + XSLT_FUNC_COMMENT, + XSLT_FUNC_PI, + XSLT_FUNC_COPYOF, + XSLT_FUNC_VALUEOF, + XSLT_FUNC_NUMBER, + XSLT_FUNC_APPLYIMPORTS, + XSLT_FUNC_CALLTEMPLATE, + XSLT_FUNC_APPLYTEMPLATES, + XSLT_FUNC_CHOOSE, + XSLT_FUNC_IF, + XSLT_FUNC_FOREACH, + XSLT_FUNC_DOCUMENT, + XSLT_FUNC_WITHPARAM, + XSLT_FUNC_PARAM, + XSLT_FUNC_VARIABLE, + XSLT_FUNC_WHEN, + XSLT_FUNC_EXTENSION +#ifdef XSLT_REFACTORED + , + XSLT_FUNC_OTHERWISE, + XSLT_FUNC_FALLBACK, + XSLT_FUNC_MESSAGE, + XSLT_FUNC_INCLUDE, + XSLT_FUNC_ATTRSET, + XSLT_FUNC_LITERAL_RESULT_ELEMENT, + XSLT_FUNC_UNKOWN_FORWARDS_COMPAT +#endif +} xsltStyleType; + +/** + * xsltElemPreCompDeallocator: + * @comp: the #xsltElemPreComp to free up + * + * Deallocates an #xsltElemPreComp structure. + */ +typedef void (*xsltElemPreCompDeallocator) (xsltElemPreCompPtr comp); + +/** + * xsltElemPreComp: + * + * The basic structure for compiled items of the AST of the XSLT processor. + * This structure is also intended to be extended by extension implementors. + * TODO: This is somehow not nice, since it has a "free" field, which + * derived stylesheet-structs do not have. + */ +struct _xsltElemPreComp { + xsltElemPreCompPtr next; /* next item in the global chained + list hold by xsltStylesheet. */ + xsltStyleType type; /* type of the element */ + xsltTransformFunction func; /* handling function */ + xmlNodePtr inst; /* the node in the stylesheet's tree + corresponding to this item */ + + /* end of common part */ + xsltElemPreCompDeallocator free; /* the deallocator */ +}; + +/** + * xsltStylePreComp: + * + * The abstract basic structure for items of the XSLT processor. + * This includes: + * 1) compiled forms of XSLT instructions (xsl:if, xsl:attribute, etc.) + * 2) compiled forms of literal result elements + * 3) compiled forms of extension elements + */ +typedef struct _xsltStylePreComp xsltStylePreComp; +typedef xsltStylePreComp *xsltStylePreCompPtr; + +#ifdef XSLT_REFACTORED + +/* +* Some pointer-list utility functions. +*/ +XSLTPUBFUN xsltPointerListPtr XSLTCALL + xsltPointerListCreate (int initialSize); +XSLTPUBFUN void XSLTCALL + xsltPointerListFree (xsltPointerListPtr list); +XSLTPUBFUN void XSLTCALL + xsltPointerListClear (xsltPointerListPtr list); +XSLTPUBFUN int XSLTCALL + xsltPointerListAddSize (xsltPointerListPtr list, + void *item, + int initialSize); + +/************************************************************************ + * * + * Refactored structures * + * * + ************************************************************************/ + +typedef struct _xsltNsListContainer xsltNsListContainer; +typedef xsltNsListContainer *xsltNsListContainerPtr; +struct _xsltNsListContainer { + xmlNsPtr *list; + int totalNumber; + int xpathNumber; +}; + +/** + * XSLT_ITEM_COMPATIBILITY_FIELDS: + * + * Fields for API compatibility to the structure + * _xsltElemPreComp which is used for extension functions. + * Note that @next is used for storage; it does not reflect a next + * sibling in the tree. + * TODO: Evaluate if we really need such a compatibility. + */ +#define XSLT_ITEM_COMPATIBILITY_FIELDS \ + xsltElemPreCompPtr next;\ + xsltStyleType type;\ + xsltTransformFunction func;\ + xmlNodePtr inst; + +/** + * XSLT_ITEM_NAVIGATION_FIELDS: + * + * Currently empty. + * TODO: It is intended to hold navigational fields in the future. + */ +#define XSLT_ITEM_NAVIGATION_FIELDS +/* + xsltStylePreCompPtr parent;\ + xsltStylePreCompPtr children;\ + xsltStylePreCompPtr nextItem; +*/ + +/** + * XSLT_ITEM_NSINSCOPE_FIELDS: + * + * The in-scope namespaces. + */ +#define XSLT_ITEM_NSINSCOPE_FIELDS xsltNsListContainerPtr inScopeNs; + +/** + * XSLT_ITEM_COMMON_FIELDS: + * + * Common fields used for all items. + */ +#define XSLT_ITEM_COMMON_FIELDS \ + XSLT_ITEM_COMPATIBILITY_FIELDS \ + XSLT_ITEM_NAVIGATION_FIELDS \ + XSLT_ITEM_NSINSCOPE_FIELDS + +/** + * _xsltStylePreComp: + * + * The abstract basic structure for items of the XSLT processor. + * This includes: + * 1) compiled forms of XSLT instructions (e.g. xsl:if, xsl:attribute, etc.) + * 2) compiled forms of literal result elements + * 3) various properties for XSLT instructions (e.g. xsl:when, + * xsl:with-param) + * + * REVISIT TODO: Keep this structure equal to the fields + * defined by XSLT_ITEM_COMMON_FIELDS + */ +struct _xsltStylePreComp { + xsltElemPreCompPtr next; /* next item in the global chained + list hold by xsltStylesheet */ + xsltStyleType type; /* type of the item */ + xsltTransformFunction func; /* handling function */ + xmlNodePtr inst; /* the node in the stylesheet's tree + corresponding to this item. */ + /* Currently no navigational fields. */ + xsltNsListContainerPtr inScopeNs; +}; + +/** + * xsltStyleBasicEmptyItem: + * + * Abstract structure only used as a short-cut for + * XSLT items with no extra fields. + * NOTE that it is intended that this structure looks the same as + * _xsltStylePreComp. + */ +typedef struct _xsltStyleBasicEmptyItem xsltStyleBasicEmptyItem; +typedef xsltStyleBasicEmptyItem *xsltStyleBasicEmptyItemPtr; + +struct _xsltStyleBasicEmptyItem { + XSLT_ITEM_COMMON_FIELDS +}; + +/** + * xsltStyleBasicExpressionItem: + * + * Abstract structure only used as a short-cut for + * XSLT items with just an expression. + */ +typedef struct _xsltStyleBasicExpressionItem xsltStyleBasicExpressionItem; +typedef xsltStyleBasicExpressionItem *xsltStyleBasicExpressionItemPtr; + +struct _xsltStyleBasicExpressionItem { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *select; /* TODO: Change this to "expression". */ + xmlXPathCompExprPtr comp; /* TODO: Change this to compExpr. */ +}; + +/************************************************************************ + * * + * XSLT-instructions/declarations * + * * + ************************************************************************/ + +/** + * xsltStyleItemElement: + * + * <!-- Category: instruction --> + * <xsl:element + * name = { qname } + * namespace = { uri-reference } + * use-attribute-sets = qnames> + * <!-- Content: template --> + * </xsl:element> + */ +typedef struct _xsltStyleItemElement xsltStyleItemElement; +typedef xsltStyleItemElement *xsltStyleItemElementPtr; + +struct _xsltStyleItemElement { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *use; + int has_use; + const xmlChar *name; + int has_name; + const xmlChar *ns; + const xmlChar *nsPrefix; + int has_ns; +}; + +/** + * xsltStyleItemAttribute: + * + * <!-- Category: instruction --> + * <xsl:attribute + * name = { qname } + * namespace = { uri-reference }> + * <!-- Content: template --> + * </xsl:attribute> + */ +typedef struct _xsltStyleItemAttribute xsltStyleItemAttribute; +typedef xsltStyleItemAttribute *xsltStyleItemAttributePtr; + +struct _xsltStyleItemAttribute { + XSLT_ITEM_COMMON_FIELDS + const xmlChar *name; + int has_name; + const xmlChar *ns; + const xmlChar *nsPrefix; + int has_ns; +}; + +/** + * xsltStyleItemText: + * + * <!-- Category: instruction --> + * <xsl:text + * disable-output-escaping = "yes" | "no"> + * <!-- Content: #PCDATA --> + * </xsl:text> + */ +typedef struct _xsltStyleItemText xsltStyleItemText; +typedef xsltStyleItemText *xsltStyleItemTextPtr; + +struct _xsltStyleItemText { + XSLT_ITEM_COMMON_FIELDS + int noescape; /* text */ +}; + +/** + * xsltStyleItemComment: + * + * <!-- Category: instruction --> + * <xsl:comment> + * <!-- Content: template --> + * </xsl:comment> + */ +typedef xsltStyleBasicEmptyItem xsltStyleItemComment; +typedef xsltStyleItemComment *xsltStyleItemCommentPtr; + +/** + * xsltStyleItemPI: + * + * <!-- Category: instruction --> + * <xsl:processing-instruction + * name = { ncname }> + * <!-- Content: template --> + * </xsl:processing-instruction> + */ +typedef struct _xsltStyleItemPI xsltStyleItemPI; +typedef xsltStyleItemPI *xsltStyleItemPIPtr; + +struct _xsltStyleItemPI { + XSLT_ITEM_COMMON_FIELDS + const xmlChar *name; + int has_name; +}; + +/** + * xsltStyleItemApplyImports: + * + * <!-- Category: instruction --> + * <xsl:apply-imports /> + */ +typedef xsltStyleBasicEmptyItem xsltStyleItemApplyImports; +typedef xsltStyleItemApplyImports *xsltStyleItemApplyImportsPtr; + +/** + * xsltStyleItemApplyTemplates: + * + * <!-- Category: instruction --> + * <xsl:apply-templates + * select = node-set-expression + * mode = qname> + * <!-- Content: (xsl:sort | xsl:with-param)* --> + * </xsl:apply-templates> + */ +typedef struct _xsltStyleItemApplyTemplates xsltStyleItemApplyTemplates; +typedef xsltStyleItemApplyTemplates *xsltStyleItemApplyTemplatesPtr; + +struct _xsltStyleItemApplyTemplates { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *mode; /* apply-templates */ + const xmlChar *modeURI; /* apply-templates */ + const xmlChar *select; /* sort, copy-of, value-of, apply-templates */ + xmlXPathCompExprPtr comp; /* a precompiled XPath expression */ + /* TODO: with-params */ +}; + +/** + * xsltStyleItemCallTemplate: + * + * <!-- Category: instruction --> + * <xsl:call-template + * name = qname> + * <!-- Content: xsl:with-param* --> + * </xsl:call-template> + */ +typedef struct _xsltStyleItemCallTemplate xsltStyleItemCallTemplate; +typedef xsltStyleItemCallTemplate *xsltStyleItemCallTemplatePtr; + +struct _xsltStyleItemCallTemplate { + XSLT_ITEM_COMMON_FIELDS + + xsltTemplatePtr templ; /* call-template */ + const xmlChar *name; /* element, attribute, pi */ + int has_name; /* element, attribute, pi */ + const xmlChar *ns; /* element */ + int has_ns; /* element */ + /* TODO: with-params */ +}; + +/** + * xsltStyleItemCopy: + * + * <!-- Category: instruction --> + * <xsl:copy + * use-attribute-sets = qnames> + * <!-- Content: template --> + * </xsl:copy> + */ +typedef struct _xsltStyleItemCopy xsltStyleItemCopy; +typedef xsltStyleItemCopy *xsltStyleItemCopyPtr; + +struct _xsltStyleItemCopy { + XSLT_ITEM_COMMON_FIELDS + const xmlChar *use; /* copy, element */ + int has_use; /* copy, element */ +}; + +/** + * xsltStyleItemIf: + * + * <!-- Category: instruction --> + * <xsl:if + * test = boolean-expression> + * <!-- Content: template --> + * </xsl:if> + */ +typedef struct _xsltStyleItemIf xsltStyleItemIf; +typedef xsltStyleItemIf *xsltStyleItemIfPtr; + +struct _xsltStyleItemIf { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *test; /* if */ + xmlXPathCompExprPtr comp; /* a precompiled XPath expression */ +}; + + +/** + * xsltStyleItemCopyOf: + * + * <!-- Category: instruction --> + * <xsl:copy-of + * select = expression /> + */ +typedef xsltStyleBasicExpressionItem xsltStyleItemCopyOf; +typedef xsltStyleItemCopyOf *xsltStyleItemCopyOfPtr; + +/** + * xsltStyleItemValueOf: + * + * <!-- Category: instruction --> + * <xsl:value-of + * select = string-expression + * disable-output-escaping = "yes" | "no" /> + */ +typedef struct _xsltStyleItemValueOf xsltStyleItemValueOf; +typedef xsltStyleItemValueOf *xsltStyleItemValueOfPtr; + +struct _xsltStyleItemValueOf { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *select; + xmlXPathCompExprPtr comp; /* a precompiled XPath expression */ + int noescape; +}; + +/** + * xsltStyleItemNumber: + * + * <!-- Category: instruction --> + * <xsl:number + * level = "single" | "multiple" | "any" + * count = pattern + * from = pattern + * value = number-expression + * format = { string } + * lang = { nmtoken } + * letter-value = { "alphabetic" | "traditional" } + * grouping-separator = { char } + * grouping-size = { number } /> + */ +typedef struct _xsltStyleItemNumber xsltStyleItemNumber; +typedef xsltStyleItemNumber *xsltStyleItemNumberPtr; + +struct _xsltStyleItemNumber { + XSLT_ITEM_COMMON_FIELDS + xsltNumberData numdata; /* number */ +}; + +/** + * xsltStyleItemChoose: + * + * <!-- Category: instruction --> + * <xsl:choose> + * <!-- Content: (xsl:when+, xsl:otherwise?) --> + * </xsl:choose> + */ +typedef xsltStyleBasicEmptyItem xsltStyleItemChoose; +typedef xsltStyleItemChoose *xsltStyleItemChoosePtr; + +/** + * xsltStyleItemFallback: + * + * <!-- Category: instruction --> + * <xsl:fallback> + * <!-- Content: template --> + * </xsl:fallback> + */ +typedef xsltStyleBasicEmptyItem xsltStyleItemFallback; +typedef xsltStyleItemFallback *xsltStyleItemFallbackPtr; + +/** + * xsltStyleItemForEach: + * + * <!-- Category: instruction --> + * <xsl:for-each + * select = node-set-expression> + * <!-- Content: (xsl:sort*, template) --> + * </xsl:for-each> + */ +typedef xsltStyleBasicExpressionItem xsltStyleItemForEach; +typedef xsltStyleItemForEach *xsltStyleItemForEachPtr; + +/** + * xsltStyleItemMessage: + * + * <!-- Category: instruction --> + * <xsl:message + * terminate = "yes" | "no"> + * <!-- Content: template --> + * </xsl:message> + */ +typedef struct _xsltStyleItemMessage xsltStyleItemMessage; +typedef xsltStyleItemMessage *xsltStyleItemMessagePtr; + +struct _xsltStyleItemMessage { + XSLT_ITEM_COMMON_FIELDS + int terminate; +}; + +/** + * xsltStyleItemDocument: + * + * NOTE: This is not an instruction of XSLT 1.0. + */ +typedef struct _xsltStyleItemDocument xsltStyleItemDocument; +typedef xsltStyleItemDocument *xsltStyleItemDocumentPtr; + +struct _xsltStyleItemDocument { + XSLT_ITEM_COMMON_FIELDS + int ver11; /* assigned: in xsltDocumentComp; + read: nowhere; + TODO: Check if we need. */ + const xmlChar *filename; /* document URL */ + int has_filename; +}; + +/************************************************************************ + * * + * Non-instructions (actually properties of instructions/declarations) * + * * + ************************************************************************/ + +/** + * xsltStyleBasicItemVariable: + * + * Basic struct for xsl:variable, xsl:param and xsl:with-param. + * It's currently important to have equal fields, since + * xsltParseStylesheetCallerParam() is used with xsl:with-param from + * the xslt side and with xsl:param from the exslt side (in + * exsltFuncFunctionFunction()). + * + * FUTURE NOTE: In XSLT 2.0 xsl:param, xsl:variable and xsl:with-param + * have additional different fields. + */ +typedef struct _xsltStyleBasicItemVariable xsltStyleBasicItemVariable; +typedef xsltStyleBasicItemVariable *xsltStyleBasicItemVariablePtr; + +struct _xsltStyleBasicItemVariable { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *select; + xmlXPathCompExprPtr comp; + + const xmlChar *name; + int has_name; + const xmlChar *ns; + int has_ns; +}; + +/** + * xsltStyleItemVariable: + * + * <!-- Category: top-level-element --> + * <xsl:param + * name = qname + * select = expression> + * <!-- Content: template --> + * </xsl:param> + */ +typedef xsltStyleBasicItemVariable xsltStyleItemVariable; +typedef xsltStyleItemVariable *xsltStyleItemVariablePtr; + +/** + * xsltStyleItemParam: + * + * <!-- Category: top-level-element --> + * <xsl:param + * name = qname + * select = expression> + * <!-- Content: template --> + * </xsl:param> + */ +typedef struct _xsltStyleItemParam xsltStyleItemParam; +typedef xsltStyleItemParam *xsltStyleItemParamPtr; + +struct _xsltStyleItemParam { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *select; + xmlXPathCompExprPtr comp; + + const xmlChar *name; + int has_name; + const xmlChar *ns; + int has_ns; +}; + +/** + * xsltStyleItemWithParam: + * + * <xsl:with-param + * name = qname + * select = expression> + * <!-- Content: template --> + * </xsl:with-param> + */ +typedef xsltStyleBasicItemVariable xsltStyleItemWithParam; +typedef xsltStyleItemWithParam *xsltStyleItemWithParamPtr; + +/** + * xsltStyleItemSort: + * + * Reflects the XSLT xsl:sort item. + * Allowed parents: xsl:apply-templates, xsl:for-each + * <xsl:sort + * select = string-expression + * lang = { nmtoken } + * data-type = { "text" | "number" | qname-but-not-ncname } + * order = { "ascending" | "descending" } + * case-order = { "upper-first" | "lower-first" } /> + */ +typedef struct _xsltStyleItemSort xsltStyleItemSort; +typedef xsltStyleItemSort *xsltStyleItemSortPtr; + +struct _xsltStyleItemSort { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *stype; /* sort */ + int has_stype; /* sort */ + int number; /* sort */ + const xmlChar *order; /* sort */ + int has_order; /* sort */ + int descending; /* sort */ + const xmlChar *lang; /* sort */ + int has_lang; /* sort */ + xsltLocale locale; /* sort */ + const xmlChar *case_order; /* sort */ + int lower_first; /* sort */ + + const xmlChar *use; + int has_use; + + const xmlChar *select; /* sort, copy-of, value-of, apply-templates */ + + xmlXPathCompExprPtr comp; /* a precompiled XPath expression */ +}; + + +/** + * xsltStyleItemWhen: + * + * <xsl:when + * test = boolean-expression> + * <!-- Content: template --> + * </xsl:when> + * Allowed parent: xsl:choose + */ +typedef struct _xsltStyleItemWhen xsltStyleItemWhen; +typedef xsltStyleItemWhen *xsltStyleItemWhenPtr; + +struct _xsltStyleItemWhen { + XSLT_ITEM_COMMON_FIELDS + + const xmlChar *test; + xmlXPathCompExprPtr comp; +}; + +/** + * xsltStyleItemOtherwise: + * + * Allowed parent: xsl:choose + * <xsl:otherwise> + * <!-- Content: template --> + * </xsl:otherwise> + */ +typedef struct _xsltStyleItemOtherwise xsltStyleItemOtherwise; +typedef xsltStyleItemOtherwise *xsltStyleItemOtherwisePtr; + +struct _xsltStyleItemOtherwise { + XSLT_ITEM_COMMON_FIELDS +}; + +typedef struct _xsltStyleItemInclude xsltStyleItemInclude; +typedef xsltStyleItemInclude *xsltStyleItemIncludePtr; + +struct _xsltStyleItemInclude { + XSLT_ITEM_COMMON_FIELDS + xsltDocumentPtr include; +}; + +/************************************************************************ + * * + * XSLT elements in forwards-compatible mode * + * * + ************************************************************************/ + +typedef struct _xsltStyleItemUknown xsltStyleItemUknown; +typedef xsltStyleItemUknown *xsltStyleItemUknownPtr; +struct _xsltStyleItemUknown { + XSLT_ITEM_COMMON_FIELDS +}; + + +/************************************************************************ + * * + * Extension elements * + * * + ************************************************************************/ + +/* + * xsltStyleItemExtElement: + * + * Reflects extension elements. + * + * NOTE: Due to the fact that the structure xsltElemPreComp is most + * probably already heavily in use out there by users, so we cannot + * easily change it, we'll create an intermediate structure which will + * hold an xsltElemPreCompPtr. + * BIG NOTE: The only problem I see here is that the user processes the + * content of the stylesheet tree, possibly he'll lookup the node->psvi + * fields in order to find subsequent extension functions. + * In this case, the user's code will break, since the node->psvi + * field will hold now the xsltStyleItemExtElementPtr and not + * the xsltElemPreCompPtr. + * However the place where the structure is anchored in the node-tree, + * namely node->psvi, has beed already once been moved from node->_private + * to node->psvi, so we have a precedent here, which, I think, should allow + * us to change such semantics without headaches. + */ +typedef struct _xsltStyleItemExtElement xsltStyleItemExtElement; +typedef xsltStyleItemExtElement *xsltStyleItemExtElementPtr; +struct _xsltStyleItemExtElement { + XSLT_ITEM_COMMON_FIELDS + xsltElemPreCompPtr item; +}; + +/************************************************************************ + * * + * Literal result elements * + * * + ************************************************************************/ + +typedef struct _xsltEffectiveNs xsltEffectiveNs; +typedef xsltEffectiveNs *xsltEffectiveNsPtr; +struct _xsltEffectiveNs { + xsltEffectiveNsPtr nextInStore; /* storage next */ + xsltEffectiveNsPtr next; /* next item in the list */ + const xmlChar *prefix; + const xmlChar *nsName; + /* + * Indicates if eclared on the literal result element; dunno if really + * needed. + */ + int holdByElem; +}; + +/* + * Info for literal result elements. + * This will be set on the elem->psvi field and will be + * shared by literal result elements, which have the same + * excluded result namespaces; i.e., this *won't* be created uniquely + * for every literal result element. + */ +typedef struct _xsltStyleItemLRElementInfo xsltStyleItemLRElementInfo; +typedef xsltStyleItemLRElementInfo *xsltStyleItemLRElementInfoPtr; +struct _xsltStyleItemLRElementInfo { + XSLT_ITEM_COMMON_FIELDS + /* + * @effectiveNs is the set of effective ns-nodes + * on the literal result element, which will be added to the result + * element if not already existing in the result tree. + * This means that excluded namespaces (via exclude-result-prefixes, + * extension-element-prefixes and the XSLT namespace) not added + * to the set. + * Namespace-aliasing was applied on the @effectiveNs. + */ + xsltEffectiveNsPtr effectiveNs; + +}; + +#ifdef XSLT_REFACTORED + +typedef struct _xsltNsAlias xsltNsAlias; +typedef xsltNsAlias *xsltNsAliasPtr; +struct _xsltNsAlias { + xsltNsAliasPtr next; /* next in the list */ + xmlNsPtr literalNs; + xmlNsPtr targetNs; + xmlDocPtr docOfTargetNs; +}; +#endif + +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + +typedef struct _xsltNsMap xsltNsMap; +typedef xsltNsMap *xsltNsMapPtr; +struct _xsltNsMap { + xsltNsMapPtr next; /* next in the list */ + xmlDocPtr doc; + xmlNodePtr elem; /* the element holding the ns-decl */ + xmlNsPtr ns; /* the xmlNs structure holding the XML namespace name */ + const xmlChar *origNsName; /* the original XML namespace name */ + const xmlChar *newNsName; /* the mapped XML namespace name */ +}; +#endif + +/************************************************************************ + * * + * Compile-time structures for *internal* use only * + * * + ************************************************************************/ + +typedef struct _xsltPrincipalStylesheetData xsltPrincipalStylesheetData; +typedef xsltPrincipalStylesheetData *xsltPrincipalStylesheetDataPtr; + +typedef struct _xsltNsList xsltNsList; +typedef xsltNsList *xsltNsListPtr; +struct _xsltNsList { + xsltNsListPtr next; /* next in the list */ + xmlNsPtr ns; +}; + +/* +* xsltVarInfo: +* +* Used at compilation time for parameters and variables. +*/ +typedef struct _xsltVarInfo xsltVarInfo; +typedef xsltVarInfo *xsltVarInfoPtr; +struct _xsltVarInfo { + xsltVarInfoPtr next; /* next in the list */ + xsltVarInfoPtr prev; + int depth; /* the depth in the tree */ + const xmlChar *name; + const xmlChar *nsName; +}; + +/** + * xsltCompilerNodeInfo: + * + * Per-node information during compile-time. + */ +typedef struct _xsltCompilerNodeInfo xsltCompilerNodeInfo; +typedef xsltCompilerNodeInfo *xsltCompilerNodeInfoPtr; +struct _xsltCompilerNodeInfo { + xsltCompilerNodeInfoPtr next; + xsltCompilerNodeInfoPtr prev; + xmlNodePtr node; + int depth; + xsltTemplatePtr templ; /* The owning template */ + int category; /* XSLT element, LR-element or + extension element */ + xsltStyleType type; + xsltElemPreCompPtr item; /* The compiled information */ + /* The current in-scope namespaces */ + xsltNsListContainerPtr inScopeNs; + /* The current excluded result namespaces */ + xsltPointerListPtr exclResultNs; + /* The current extension instruction namespaces */ + xsltPointerListPtr extElemNs; + + /* The current info for literal result elements. */ + xsltStyleItemLRElementInfoPtr litResElemInfo; + /* + * Set to 1 if in-scope namespaces changed, + * or excluded result namespaces changed, + * or extension element namespaces changed. + * This will trigger creation of new infos + * for literal result elements. + */ + int nsChanged; + int preserveWhitespace; + int stripWhitespace; + int isRoot; /* whether this is the stylesheet's root node */ + int forwardsCompat; /* whether forwards-compatible mode is enabled */ + /* whether the content of an extension element was processed */ + int extContentHandled; + /* the type of the current child */ + xsltStyleType curChildType; +}; + +/** + * XSLT_CCTXT: + * + * get pointer to compiler context + */ +#define XSLT_CCTXT(style) ((xsltCompilerCtxtPtr) style->compCtxt) + +typedef enum { + XSLT_ERROR_SEVERITY_ERROR = 0, + XSLT_ERROR_SEVERITY_WARNING +} xsltErrorSeverityType; + +typedef struct _xsltCompilerCtxt xsltCompilerCtxt; +typedef xsltCompilerCtxt *xsltCompilerCtxtPtr; +struct _xsltCompilerCtxt { + void *errorCtxt; /* user specific error context */ + /* + * used for error/warning reports; e.g. XSLT_ERROR_SEVERITY_WARNING */ + xsltErrorSeverityType errSeverity; + int warnings; /* TODO: number of warnings found at + compilation */ + int errors; /* TODO: number of errors found at + compilation */ + xmlDictPtr dict; + xsltStylesheetPtr style; + int simplified; /* whether this is a simplified stylesheet */ + /* TODO: structured/unstructured error contexts. */ + int depth; /* Current depth of processing */ + + xsltCompilerNodeInfoPtr inode; + xsltCompilerNodeInfoPtr inodeList; + xsltCompilerNodeInfoPtr inodeLast; + xsltPointerListPtr tmpList; /* Used for various purposes */ + /* + * The XSLT version as specified by the stylesheet's root element. + */ + int isInclude; + int hasForwardsCompat; /* whether forwards-compatible mode was used + in a parsing episode */ + int maxNodeInfos; /* TEMP TODO: just for the interest */ + int maxLREs; /* TEMP TODO: just for the interest */ + /* + * In order to keep the old behaviour, applying strict rules of + * the spec can be turned off. This has effect only on special + * mechanisms like whitespace-stripping in the stylesheet. + */ + int strict; + xsltPrincipalStylesheetDataPtr psData; +#ifdef XSLT_REFACTORED_XPATHCOMP + xmlXPathContextPtr xpathCtxt; +#endif + xsltStyleItemUknownPtr unknownItem; + int hasNsAliases; /* Indicator if there was an xsl:namespace-alias. */ + xsltNsAliasPtr nsAliases; + xsltVarInfoPtr ivars; /* Storage of local in-scope variables/params. */ + xsltVarInfoPtr ivar; /* topmost local variable/param. */ +}; + +#else /* XSLT_REFACTORED */ +/* +* The old structures before refactoring. +*/ + +/** + * _xsltStylePreComp: + * + * The in-memory structure corresponding to XSLT stylesheet constructs + * precomputed data. + */ +struct _xsltStylePreComp { + xsltElemPreCompPtr next; /* chained list */ + xsltStyleType type; /* type of the element */ + xsltTransformFunction func; /* handling function */ + xmlNodePtr inst; /* the instruction */ + + /* + * Pre computed values. + */ + + const xmlChar *stype; /* sort */ + int has_stype; /* sort */ + int number; /* sort */ + const xmlChar *order; /* sort */ + int has_order; /* sort */ + int descending; /* sort */ + const xmlChar *lang; /* sort */ + int has_lang; /* sort */ + xsltLocale locale; /* sort */ + const xmlChar *case_order; /* sort */ + int lower_first; /* sort */ + + const xmlChar *use; /* copy, element */ + int has_use; /* copy, element */ + + int noescape; /* text */ + + const xmlChar *name; /* element, attribute, pi */ + int has_name; /* element, attribute, pi */ + const xmlChar *ns; /* element */ + int has_ns; /* element */ + + const xmlChar *mode; /* apply-templates */ + const xmlChar *modeURI; /* apply-templates */ + + const xmlChar *test; /* if */ + + xsltTemplatePtr templ; /* call-template */ + + const xmlChar *select; /* sort, copy-of, value-of, apply-templates */ + + int ver11; /* document */ + const xmlChar *filename; /* document URL */ + int has_filename; /* document */ + + xsltNumberData numdata; /* number */ + + xmlXPathCompExprPtr comp; /* a precompiled XPath expression */ + xmlNsPtr *nsList; /* the namespaces in scope */ + int nsNr; /* the number of namespaces in scope */ +}; + +#endif /* XSLT_REFACTORED */ + + +/* + * The in-memory structure corresponding to an XSLT Variable + * or Param. + */ +typedef struct _xsltStackElem xsltStackElem; +typedef xsltStackElem *xsltStackElemPtr; +struct _xsltStackElem { + struct _xsltStackElem *next;/* chained list */ + xsltStylePreCompPtr comp; /* the compiled form */ + int computed; /* was the evaluation done */ + const xmlChar *name; /* the local part of the name QName */ + const xmlChar *nameURI; /* the URI part of the name QName */ + const xmlChar *select; /* the eval string */ + xmlNodePtr tree; /* the sequence constructor if no eval + string or the location */ + xmlXPathObjectPtr value; /* The value if computed */ + xmlDocPtr fragment; /* The Result Tree Fragments (needed for XSLT 1.0) + which are bound to the variable's lifetime. */ + int level; /* the depth in the tree; + -1 if persistent (e.g. a given xsl:with-param) */ + xsltTransformContextPtr context; /* The transformation context; needed to cache + the variables */ + int flags; +}; + +#ifdef XSLT_REFACTORED + +struct _xsltPrincipalStylesheetData { + /* + * Namespace dictionary for ns-prefixes and ns-names: + * TODO: Shared between stylesheets, and XPath mechanisms. + * Not used yet. + */ + xmlDictPtr namespaceDict; + /* + * Global list of in-scope namespaces. + */ + xsltPointerListPtr inScopeNamespaces; + /* + * Global list of information for [xsl:]excluded-result-prefixes. + */ + xsltPointerListPtr exclResultNamespaces; + /* + * Global list of information for [xsl:]extension-element-prefixes. + */ + xsltPointerListPtr extElemNamespaces; + xsltEffectiveNsPtr effectiveNs; +#ifdef XSLT_REFACTORED_XSLT_NSCOMP + /* + * Namespace name map to get rid of string comparison of namespace names. + */ + xsltNsMapPtr nsMap; +#endif +}; + + +#endif +/* + * Note that we added a @compCtxt field to anchor an stylesheet compilation + * context, since, due to historical reasons, various compile-time function + * take only the stylesheet as argument and not a compilation context. + */ +struct _xsltStylesheet { + /* + * The stylesheet import relation is kept as a tree. + */ + struct _xsltStylesheet *parent; + struct _xsltStylesheet *next; + struct _xsltStylesheet *imports; + + xsltDocumentPtr docList; /* the include document list */ + + /* + * General data on the style sheet document. + */ + xmlDocPtr doc; /* the parsed XML stylesheet */ + xmlHashTablePtr stripSpaces;/* the hash table of the strip-space and + preserve space elements */ + int stripAll; /* strip-space * (1) preserve-space * (-1) */ + xmlHashTablePtr cdataSection;/* the hash table of the cdata-section */ + + /* + * Global variable or parameters. + */ + xsltStackElemPtr variables; /* linked list of param and variables */ + + /* + * Template descriptions. + */ + xsltTemplatePtr templates; /* the ordered list of templates */ + void *templatesHash; /* hash table or wherever compiled templates + informations are stored */ + void *rootMatch; /* template based on / */ + void *keyMatch; /* template based on key() */ + void *elemMatch; /* template based on * */ + void *attrMatch; /* template based on @* */ + void *parentMatch; /* template based on .. */ + void *textMatch; /* template based on text() */ + void *piMatch; /* template based on processing-instruction() */ + void *commentMatch; /* template based on comment() */ + + /* + * Namespace aliases. + * NOTE: Not used in the refactored code. + */ + xmlHashTablePtr nsAliases; /* the namespace alias hash tables */ + + /* + * Attribute sets. + */ + xmlHashTablePtr attributeSets;/* the attribute sets hash tables */ + + /* + * Namespaces. + * TODO: Eliminate this. + */ + xmlHashTablePtr nsHash; /* the set of namespaces in use: + ATTENTION: This is used for + execution of XPath expressions; unfortunately + it restricts the stylesheet to have distinct + prefixes. + TODO: We need to get rid of this. + */ + void *nsDefs; /* ATTENTION TODO: This is currently used to store + xsltExtDefPtr (in extensions.c) and + *not* xmlNsPtr. + */ + + /* + * Key definitions. + */ + void *keys; /* key definitions */ + + /* + * Output related stuff. + */ + xmlChar *method; /* the output method */ + xmlChar *methodURI; /* associated namespace if any */ + xmlChar *version; /* version string */ + xmlChar *encoding; /* encoding string */ + int omitXmlDeclaration; /* omit-xml-declaration = "yes" | "no" */ + + /* + * Number formatting. + */ + xsltDecimalFormatPtr decimalFormat; + int standalone; /* standalone = "yes" | "no" */ + xmlChar *doctypePublic; /* doctype-public string */ + xmlChar *doctypeSystem; /* doctype-system string */ + int indent; /* should output being indented */ + xmlChar *mediaType; /* media-type string */ + + /* + * Precomputed blocks. + */ + xsltElemPreCompPtr preComps;/* list of precomputed blocks */ + int warnings; /* number of warnings found at compilation */ + int errors; /* number of errors found at compilation */ + + xmlChar *exclPrefix; /* last excluded prefixes */ + xmlChar **exclPrefixTab; /* array of excluded prefixes */ + int exclPrefixNr; /* number of excluded prefixes in scope */ + int exclPrefixMax; /* size of the array */ + + void *_private; /* user defined data */ + + /* + * Extensions. + */ + xmlHashTablePtr extInfos; /* the extension data */ + int extrasNr; /* the number of extras required */ + + /* + * For keeping track of nested includes + */ + xsltDocumentPtr includes; /* points to last nested include */ + + /* + * dictionary: shared between stylesheet, context and documents. + */ + xmlDictPtr dict; + /* + * precompiled attribute value templates. + */ + void *attVTs; + /* + * if namespace-alias has an alias for the default stylesheet prefix + * NOTE: Not used in the refactored code. + */ + const xmlChar *defaultAlias; + /* + * bypass pre-processing (already done) (used in imports) + */ + int nopreproc; + /* + * all document text strings were internalized + */ + int internalized; + /* + * Literal Result Element as Stylesheet c.f. section 2.3 + */ + int literal_result; + /* + * The principal stylesheet + */ + xsltStylesheetPtr principal; +#ifdef XSLT_REFACTORED + /* + * Compilation context used during compile-time. + */ + xsltCompilerCtxtPtr compCtxt; /* TODO: Change this to (void *). */ + + xsltPrincipalStylesheetDataPtr principalData; +#endif + /* + * Forwards-compatible processing + */ + int forwards_compatible; + + xmlHashTablePtr namedTemplates; /* hash table of named templates */ +}; + +typedef struct _xsltTransformCache xsltTransformCache; +typedef xsltTransformCache *xsltTransformCachePtr; +struct _xsltTransformCache { + xmlDocPtr RVT; + int nbRVT; + xsltStackElemPtr stackItems; + int nbStackItems; +#ifdef XSLT_DEBUG_PROFILE_CACHE + int dbgCachedRVTs; + int dbgReusedRVTs; + int dbgCachedVars; + int dbgReusedVars; +#endif +}; + +/* + * The in-memory structure corresponding to an XSLT Transformation. + */ +typedef enum { + XSLT_OUTPUT_XML = 0, + XSLT_OUTPUT_HTML, + XSLT_OUTPUT_TEXT +} xsltOutputType; + +typedef enum { + XSLT_STATE_OK = 0, + XSLT_STATE_ERROR, + XSLT_STATE_STOPPED +} xsltTransformState; + +struct _xsltTransformContext { + xsltStylesheetPtr style; /* the stylesheet used */ + xsltOutputType type; /* the type of output */ + + xsltTemplatePtr templ; /* the current template */ + int templNr; /* Nb of templates in the stack */ + int templMax; /* Size of the templtes stack */ + xsltTemplatePtr *templTab; /* the template stack */ + + xsltStackElemPtr vars; /* the current variable list */ + int varsNr; /* Nb of variable list in the stack */ + int varsMax; /* Size of the variable list stack */ + xsltStackElemPtr *varsTab; /* the variable list stack */ + int varsBase; /* the var base for current templ */ + + /* + * Extensions + */ + xmlHashTablePtr extFunctions; /* the extension functions */ + xmlHashTablePtr extElements; /* the extension elements */ + xmlHashTablePtr extInfos; /* the extension data */ + + const xmlChar *mode; /* the current mode */ + const xmlChar *modeURI; /* the current mode URI */ + + xsltDocumentPtr docList; /* the document list */ + + xsltDocumentPtr document; /* the current source document; can be NULL if an RTF */ + xmlNodePtr node; /* the current node being processed */ + xmlNodeSetPtr nodeList; /* the current node list */ + /* xmlNodePtr current; the node */ + + xmlDocPtr output; /* the resulting document */ + xmlNodePtr insert; /* the insertion node */ + + xmlXPathContextPtr xpathCtxt; /* the XPath context */ + xsltTransformState state; /* the current state */ + + /* + * Global variables + */ + xmlHashTablePtr globalVars; /* the global variables and params */ + + xmlNodePtr inst; /* the instruction in the stylesheet */ + + int xinclude; /* should XInclude be processed */ + + const char * outputFile; /* the output URI if known */ + + int profile; /* is this run profiled */ + long prof; /* the current profiled value */ + int profNr; /* Nb of templates in the stack */ + int profMax; /* Size of the templtaes stack */ + long *profTab; /* the profile template stack */ + + void *_private; /* user defined data */ + + int extrasNr; /* the number of extras used */ + int extrasMax; /* the number of extras allocated */ + xsltRuntimeExtraPtr extras; /* extra per runtime informations */ + + xsltDocumentPtr styleList; /* the stylesheet docs list */ + void * sec; /* the security preferences if any */ + + xmlGenericErrorFunc error; /* a specific error handler */ + void * errctx; /* context for the error handler */ + + xsltSortFunc sortfunc; /* a ctxt specific sort routine */ + + /* + * handling of temporary Result Value Tree + * (XSLT 1.0 term: "Result Tree Fragment") + */ + xmlDocPtr tmpRVT; /* list of RVT without persistance */ + xmlDocPtr persistRVT; /* list of persistant RVTs */ + int ctxtflags; /* context processing flags */ + + /* + * Speed optimization when coalescing text nodes + */ + const xmlChar *lasttext; /* last text node content */ + unsigned int lasttsize; /* last text node size */ + unsigned int lasttuse; /* last text node use */ + /* + * Per Context Debugging + */ + int debugStatus; /* the context level debug status */ + unsigned long* traceCode; /* pointer to the variable holding the mask */ + + int parserOptions; /* parser options xmlParserOption */ + + /* + * dictionary: shared between stylesheet, context and documents. + */ + xmlDictPtr dict; + xmlDocPtr tmpDoc; /* Obsolete; not used in the library. */ + /* + * all document text strings are internalized + */ + int internalized; + int nbKeys; + int hasTemplKeyPatterns; + xsltTemplatePtr currentTemplateRule; /* the Current Template Rule */ + xmlNodePtr initialContextNode; + xmlDocPtr initialContextDoc; + xsltTransformCachePtr cache; + void *contextVariable; /* the current variable item */ + xmlDocPtr localRVT; /* list of local tree fragments; will be freed when + the instruction which created the fragment + exits */ + xmlDocPtr localRVTBase; /* Obsolete */ + int keyInitLevel; /* Needed to catch recursive keys issues */ + int depth; /* Needed to catch recursions */ + int maxTemplateDepth; + int maxTemplateVars; +}; + +/** + * CHECK_STOPPED: + * + * Macro to check if the XSLT processing should be stopped. + * Will return from the function. + */ +#define CHECK_STOPPED if (ctxt->state == XSLT_STATE_STOPPED) return; + +/** + * CHECK_STOPPEDE: + * + * Macro to check if the XSLT processing should be stopped. + * Will goto the error: label. + */ +#define CHECK_STOPPEDE if (ctxt->state == XSLT_STATE_STOPPED) goto error; + +/** + * CHECK_STOPPED0: + * + * Macro to check if the XSLT processing should be stopped. + * Will return from the function with a 0 value. + */ +#define CHECK_STOPPED0 if (ctxt->state == XSLT_STATE_STOPPED) return(0); + +/* + * The macro XML_CAST_FPTR is a hack to avoid a gcc warning about + * possible incompatibilities between function pointers and object + * pointers. It is defined in libxml/hash.h within recent versions + * of libxml2, but is put here for compatibility. + */ +#ifndef XML_CAST_FPTR +/** + * XML_CAST_FPTR: + * @fptr: pointer to a function + * + * Macro to do a casting from an object pointer to a + * function pointer without encountering a warning from + * gcc + * + * #define XML_CAST_FPTR(fptr) (*(void **)(&fptr)) + * This macro violated ISO C aliasing rules (gcc4 on s390 broke) + * so it is disabled now + */ + +#define XML_CAST_FPTR(fptr) fptr +#endif +/* + * Functions associated to the internal types +xsltDecimalFormatPtr xsltDecimalFormatGetByName(xsltStylesheetPtr sheet, + xmlChar *name); + */ +XSLTPUBFUN xsltStylesheetPtr XSLTCALL + xsltNewStylesheet (void); +XSLTPUBFUN xsltStylesheetPtr XSLTCALL + xsltParseStylesheetFile (const xmlChar* filename); +XSLTPUBFUN void XSLTCALL + xsltFreeStylesheet (xsltStylesheetPtr style); +XSLTPUBFUN int XSLTCALL + xsltIsBlank (xmlChar *str); +XSLTPUBFUN void XSLTCALL + xsltFreeStackElemList (xsltStackElemPtr elem); +XSLTPUBFUN xsltDecimalFormatPtr XSLTCALL + xsltDecimalFormatGetByName(xsltStylesheetPtr style, + xmlChar *name); + +XSLTPUBFUN xsltStylesheetPtr XSLTCALL + xsltParseStylesheetProcess(xsltStylesheetPtr ret, + xmlDocPtr doc); +XSLTPUBFUN void XSLTCALL + xsltParseStylesheetOutput(xsltStylesheetPtr style, + xmlNodePtr cur); +XSLTPUBFUN xsltStylesheetPtr XSLTCALL + xsltParseStylesheetDoc (xmlDocPtr doc); +XSLTPUBFUN xsltStylesheetPtr XSLTCALL + xsltParseStylesheetImportedDoc(xmlDocPtr doc, + xsltStylesheetPtr style); +XSLTPUBFUN xsltStylesheetPtr XSLTCALL + xsltLoadStylesheetPI (xmlDocPtr doc); +XSLTPUBFUN void XSLTCALL + xsltNumberFormat (xsltTransformContextPtr ctxt, + xsltNumberDataPtr data, + xmlNodePtr node); +XSLTPUBFUN xmlXPathError XSLTCALL + xsltFormatNumberConversion(xsltDecimalFormatPtr self, + xmlChar *format, + double number, + xmlChar **result); + +XSLTPUBFUN void XSLTCALL + xsltParseTemplateContent(xsltStylesheetPtr style, + xmlNodePtr templ); +XSLTPUBFUN int XSLTCALL + xsltAllocateExtra (xsltStylesheetPtr style); +XSLTPUBFUN int XSLTCALL + xsltAllocateExtraCtxt (xsltTransformContextPtr ctxt); +/* + * Extra functions for Result Value Trees + */ +XSLTPUBFUN xmlDocPtr XSLTCALL + xsltCreateRVT (xsltTransformContextPtr ctxt); +XSLTPUBFUN int XSLTCALL + xsltRegisterTmpRVT (xsltTransformContextPtr ctxt, + xmlDocPtr RVT); +XSLTPUBFUN int XSLTCALL + xsltRegisterLocalRVT (xsltTransformContextPtr ctxt, + xmlDocPtr RVT); +XSLTPUBFUN int XSLTCALL + xsltRegisterPersistRVT (xsltTransformContextPtr ctxt, + xmlDocPtr RVT); +XSLTPUBFUN int XSLTCALL + xsltExtensionInstructionResultRegister( + xsltTransformContextPtr ctxt, + xmlXPathObjectPtr obj); +XSLTPUBFUN int XSLTCALL + xsltExtensionInstructionResultFinalize( + xsltTransformContextPtr ctxt); +XSLTPUBFUN int XSLTCALL + xsltFlagRVTs( + xsltTransformContextPtr ctxt, + xmlXPathObjectPtr obj, + void *val); +XSLTPUBFUN void XSLTCALL + xsltFreeRVTs (xsltTransformContextPtr ctxt); +XSLTPUBFUN void XSLTCALL + xsltReleaseRVT (xsltTransformContextPtr ctxt, + xmlDocPtr RVT); +/* + * Extra functions for Attribute Value Templates + */ +XSLTPUBFUN void XSLTCALL + xsltCompileAttr (xsltStylesheetPtr style, + xmlAttrPtr attr); +XSLTPUBFUN xmlChar * XSLTCALL + xsltEvalAVT (xsltTransformContextPtr ctxt, + void *avt, + xmlNodePtr node); +XSLTPUBFUN void XSLTCALL + xsltFreeAVTList (void *avt); + +/* + * Extra function for successful xsltCleanupGlobals / xsltInit sequence. + */ + +XSLTPUBFUN void XSLTCALL + xsltUninit (void); + +/************************************************************************ + * * + * Compile-time functions for *internal* use only * + * * + ************************************************************************/ + +#ifdef XSLT_REFACTORED +XSLTPUBFUN void XSLTCALL + xsltParseSequenceConstructor( + xsltCompilerCtxtPtr cctxt, + xmlNodePtr start); +XSLTPUBFUN int XSLTCALL + xsltParseAnyXSLTElem (xsltCompilerCtxtPtr cctxt, + xmlNodePtr elem); +#ifdef XSLT_REFACTORED_XSLT_NSCOMP +XSLTPUBFUN int XSLTCALL + xsltRestoreDocumentNamespaces( + xsltNsMapPtr ns, + xmlDocPtr doc); +#endif +#endif /* XSLT_REFACTORED */ + +/************************************************************************ + * * + * Transformation-time functions for *internal* use only * + * * + ************************************************************************/ +XSLTPUBFUN int XSLTCALL + xsltInitCtxtKey (xsltTransformContextPtr ctxt, + xsltDocumentPtr doc, + xsltKeyDefPtr keyd); +XSLTPUBFUN int XSLTCALL + xsltInitAllDocKeys (xsltTransformContextPtr ctxt); +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLT_H__ */ + diff --git a/libxslt/xsltconfig.h.in b/libxslt/xsltconfig.h.in new file mode 100644 index 0000000..6e4e328 --- /dev/null +++ b/libxslt/xsltconfig.h.in @@ -0,0 +1,183 @@ +/* + * Summary: compile-time version informations for the XSLT engine + * Description: compile-time version informations for the XSLT engine + * this module is autogenerated. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLTCONFIG_H__ +#define __XML_XSLTCONFIG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * LIBXSLT_DOTTED_VERSION: + * + * the version string like "1.2.3" + */ +#define LIBXSLT_DOTTED_VERSION "@VERSION@" + +/** + * LIBXSLT_VERSION: + * + * the version number: 1.2.3 value is 10203 + */ +#define LIBXSLT_VERSION @LIBXSLT_VERSION_NUMBER@ + +/** + * LIBXSLT_VERSION_STRING: + * + * the version number string, 1.2.3 value is "10203" + */ +#define LIBXSLT_VERSION_STRING "@LIBXSLT_VERSION_NUMBER@" + +/** + * LIBXSLT_VERSION_EXTRA: + * + * extra version information, used to show a CVS compilation + */ +#define LIBXSLT_VERSION_EXTRA "@LIBXSLT_VERSION_EXTRA@" + +/** + * WITH_XSLT_DEBUG: + * + * Activate the compilation of the debug reporting. Speed penalty + * is insignifiant and being able to run xsltpoc -v is useful. On + * by default unless --without-debug is passed to configure + */ +#if @WITH_XSLT_DEBUG@ +#define WITH_XSLT_DEBUG +#endif + +#if @WITH_MEM_DEBUG@ +/** + * DEBUG_MEMORY: + * + * should be activated only when debugging libxslt. It replaces the + * allocator with a collect and debug shell to the libc allocator. + * Use configure --with-mem-debug to activate it on both library + */ +#define DEBUG_MEMORY + +/** + * DEBUG_MEMORY_LOCATION: + * + * should be activated only when debugging libxslt. + * DEBUG_MEMORY_LOCATION should be activated only when libxml has + * been configured with --with-debug-mem too + */ +#define DEBUG_MEMORY_LOCATION +#endif + +/** + * XSLT_NEED_TRIO: + * + * should be activated if the existing libc library lacks some of the + * string formatting function, in that case reuse the Trio ones already + * compiled in the libxml2 library. + */ + +#if @WITH_TRIO@ +#define XSLT_NEED_TRIO +#endif +#ifdef __VMS +#define HAVE_MATH_H 1 +#define HAVE_SYS_STAT_H 1 +#ifndef XSLT_NEED_TRIO +#define XSLT_NEED_TRIO +#endif +#endif + +#ifdef XSLT_NEED_TRIO +#define TRIO_REPLACE_STDIO +#endif + +/** + * WITH_XSLT_DEBUGGER: + * + * Activate the compilation of the debugger support. Speed penalty + * is insignifiant. + * On by default unless --without-debugger is passed to configure + */ +#if @WITH_DEBUGGER@ +#ifndef WITH_DEBUGGER +#define WITH_DEBUGGER +#endif +#endif + +/** + * WITH_MODULES: + * + * Whether module support is configured into libxslt + * Note: no default module path for win32 platforms + */ +#if @WITH_MODULES@ +#ifndef WITH_MODULES +#define WITH_MODULES +#endif +#define LIBXSLT_DEFAULT_PLUGINS_PATH() "@LIBXSLT_DEFAULT_PLUGINS_PATH@" +#endif + +/** + * Locale support + */ +#if @XSLT_LOCALE_XLOCALE@ +#ifndef XSLT_LOCALE_XLOCALE +#define XSLT_LOCALE_XLOCALE +#endif +#elif @XSLT_LOCALE_WINAPI@ +#ifndef XSLT_LOCALE_WINAPI +#define XSLT_LOCALE_WINAPI +#endif +#endif + +/** + * ATTRIBUTE_UNUSED: + * + * This macro is used to flag unused function parameters to GCC + */ +#ifdef __GNUC__ +#ifdef HAVE_ANSIDECL_H +#include <ansidecl.h> +#endif +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__((unused)) +#endif +#else +#define ATTRIBUTE_UNUSED +#endif + +/** + * LIBXSLT_ATTR_FORMAT: + * + * This macro is used to indicate to GCC the parameters are printf-like + */ +#ifdef __GNUC__ +#define LIBXSLT_ATTR_FORMAT(fmt,args) __attribute__((__format__(__printf__,fmt,args))) +#else +#define LIBXSLT_ATTR_FORMAT(fmt,args) +#endif + +/** + * LIBXSLT_PUBLIC: + * + * This macro is used to declare PUBLIC variables for Cygwin and for MSC on Windows + */ +#if !defined LIBXSLT_PUBLIC +#if (defined(__CYGWIN__) || defined _MSC_VER) && !defined IN_LIBXSLT && !defined LIBXSLT_STATIC +#define LIBXSLT_PUBLIC __declspec(dllimport) +#else +#define LIBXSLT_PUBLIC +#endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLTCONFIG_H__ */ diff --git a/libxslt/xsltexports.h b/libxslt/xsltexports.h new file mode 100644 index 0000000..825c122 --- /dev/null +++ b/libxslt/xsltexports.h @@ -0,0 +1,142 @@ +/* + * Summary: macros for marking symbols as exportable/importable. + * Description: macros for marking symbols as exportable/importable. + * + * Copy: See Copyright for the status of this software. + * + * Author: Igor Zlatkovic <igor@zlatkovic.com> + */ + +#ifndef __XSLT_EXPORTS_H__ +#define __XSLT_EXPORTS_H__ + +/** + * XSLTPUBFUN: + * XSLTPUBFUN, XSLTPUBVAR, XSLTCALL + * + * Macros which declare an exportable function, an exportable variable and + * the calling convention used for functions. + * + * Please use an extra block for every platform/compiler combination when + * modifying this, rather than overlong #ifdef lines. This helps + * readability as well as the fact that different compilers on the same + * platform might need different definitions. + */ + +/** + * XSLTPUBFUN: + * + * Macros which declare an exportable function + */ +#define XSLTPUBFUN +/** + * XSLTPUBVAR: + * + * Macros which declare an exportable variable + */ +#define XSLTPUBVAR extern +/** + * XSLTCALL: + * + * Macros which declare the called convention for exported functions + */ +#define XSLTCALL + +/** DOC_DISABLE */ + +/* Windows platform with MS compiler */ +#if defined(_WIN32) && defined(_MSC_VER) + #undef XSLTPUBFUN + #undef XSLTPUBVAR + #undef XSLTCALL + #if defined(IN_LIBXSLT) && !defined(LIBXSLT_STATIC) + #define XSLTPUBFUN __declspec(dllexport) + #define XSLTPUBVAR __declspec(dllexport) + #else + #define XSLTPUBFUN + #if !defined(LIBXSLT_STATIC) + #define XSLTPUBVAR __declspec(dllimport) extern + #else + #define XSLTPUBVAR extern + #endif + #endif + #define XSLTCALL __cdecl + #if !defined _REENTRANT + #define _REENTRANT + #endif +#endif + +/* Windows platform with Borland compiler */ +#if defined(_WIN32) && defined(__BORLANDC__) + #undef XSLTPUBFUN + #undef XSLTPUBVAR + #undef XSLTCALL + #if defined(IN_LIBXSLT) && !defined(LIBXSLT_STATIC) + #define XSLTPUBFUN __declspec(dllexport) + #define XSLTPUBVAR __declspec(dllexport) extern + #else + #define XSLTPUBFUN + #if !defined(LIBXSLT_STATIC) + #define XSLTPUBVAR __declspec(dllimport) extern + #else + #define XSLTPUBVAR extern + #endif + #endif + #define XSLTCALL __cdecl + #if !defined _REENTRANT + #define _REENTRANT + #endif +#endif + +/* Windows platform with GNU compiler (Mingw) */ +#if defined(_WIN32) && defined(__MINGW32__) + #undef XSLTPUBFUN + #undef XSLTPUBVAR + #undef XSLTCALL +/* + #if defined(IN_LIBXSLT) && !defined(LIBXSLT_STATIC) +*/ + #if !defined(LIBXSLT_STATIC) + #define XSLTPUBFUN __declspec(dllexport) + #define XSLTPUBVAR __declspec(dllexport) extern + #else + #define XSLTPUBFUN + #if !defined(LIBXSLT_STATIC) + #define XSLTPUBVAR __declspec(dllimport) extern + #else + #define XSLTPUBVAR extern + #endif + #endif + #define XSLTCALL __cdecl + #if !defined _REENTRANT + #define _REENTRANT + #endif +#endif + +/* Cygwin platform, GNU compiler */ +#if defined(_WIN32) && defined(__CYGWIN__) + #undef XSLTPUBFUN + #undef XSLTPUBVAR + #undef XSLTCALL + #if defined(IN_LIBXSLT) && !defined(LIBXSLT_STATIC) + #define XSLTPUBFUN __declspec(dllexport) + #define XSLTPUBVAR __declspec(dllexport) + #else + #define XSLTPUBFUN + #if !defined(LIBXSLT_STATIC) + #define XSLTPUBVAR __declspec(dllimport) extern + #else + #define XSLTPUBVAR + #endif + #endif + #define XSLTCALL __cdecl +#endif + +/* Compatibility */ +#if !defined(LIBXSLT_PUBLIC) +#define LIBXSLT_PUBLIC XSLTPUBVAR +#endif + +#endif /* __XSLT_EXPORTS_H__ */ + + diff --git a/libxslt/xsltlocale.c b/libxslt/xsltlocale.c new file mode 100644 index 0000000..b5fe986 --- /dev/null +++ b/libxslt/xsltlocale.c @@ -0,0 +1,525 @@ +/* + * xsltlocale.c: locale handling + * + * Reference: + * RFC 3066: Tags for the Identification of Languages + * http://www.ietf.org/rfc/rfc3066.txt + * ISO 639-1, ISO 3166-1 + * + * Author: Nick Wellnhofer + * winapi port: Roumen Petrov + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#include <string.h> +#include <libxml/xmlmemory.h> + +#include "xsltlocale.h" +#include "xsltutils.h" + +#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ <= 2 +#define newlocale __newlocale +#define freelocale __freelocale +#define strxfrm_l __strxfrm_l +#define LC_COLLATE_MASK (1 << LC_COLLATE) +#endif + +#define TOUPPER(c) (c & ~0x20) +#define TOLOWER(c) (c | 0x20) +#define ISALPHA(c) ((unsigned)(TOUPPER(c) - 'A') < 26) + +/*without terminating null character*/ +#define XSLTMAX_ISO639LANGLEN 8 +#define XSLTMAX_ISO3166CNTRYLEN 8 + /* <lang>-<cntry> */ +#define XSLTMAX_LANGTAGLEN (XSLTMAX_ISO639LANGLEN+1+XSLTMAX_ISO3166CNTRYLEN) + +static const xmlChar* xsltDefaultRegion(const xmlChar *localeName); + +#ifdef XSLT_LOCALE_WINAPI +xmlRMutexPtr xsltLocaleMutex = NULL; + +struct xsltRFC1766Info_s { + /*note typedef unsigned char xmlChar !*/ + xmlChar tag[XSLTMAX_LANGTAGLEN+1]; + /*note typedef LCID xsltLocale !*/ + xsltLocale lcid; +}; +typedef struct xsltRFC1766Info_s xsltRFC1766Info; + +static int xsltLocaleListSize = 0; +static xsltRFC1766Info *xsltLocaleList = NULL; + + +static xsltLocale +xslt_locale_WINAPI(const xmlChar *languageTag) { + int k; + xsltRFC1766Info *p = xsltLocaleList; + + for (k=0; k<xsltLocaleListSize; k++, p++) + if (xmlStrcmp(p->tag, languageTag) == 0) return p->lcid; + return((xsltLocale)0); +} + +static void xsltEnumSupportedLocales(void); +#endif + +/** + * xsltFreeLocales: + * + * Cleanup function for the locale support on shutdown + */ +void +xsltFreeLocales(void) { +#ifdef XSLT_LOCALE_WINAPI + xmlRMutexLock(xsltLocaleMutex); + xmlFree(xsltLocaleList); + xsltLocaleList = NULL; + xmlRMutexUnlock(xsltLocaleMutex); +#endif +} + +/** + * xsltNewLocale: + * @languageTag: RFC 3066 language tag + * + * Creates a new locale of an opaque system dependent type based on the + * language tag. + * + * Returns the locale or NULL on error or if no matching locale was found + */ +xsltLocale +xsltNewLocale(const xmlChar *languageTag) { +#ifdef XSLT_LOCALE_XLOCALE + xsltLocale locale; + char localeName[XSLTMAX_LANGTAGLEN+6]; /* 6 chars for ".utf8\0" */ + const xmlChar *p = languageTag; + const char *region = NULL; + char *q = localeName; + int i, llen; + + /* Convert something like "pt-br" to "pt_BR.utf8" */ + + if (languageTag == NULL) + return(NULL); + + for (i=0; i<XSLTMAX_ISO639LANGLEN && ISALPHA(*p); ++i) + *q++ = TOLOWER(*p++); + + if (i == 0) + return(NULL); + + llen = i; + + if (*p) { + if (*p++ != '-') + return(NULL); + *q++ = '_'; + + for (i=0; i<XSLTMAX_ISO3166CNTRYLEN && ISALPHA(*p); ++i) + *q++ = TOUPPER(*p++); + + if (i == 0 || *p) + return(NULL); + + memcpy(q, ".utf8", 6); + locale = newlocale(LC_COLLATE_MASK, localeName, NULL); + if (locale != NULL) + return(locale); + + /* Continue without using country code */ + + q = localeName + llen; + } + + /* Try locale without territory, e.g. for Esperanto (eo) */ + + memcpy(q, ".utf8", 6); + locale = newlocale(LC_COLLATE_MASK, localeName, NULL); + if (locale != NULL) + return(locale); + + /* Try to find most common country for language */ + + if (llen != 2) + return(NULL); + + region = (char *)xsltDefaultRegion((xmlChar *)localeName); + if (region == NULL) + return(NULL); + + q = localeName + llen; + *q++ = '_'; + *q++ = region[0]; + *q++ = region[1]; + memcpy(q, ".utf8", 6); + locale = newlocale(LC_COLLATE_MASK, localeName, NULL); + + return(locale); +#endif + +#ifdef XSLT_LOCALE_WINAPI +{ + xsltLocale locale = (xsltLocale)0; + xmlChar localeName[XSLTMAX_LANGTAGLEN+1]; + xmlChar *q = localeName; + const xmlChar *p = languageTag; + int i, llen; + const xmlChar *region = NULL; + + if (languageTag == NULL) goto end; + + xsltEnumSupportedLocales(); + + for (i=0; i<XSLTMAX_ISO639LANGLEN && ISALPHA(*p); ++i) + *q++ = TOLOWER(*p++); + if (i == 0) goto end; + + llen = i; + *q++ = '-'; + if (*p) { /*if country tag is given*/ + if (*p++ != '-') goto end; + + for (i=0; i<XSLTMAX_ISO3166CNTRYLEN && ISALPHA(*p); ++i) + *q++ = TOUPPER(*p++); + if (i == 0 || *p) goto end; + + *q = '\0'; + locale = xslt_locale_WINAPI(localeName); + if (locale != (xsltLocale)0) goto end; + } + /* Try to find most common country for language */ + region = xsltDefaultRegion(localeName); + if (region == NULL) goto end; + + strcpy(localeName + llen + 1, region); + locale = xslt_locale_WINAPI(localeName); +end: + return(locale); +} +#endif + +#ifdef XSLT_LOCALE_NONE + return(NULL); +#endif +} + +static const xmlChar* +xsltDefaultRegion(const xmlChar *localeName) { + xmlChar c; + /* region should be xmlChar, but gcc warns on all string assignments */ + const char *region = NULL; + + c = localeName[1]; + /* This is based on the locales from glibc 2.3.3 */ + + switch (localeName[0]) { + case 'a': + if (c == 'a' || c == 'm') region = "ET"; + else if (c == 'f') region = "ZA"; + else if (c == 'n') region = "ES"; + else if (c == 'r') region = "AE"; + else if (c == 'z') region = "AZ"; + break; + case 'b': + if (c == 'e') region = "BY"; + else if (c == 'g') region = "BG"; + else if (c == 'n') region = "BD"; + else if (c == 'r') region = "FR"; + else if (c == 's') region = "BA"; + break; + case 'c': + if (c == 'a') region = "ES"; + else if (c == 's') region = "CZ"; + else if (c == 'y') region = "GB"; + break; + case 'd': + if (c == 'a') region = "DK"; + else if (c == 'e') region = "DE"; + break; + case 'e': + if (c == 'l') region = "GR"; + else if (c == 'n' || c == 'o') region = "US"; + else if (c == 's' || c == 'u') region = "ES"; + else if (c == 't') region = "EE"; + break; + case 'f': + if (c == 'a') region = "IR"; + else if (c == 'i') region = "FI"; + else if (c == 'o') region = "FO"; + else if (c == 'r') region = "FR"; + break; + case 'g': + if (c == 'a') region = "IE"; + else if (c == 'l') region = "ES"; + else if (c == 'v') region = "GB"; + break; + case 'h': + if (c == 'e') region = "IL"; + else if (c == 'i') region = "IN"; + else if (c == 'r') region = "HT"; + else if (c == 'u') region = "HU"; + break; + case 'i': + if (c == 'd') region = "ID"; + else if (c == 's') region = "IS"; + else if (c == 't') region = "IT"; + else if (c == 'w') region = "IL"; + break; + case 'j': + if (c == 'a') region = "JP"; + break; + case 'k': + if (c == 'l') region = "GL"; + else if (c == 'o') region = "KR"; + else if (c == 'w') region = "GB"; + break; + case 'l': + if (c == 't') region = "LT"; + else if (c == 'v') region = "LV"; + break; + case 'm': + if (c == 'k') region = "MK"; + else if (c == 'l' || c == 'r') region = "IN"; + else if (c == 'n') region = "MN"; + else if (c == 's') region = "MY"; + else if (c == 't') region = "MT"; + break; + case 'n': + if (c == 'b' || c == 'n' || c == 'o') region = "NO"; + else if (c == 'e') region = "NP"; + else if (c == 'l') region = "NL"; + break; + case 'o': + if (c == 'm') region = "ET"; + break; + case 'p': + if (c == 'a') region = "IN"; + else if (c == 'l') region = "PL"; + else if (c == 't') region = "PT"; + break; + case 'r': + if (c == 'o') region = "RO"; + else if (c == 'u') region = "RU"; + break; + case 's': + switch (c) { + case 'e': region = "NO"; break; + case 'h': region = "YU"; break; + case 'k': region = "SK"; break; + case 'l': region = "SI"; break; + case 'o': region = "ET"; break; + case 'q': region = "AL"; break; + case 't': region = "ZA"; break; + case 'v': region = "SE"; break; + } + break; + case 't': + if (c == 'a' || c == 'e') region = "IN"; + else if (c == 'h') region = "TH"; + else if (c == 'i') region = "ER"; + else if (c == 'r') region = "TR"; + else if (c == 't') region = "RU"; + break; + case 'u': + if (c == 'k') region = "UA"; + else if (c == 'r') region = "PK"; + break; + case 'v': + if (c == 'i') region = "VN"; + break; + case 'w': + if (c == 'a') region = "BE"; + break; + case 'x': + if (c == 'h') region = "ZA"; + break; + case 'z': + if (c == 'h') region = "CN"; + else if (c == 'u') region = "ZA"; + break; + } + return((xmlChar *)region); +} + +/** + * xsltFreeLocale: + * @locale: the locale to free + * + * Frees a locale created with xsltNewLocale + */ +void +xsltFreeLocale(xsltLocale locale) { +#ifdef XSLT_LOCALE_XLOCALE + freelocale(locale); +#endif +} + +/** + * xsltStrxfrm: + * @locale: locale created with xsltNewLocale + * @string: UTF-8 string to transform + * + * Transforms a string according to locale. The transformed string must then be + * compared with xsltLocaleStrcmp and freed with xmlFree. + * + * Returns the transformed string or NULL on error + */ +xsltLocaleChar * +xsltStrxfrm(xsltLocale locale, const xmlChar *string) +{ +#ifdef XSLT_LOCALE_NONE + return(NULL); +#else + size_t xstrlen, r; + xsltLocaleChar *xstr; + +#ifdef XSLT_LOCALE_XLOCALE + xstrlen = strxfrm_l(NULL, (const char *)string, 0, locale) + 1; + xstr = (xsltLocaleChar *) xmlMalloc(xstrlen); + if (xstr == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltStrxfrm : out of memory error\n"); + return(NULL); + } + + r = strxfrm_l((char *)xstr, (const char *)string, xstrlen, locale); +#endif + +#ifdef XSLT_LOCALE_WINAPI + xstrlen = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0); + if (xstrlen == 0) { + xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : MultiByteToWideChar check failed\n"); + return(NULL); + } + xstr = (xsltLocaleChar*) xmlMalloc(xstrlen * sizeof(xsltLocaleChar)); + if (xstr == NULL) { + xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : out of memory\n"); + return(NULL); + } + r = MultiByteToWideChar(CP_UTF8, 0, string, -1, xstr, xstrlen); + if (r == 0) { + xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : MultiByteToWideChar failed\n"); + xmlFree(xstr); + return(NULL); + } + return(xstr); +#endif /* XSLT_LOCALE_WINAPI */ + + if (r >= xstrlen) { + xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : strxfrm failed\n"); + xmlFree(xstr); + return(NULL); + } + + return(xstr); +#endif /* XSLT_LOCALE_NONE */ +} + +/** + * xsltLocaleStrcmp: + * @locale: a locale identifier + * @str1: a string transformed with xsltStrxfrm + * @str2: a string transformed with xsltStrxfrm + * + * Compares two strings transformed with xsltStrxfrm + * + * Returns a value < 0 if str1 sorts before str2, + * a value > 0 if str1 sorts after str2, + * 0 if str1 and str2 are equal wrt sorting + */ +int +xsltLocaleStrcmp(xsltLocale locale, const xsltLocaleChar *str1, const xsltLocaleChar *str2) { + (void)locale; +#ifdef XSLT_LOCALE_WINAPI +{ + int ret; + if (str1 == str2) return(0); + if (str1 == NULL) return(-1); + if (str2 == NULL) return(1); + ret = CompareStringW(locale, 0, str1, -1, str2, -1); + if (ret == 0) { + xsltTransformError(NULL, NULL, NULL, "xsltLocaleStrcmp : CompareStringW fail\n"); + return(0); + } + return(ret - 2); +} +#else + return(xmlStrcmp(str1, str2)); +#endif +} + +#ifdef XSLT_LOCALE_WINAPI +/** + * xsltCountSupportedLocales: + * @lcid: not used + * + * callback used to count locales + * + * Returns TRUE + */ +BOOL CALLBACK +xsltCountSupportedLocales(LPSTR lcid) { + (void) lcid; + ++xsltLocaleListSize; + return(TRUE); +} + +/** + * xsltIterateSupportedLocales: + * @lcid: not used + * + * callback used to track locales + * + * Returns TRUE if not at the end of the array + */ +BOOL CALLBACK +xsltIterateSupportedLocales(LPSTR lcid) { + static int count = 0; + xmlChar iso639lang [XSLTMAX_ISO639LANGLEN +1]; + xmlChar iso3136ctry[XSLTMAX_ISO3166CNTRYLEN+1]; + int k, l; + xsltRFC1766Info *p = xsltLocaleList + count; + + k = sscanf(lcid, "%lx", (long*)&p->lcid); + if (k < 1) goto end; + /*don't count terminating null character*/ + k = GetLocaleInfoA(p->lcid, LOCALE_SISO639LANGNAME , iso639lang , sizeof(iso639lang )); + if (--k < 1) goto end; + l = GetLocaleInfoA(p->lcid, LOCALE_SISO3166CTRYNAME, iso3136ctry, sizeof(iso3136ctry)); + if (--l < 1) goto end; + + { /*fill results*/ + xmlChar *q = p->tag; + memcpy(q, iso639lang, k); + q += k; + *q++ = '-'; + memcpy(q, iso3136ctry, l); + q += l; + *q = '\0'; + } + ++count; +end: + return((count < xsltLocaleListSize) ? TRUE : FALSE); +} + + +static void +xsltEnumSupportedLocales(void) { + xmlRMutexLock(xsltLocaleMutex); + if (xsltLocaleListSize <= 0) { + size_t len; + + EnumSystemLocalesA(xsltCountSupportedLocales, LCID_SUPPORTED); + + len = xsltLocaleListSize * sizeof(xsltRFC1766Info); + xsltLocaleList = xmlMalloc(len); + memset(xsltLocaleList, 0, len); + EnumSystemLocalesA(xsltIterateSupportedLocales, LCID_SUPPORTED); + } + xmlRMutexUnlock(xsltLocaleMutex); +} + +#endif /*def XSLT_LOCALE_WINAPI*/ diff --git a/libxslt/xsltlocale.h b/libxslt/xsltlocale.h new file mode 100644 index 0000000..8a9ca15 --- /dev/null +++ b/libxslt/xsltlocale.h @@ -0,0 +1,67 @@ +/* + * Summary: Locale handling + * Description: Interfaces for locale handling. Needed for language dependent + * sorting. + * + * Copy: See Copyright for the status of this software. + * + * Author: Nick Wellnhofer + */ + +#ifndef __XML_XSLTLOCALE_H__ +#define __XML_XSLTLOCALE_H__ + +#include <libxml/xmlstring.h> +#include "xsltexports.h" + +#ifdef XSLT_LOCALE_XLOCALE + +#include <locale.h> +#include <xlocale.h> + +#ifdef __GLIBC__ +/*locale_t is defined only if _GNU_SOURCE is defined*/ +typedef __locale_t xsltLocale; +#else +typedef locale_t xsltLocale; +#endif +typedef xmlChar xsltLocaleChar; + +#elif defined(XSLT_LOCALE_WINAPI) + +#include <windows.h> +#include <winnls.h> + +typedef LCID xsltLocale; +typedef wchar_t xsltLocaleChar; + +#else + +/* + * XSLT_LOCALE_NONE: + * Macro indicating that locale are not supported + */ +#ifndef XSLT_LOCALE_NONE +#define XSLT_LOCALE_NONE +#endif + +typedef void *xsltLocale; +typedef xmlChar xsltLocaleChar; + +#endif + +XSLTPUBFUN xsltLocale XSLTCALL + xsltNewLocale (const xmlChar *langName); +XSLTPUBFUN void XSLTCALL + xsltFreeLocale (xsltLocale locale); +XSLTPUBFUN xsltLocaleChar * XSLTCALL + xsltStrxfrm (xsltLocale locale, + const xmlChar *string); +XSLTPUBFUN int XSLTCALL + xsltLocaleStrcmp (xsltLocale locale, + const xsltLocaleChar *str1, + const xsltLocaleChar *str2); +XSLTPUBFUN void XSLTCALL + xsltFreeLocales (void); + +#endif /* __XML_XSLTLOCALE_H__ */ diff --git a/libxslt/xsltutils.c b/libxslt/xsltutils.c new file mode 100644 index 0000000..c250ccf --- /dev/null +++ b/libxslt/xsltutils.c @@ -0,0 +1,2483 @@ +/* + * xsltutils.c: Utilities for the XSL Transformation 1.0 engine + * + * Reference: + * http://www.w3.org/TR/1999/REC-xslt-19991116 + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXSLT +#include "libxslt.h" + +#ifndef XSLT_NEED_TRIO +#include <stdio.h> +#else +#include <trio.h> +#endif + +#include <string.h> +#include <time.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <stdarg.h> + +#include <libxml/xmlmemory.h> +#include <libxml/tree.h> +#include <libxml/HTMLtree.h> +#include <libxml/xmlerror.h> +#include <libxml/xmlIO.h> +#include "xsltutils.h" +#include "templates.h" +#include "xsltInternals.h" +#include "imports.h" +#include "transform.h" + +/* gettimeofday on Windows ??? */ +#if defined(WIN32) && !defined(__CYGWIN__) +#ifdef _MSC_VER +#include <winsock2.h> +#pragma comment(lib, "ws2_32.lib") +#define gettimeofday(p1,p2) +#define HAVE_GETTIMEOFDAY +#define XSLT_WIN32_PERFORMANCE_COUNTER +#endif /* _MS_VER */ +#endif /* WIN32 */ + +/************************************************************************ + * * + * Convenience function * + * * + ************************************************************************/ + +/** + * xsltGetCNsProp: + * @style: the stylesheet + * @node: the node + * @name: the attribute name + * @nameSpace: the URI of the namespace + * + * Similar to xmlGetNsProp() but with a slightly different semantic + * + * Search and get the value of an attribute associated to a node + * This attribute has to be anchored in the namespace specified, + * or has no namespace and the element is in that namespace. + * + * This does the entity substitution. + * This function looks in DTD attribute declaration for #FIXED or + * default declaration values unless DTD use has been turned off. + * + * Returns the attribute value or NULL if not found. The string is allocated + * in the stylesheet dictionary. + */ +const xmlChar * +xsltGetCNsProp(xsltStylesheetPtr style, xmlNodePtr node, + const xmlChar *name, const xmlChar *nameSpace) { + xmlAttrPtr prop; + xmlDocPtr doc; + xmlNsPtr ns; + xmlChar *tmp; + const xmlChar *ret; + + if ((node == NULL) || (style == NULL) || (style->dict == NULL)) + return(NULL); + + if (nameSpace == NULL) + return xmlGetProp(node, name); + + if (node->type == XML_NAMESPACE_DECL) + return(NULL); + if (node->type == XML_ELEMENT_NODE) + prop = node->properties; + else + prop = NULL; + while (prop != NULL) { + /* + * One need to have + * - same attribute names + * - and the attribute carrying that namespace + */ + if ((xmlStrEqual(prop->name, name)) && + (((prop->ns == NULL) && (node->ns != NULL) && + (xmlStrEqual(node->ns->href, nameSpace))) || + ((prop->ns != NULL) && + (xmlStrEqual(prop->ns->href, nameSpace))))) { + + tmp = xmlNodeListGetString(node->doc, prop->children, 1); + if (tmp == NULL) + ret = xmlDictLookup(style->dict, BAD_CAST "", 0); + else { + ret = xmlDictLookup(style->dict, tmp, -1); + xmlFree(tmp); + } + return ret; + } + prop = prop->next; + } + tmp = NULL; + /* + * Check if there is a default declaration in the internal + * or external subsets + */ + doc = node->doc; + if (doc != NULL) { + if (doc->intSubset != NULL) { + xmlAttributePtr attrDecl; + + attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) + attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name); + + if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) { + /* + * The DTD declaration only allows a prefix search + */ + ns = xmlSearchNs(doc, node, attrDecl->prefix); + if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace))) + return(xmlDictLookup(style->dict, + attrDecl->defaultValue, -1)); + } + } + } + return(NULL); +} +/** + * xsltGetNsProp: + * @node: the node + * @name: the attribute name + * @nameSpace: the URI of the namespace + * + * Similar to xmlGetNsProp() but with a slightly different semantic + * + * Search and get the value of an attribute associated to a node + * This attribute has to be anchored in the namespace specified, + * or has no namespace and the element is in that namespace. + * + * This does the entity substitution. + * This function looks in DTD attribute declaration for #FIXED or + * default declaration values unless DTD use has been turned off. + * + * Returns the attribute value or NULL if not found. + * It's up to the caller to free the memory. + */ +xmlChar * +xsltGetNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) { + xmlAttrPtr prop; + xmlDocPtr doc; + xmlNsPtr ns; + + if (node == NULL) + return(NULL); + + if (nameSpace == NULL) + return xmlGetProp(node, name); + + if (node->type == XML_NAMESPACE_DECL) + return(NULL); + if (node->type == XML_ELEMENT_NODE) + prop = node->properties; + else + prop = NULL; + /* + * TODO: Substitute xmlGetProp() for xmlGetNsProp(), since the former + * is not namespace-aware and will return an attribute with equal + * name regardless of its namespace. + * Example: + * <xsl:element foo:name="myName"/> + * So this would return "myName" even if an attribute @name + * in the XSLT was requested. + */ + while (prop != NULL) { + /* + * One need to have + * - same attribute names + * - and the attribute carrying that namespace + */ + if ((xmlStrEqual(prop->name, name)) && + (((prop->ns == NULL) && (node->ns != NULL) && + (xmlStrEqual(node->ns->href, nameSpace))) || + ((prop->ns != NULL) && + (xmlStrEqual(prop->ns->href, nameSpace))))) { + xmlChar *ret; + + ret = xmlNodeListGetString(node->doc, prop->children, 1); + if (ret == NULL) return(xmlStrdup((xmlChar *)"")); + return(ret); + } + prop = prop->next; + } + + /* + * Check if there is a default declaration in the internal + * or external subsets + */ + doc = node->doc; + if (doc != NULL) { + if (doc->intSubset != NULL) { + xmlAttributePtr attrDecl; + + attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) + attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name); + + if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) { + /* + * The DTD declaration only allows a prefix search + */ + ns = xmlSearchNs(doc, node, attrDecl->prefix); + if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace))) + return(xmlStrdup(attrDecl->defaultValue)); + } + } + } + return(NULL); +} + +/** + * xsltGetUTF8Char: + * @utf: a sequence of UTF-8 encoded bytes + * @len: a pointer to @bytes len + * + * Read one UTF8 Char from @utf + * Function copied from libxml2 xmlGetUTF8Char() ... to discard ultimately + * and use the original API + * + * Returns the char value or -1 in case of error and update @len with the + * number of bytes used + */ +int +xsltGetUTF8Char(const unsigned char *utf, int *len) { + unsigned int c; + + if (utf == NULL) + goto error; + if (len == NULL) + goto error; + if (*len < 1) + goto error; + + c = utf[0]; + if (c & 0x80) { + if (*len < 2) + goto error; + if ((utf[1] & 0xc0) != 0x80) + goto error; + if ((c & 0xe0) == 0xe0) { + if (*len < 3) + goto error; + if ((utf[2] & 0xc0) != 0x80) + goto error; + if ((c & 0xf0) == 0xf0) { + if (*len < 4) + goto error; + if ((c & 0xf8) != 0xf0 || (utf[3] & 0xc0) != 0x80) + goto error; + *len = 4; + /* 4-byte code */ + c = (utf[0] & 0x7) << 18; + c |= (utf[1] & 0x3f) << 12; + c |= (utf[2] & 0x3f) << 6; + c |= utf[3] & 0x3f; + } else { + /* 3-byte code */ + *len = 3; + c = (utf[0] & 0xf) << 12; + c |= (utf[1] & 0x3f) << 6; + c |= utf[2] & 0x3f; + } + } else { + /* 2-byte code */ + *len = 2; + c = (utf[0] & 0x1f) << 6; + c |= utf[1] & 0x3f; + } + } else { + /* 1-byte code */ + *len = 1; + } + return(c); + +error: + if (len != NULL) + *len = 0; + return(-1); +} + +#ifdef XSLT_REFACTORED + +/** + * xsltPointerListAddSize: + * @list: the pointer list structure + * @item: the item to be stored + * @initialSize: the initial size of the list + * + * Adds an item to the list. + * + * Returns the position of the added item in the list or + * -1 in case of an error. + */ +int +xsltPointerListAddSize(xsltPointerListPtr list, + void *item, + int initialSize) +{ + if (list->items == NULL) { + if (initialSize <= 0) + initialSize = 1; + list->items = (void **) xmlMalloc( + initialSize * sizeof(void *)); + if (list->items == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltPointerListAddSize: memory allocation failure.\n"); + return(-1); + } + list->number = 0; + list->size = initialSize; + } else if (list->size <= list->number) { + list->size *= 2; + list->items = (void **) xmlRealloc(list->items, + list->size * sizeof(void *)); + if (list->items == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltPointerListAddSize: memory re-allocation failure.\n"); + list->size = 0; + return(-1); + } + } + list->items[list->number++] = item; + return(0); +} + +/** + * xsltPointerListCreate: + * @initialSize: the initial size for the list + * + * Creates an xsltPointerList structure. + * + * Returns a xsltPointerList structure or NULL in case of an error. + */ +xsltPointerListPtr +xsltPointerListCreate(int initialSize) +{ + xsltPointerListPtr ret; + + ret = xmlMalloc(sizeof(xsltPointerList)); + if (ret == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltPointerListCreate: memory allocation failure.\n"); + return (NULL); + } + memset(ret, 0, sizeof(xsltPointerList)); + if (initialSize > 0) { + xsltPointerListAddSize(ret, NULL, initialSize); + ret->number = 0; + } + return (ret); +} + +/** + * xsltPointerListFree: + * @list: pointer to the list to be freed + * + * Frees the xsltPointerList structure. This does not free + * the content of the list. + */ +void +xsltPointerListFree(xsltPointerListPtr list) +{ + if (list == NULL) + return; + if (list->items != NULL) + xmlFree(list->items); + xmlFree(list); +} + +/** + * xsltPointerListClear: + * @list: pointer to the list to be cleared + * + * Resets the list, but does not free the allocated array + * and does not free the content of the list. + */ +void +xsltPointerListClear(xsltPointerListPtr list) +{ + if (list->items != NULL) { + xmlFree(list->items); + list->items = NULL; + } + list->number = 0; + list->size = 0; +} + +#endif /* XSLT_REFACTORED */ + +/************************************************************************ + * * + * Handling of XSLT stylesheets messages * + * * + ************************************************************************/ + +/** + * xsltMessage: + * @ctxt: an XSLT processing context + * @node: The current node + * @inst: The node containing the message instruction + * + * Process and xsl:message construct + */ +void +xsltMessage(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst) { + xmlGenericErrorFunc error = xsltGenericError; + void *errctx = xsltGenericErrorContext; + xmlChar *prop, *message; + int terminate = 0; + + if ((ctxt == NULL) || (inst == NULL)) + return; + + if (ctxt->error != NULL) { + error = ctxt->error; + errctx = ctxt->errctx; + } + + prop = xmlGetNsProp(inst, (const xmlChar *)"terminate", NULL); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *)"yes")) { + terminate = 1; + } else if (xmlStrEqual(prop, (const xmlChar *)"no")) { + terminate = 0; + } else { + xsltTransformError(ctxt, NULL, inst, + "xsl:message : terminate expecting 'yes' or 'no'\n"); + } + xmlFree(prop); + } + message = xsltEvalTemplateString(ctxt, node, inst); + if (message != NULL) { + int len = xmlStrlen(message); + + error(errctx, "%s", (const char *)message); + if ((len > 0) && (message[len - 1] != '\n')) + error(errctx, "\n"); + xmlFree(message); + } + if (terminate) + ctxt->state = XSLT_STATE_STOPPED; +} + +/************************************************************************ + * * + * Handling of out of context errors * + * * + ************************************************************************/ + +#define XSLT_GET_VAR_STR(msg, str) { \ + int size; \ + int chars; \ + char *larger; \ + va_list ap; \ + \ + str = (char *) xmlMalloc(150); \ + if (str == NULL) \ + return; \ + \ + size = 150; \ + \ + while (size < 64000) { \ + va_start(ap, msg); \ + chars = vsnprintf(str, size, msg, ap); \ + va_end(ap); \ + if ((chars > -1) && (chars < size)) \ + break; \ + if (chars > -1) \ + size += chars + 1; \ + else \ + size += 100; \ + if ((larger = (char *) xmlRealloc(str, size)) == NULL) {\ + xmlFree(str); \ + return; \ + } \ + str = larger; \ + } \ +} +/** + * xsltGenericErrorDefaultFunc: + * @ctx: an error context + * @msg: the message to display/transmit + * @...: extra parameters for the message display + * + * Default handler for out of context error messages. + */ +static void LIBXSLT_ATTR_FORMAT(2,3) +xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { + va_list args; + + if (xsltGenericErrorContext == NULL) + xsltGenericErrorContext = (void *) stderr; + + va_start(args, msg); + vfprintf((FILE *)xsltGenericErrorContext, msg, args); + va_end(args); +} + +xmlGenericErrorFunc xsltGenericError = xsltGenericErrorDefaultFunc; +void *xsltGenericErrorContext = NULL; + + +/** + * xsltSetGenericErrorFunc: + * @ctx: the new error handling context + * @handler: the new handler function + * + * Function to reset the handler and the error context for out of + * context error messages. + * This simply means that @handler will be called for subsequent + * error messages while not parsing nor validating. And @ctx will + * be passed as first argument to @handler + * One can simply force messages to be emitted to another FILE * than + * stderr by setting @ctx to this file handle and @handler to NULL. + */ +void +xsltSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) { + xsltGenericErrorContext = ctx; + if (handler != NULL) + xsltGenericError = handler; + else + xsltGenericError = xsltGenericErrorDefaultFunc; +} + +/** + * xsltGenericDebugDefaultFunc: + * @ctx: an error context + * @msg: the message to display/transmit + * @...: extra parameters for the message display + * + * Default handler for out of context error messages. + */ +static void LIBXSLT_ATTR_FORMAT(2,3) +xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { + va_list args; + + if (xsltGenericDebugContext == NULL) + return; + + va_start(args, msg); + vfprintf((FILE *)xsltGenericDebugContext, msg, args); + va_end(args); +} + +xmlGenericErrorFunc xsltGenericDebug = xsltGenericDebugDefaultFunc; +void *xsltGenericDebugContext = NULL; + + +/** + * xsltSetGenericDebugFunc: + * @ctx: the new error handling context + * @handler: the new handler function + * + * Function to reset the handler and the error context for out of + * context error messages. + * This simply means that @handler will be called for subsequent + * error messages while not parsing or validating. And @ctx will + * be passed as first argument to @handler + * One can simply force messages to be emitted to another FILE * than + * stderr by setting @ctx to this file handle and @handler to NULL. + */ +void +xsltSetGenericDebugFunc(void *ctx, xmlGenericErrorFunc handler) { + xsltGenericDebugContext = ctx; + if (handler != NULL) + xsltGenericDebug = handler; + else + xsltGenericDebug = xsltGenericDebugDefaultFunc; +} + +/** + * xsltPrintErrorContext: + * @ctxt: the transformation context + * @style: the stylesheet + * @node: the current node being processed + * + * Display the context of an error. + */ +void +xsltPrintErrorContext(xsltTransformContextPtr ctxt, + xsltStylesheetPtr style, xmlNodePtr node) { + int line = 0; + const xmlChar *file = NULL; + const xmlChar *name = NULL; + const char *type = "error"; + xmlGenericErrorFunc error = xsltGenericError; + void *errctx = xsltGenericErrorContext; + + if (ctxt != NULL) { + if (ctxt->state == XSLT_STATE_OK) + ctxt->state = XSLT_STATE_ERROR; + if (ctxt->error != NULL) { + error = ctxt->error; + errctx = ctxt->errctx; + } + } + if ((node == NULL) && (ctxt != NULL)) + node = ctxt->inst; + + if (node != NULL) { + if ((node->type == XML_DOCUMENT_NODE) || + (node->type == XML_HTML_DOCUMENT_NODE)) { + xmlDocPtr doc = (xmlDocPtr) node; + + file = doc->URL; + } else { + line = xmlGetLineNo(node); + if ((node->doc != NULL) && (node->doc->URL != NULL)) + file = node->doc->URL; + if (node->name != NULL) + name = node->name; + } + } + + if (ctxt != NULL) + type = "runtime error"; + else if (style != NULL) { +#ifdef XSLT_REFACTORED + if (XSLT_CCTXT(style)->errSeverity == XSLT_ERROR_SEVERITY_WARNING) + type = "compilation warning"; + else + type = "compilation error"; +#else + type = "compilation error"; +#endif + } + + if ((file != NULL) && (line != 0) && (name != NULL)) + error(errctx, "%s: file %s line %d element %s\n", + type, file, line, name); + else if ((file != NULL) && (name != NULL)) + error(errctx, "%s: file %s element %s\n", type, file, name); + else if ((file != NULL) && (line != 0)) + error(errctx, "%s: file %s line %d\n", type, file, line); + else if (file != NULL) + error(errctx, "%s: file %s\n", type, file); + else if (name != NULL) + error(errctx, "%s: element %s\n", type, name); + else + error(errctx, "%s\n", type); +} + +/** + * xsltSetTransformErrorFunc: + * @ctxt: the XSLT transformation context + * @ctx: the new error handling context + * @handler: the new handler function + * + * Function to reset the handler and the error context for out of + * context error messages specific to a given XSLT transromation. + * + * This simply means that @handler will be called for subsequent + * error messages while running the transformation. + */ +void +xsltSetTransformErrorFunc(xsltTransformContextPtr ctxt, + void *ctx, xmlGenericErrorFunc handler) +{ + ctxt->error = handler; + ctxt->errctx = ctx; +} + +/** + * xsltTransformError: + * @ctxt: an XSLT transformation context + * @style: the XSLT stylesheet used + * @node: the current node in the stylesheet + * @msg: the message to display/transmit + * @...: extra parameters for the message display + * + * Display and format an error messages, gives file, line, position and + * extra parameters, will use the specific transformation context if available + */ +void +xsltTransformError(xsltTransformContextPtr ctxt, + xsltStylesheetPtr style, + xmlNodePtr node, + const char *msg, ...) { + xmlGenericErrorFunc error = xsltGenericError; + void *errctx = xsltGenericErrorContext; + char * str; + + if (ctxt != NULL) { + if (ctxt->state == XSLT_STATE_OK) + ctxt->state = XSLT_STATE_ERROR; + if (ctxt->error != NULL) { + error = ctxt->error; + errctx = ctxt->errctx; + } + } + if ((node == NULL) && (ctxt != NULL)) + node = ctxt->inst; + xsltPrintErrorContext(ctxt, style, node); + XSLT_GET_VAR_STR(msg, str); + error(errctx, "%s", str); + if (str != NULL) + xmlFree(str); +} + +/************************************************************************ + * * + * QNames * + * * + ************************************************************************/ + +/** + * xsltSplitQName: + * @dict: a dictionary + * @name: the full QName + * @prefix: the return value + * + * Split QNames into prefix and local names, both allocated from a dictionary. + * + * Returns: the localname or NULL in case of error. + */ +const xmlChar * +xsltSplitQName(xmlDictPtr dict, const xmlChar *name, const xmlChar **prefix) { + int len = 0; + const xmlChar *ret = NULL; + + *prefix = NULL; + if ((name == NULL) || (dict == NULL)) return(NULL); + if (name[0] == ':') + return(xmlDictLookup(dict, name, -1)); + while ((name[len] != 0) && (name[len] != ':')) len++; + if (name[len] == 0) return(xmlDictLookup(dict, name, -1)); + *prefix = xmlDictLookup(dict, name, len); + ret = xmlDictLookup(dict, &name[len + 1], -1); + return(ret); +} + +/** + * xsltGetQNameURI: + * @node: the node holding the QName + * @name: pointer to the initial QName value + * + * This function analyzes @name, if the name contains a prefix, + * the function seaches the associated namespace in scope for it. + * It will also replace @name value with the NCName, the old value being + * freed. + * Errors in the prefix lookup are signalled by setting @name to NULL. + * + * NOTE: the namespace returned is a pointer to the place where it is + * defined and hence has the same lifespan as the document holding it. + * + * Returns the namespace URI if there is a prefix, or NULL if @name is + * not prefixed. + */ +const xmlChar * +xsltGetQNameURI(xmlNodePtr node, xmlChar ** name) +{ + int len = 0; + xmlChar *qname; + xmlNsPtr ns; + + if (name == NULL) + return(NULL); + qname = *name; + if ((qname == NULL) || (*qname == 0)) + return(NULL); + if (node == NULL) { + xsltGenericError(xsltGenericErrorContext, + "QName: no element for namespace lookup %s\n", + qname); + xmlFree(qname); + *name = NULL; + return(NULL); + } + + /* nasty but valid */ + if (qname[0] == ':') + return(NULL); + + /* + * we are not trying to validate but just to cut, and yes it will + * work even if this is a set of UTF-8 encoded chars + */ + while ((qname[len] != 0) && (qname[len] != ':')) + len++; + + if (qname[len] == 0) + return(NULL); + + /* + * handle xml: separately, this one is magical + */ + if ((qname[0] == 'x') && (qname[1] == 'm') && + (qname[2] == 'l') && (qname[3] == ':')) { + if (qname[4] == 0) + return(NULL); + *name = xmlStrdup(&qname[4]); + xmlFree(qname); + return(XML_XML_NAMESPACE); + } + + qname[len] = 0; + ns = xmlSearchNs(node->doc, node, qname); + if (ns == NULL) { + xsltGenericError(xsltGenericErrorContext, + "%s:%s : no namespace bound to prefix %s\n", + qname, &qname[len + 1], qname); + *name = NULL; + xmlFree(qname); + return(NULL); + } + *name = xmlStrdup(&qname[len + 1]); + xmlFree(qname); + return(ns->href); +} + +/** + * xsltGetQNameURI2: + * @style: stylesheet pointer + * @node: the node holding the QName + * @name: pointer to the initial QName value + * + * This function is similar to xsltGetQNameURI, but is used when + * @name is a dictionary entry. + * + * Returns the namespace URI if there is a prefix, or NULL if @name is + * not prefixed. + */ +const xmlChar * +xsltGetQNameURI2(xsltStylesheetPtr style, xmlNodePtr node, + const xmlChar **name) { + int len = 0; + xmlChar *qname; + xmlNsPtr ns; + + if (name == NULL) + return(NULL); + qname = (xmlChar *)*name; + if ((qname == NULL) || (*qname == 0)) + return(NULL); + if (node == NULL) { + xsltGenericError(xsltGenericErrorContext, + "QName: no element for namespace lookup %s\n", + qname); + *name = NULL; + return(NULL); + } + + /* + * we are not trying to validate but just to cut, and yes it will + * work even if this is a set of UTF-8 encoded chars + */ + while ((qname[len] != 0) && (qname[len] != ':')) + len++; + + if (qname[len] == 0) + return(NULL); + + /* + * handle xml: separately, this one is magical + */ + if ((qname[0] == 'x') && (qname[1] == 'm') && + (qname[2] == 'l') && (qname[3] == ':')) { + if (qname[4] == 0) + return(NULL); + *name = xmlDictLookup(style->dict, &qname[4], -1); + return(XML_XML_NAMESPACE); + } + + qname = xmlStrndup(*name, len); + ns = xmlSearchNs(node->doc, node, qname); + if (ns == NULL) { + if (style) { + xsltTransformError(NULL, style, node, + "No namespace bound to prefix '%s'.\n", + qname); + style->errors++; + } else { + xsltGenericError(xsltGenericErrorContext, + "%s : no namespace bound to prefix %s\n", + *name, qname); + } + *name = NULL; + xmlFree(qname); + return(NULL); + } + *name = xmlDictLookup(style->dict, (*name)+len+1, -1); + xmlFree(qname); + return(ns->href); +} + +/************************************************************************ + * * + * Sorting * + * * + ************************************************************************/ + +/** + * xsltDocumentSortFunction: + * @list: the node set + * + * reorder the current node list @list accordingly to the document order + * This function is slow, obsolete and should not be used anymore. + */ +void +xsltDocumentSortFunction(xmlNodeSetPtr list) { + int i, j; + int len, tst; + xmlNodePtr node; + + if (list == NULL) + return; + len = list->nodeNr; + if (len <= 1) + return; + /* TODO: sort is really not optimized, does it needs to ? */ + for (i = 0;i < len -1;i++) { + for (j = i + 1; j < len; j++) { + tst = xmlXPathCmpNodes(list->nodeTab[i], list->nodeTab[j]); + if (tst == -1) { + node = list->nodeTab[i]; + list->nodeTab[i] = list->nodeTab[j]; + list->nodeTab[j] = node; + } + } + } +} + +/** + * xsltComputeSortResult: + * @ctxt: a XSLT process context + * @sort: node list + * + * reorder the current node list accordingly to the set of sorting + * requirement provided by the array of nodes. + * + * Returns a ordered XPath nodeset or NULL in case of error. + */ +xmlXPathObjectPtr * +xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) { +#ifdef XSLT_REFACTORED + xsltStyleItemSortPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + xmlXPathObjectPtr *results = NULL; + xmlNodeSetPtr list = NULL; + xmlXPathObjectPtr res; + int len = 0; + int i; + xmlNodePtr oldNode; + xmlNodePtr oldInst; + int oldPos, oldSize ; + int oldNsNr; + xmlNsPtr *oldNamespaces; + + comp = sort->psvi; + if (comp == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsl:sort : compilation failed\n"); + return(NULL); + } + + if ((comp->select == NULL) || (comp->comp == NULL)) + return(NULL); + + list = ctxt->nodeList; + if ((list == NULL) || (list->nodeNr <= 1)) + return(NULL); + + len = list->nodeNr; + + /* TODO: xsl:sort lang attribute */ + /* TODO: xsl:sort case-order attribute */ + + + results = xmlMalloc(len * sizeof(xmlXPathObjectPtr)); + if (results == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltComputeSortResult: memory allocation failure\n"); + return(NULL); + } + + oldNode = ctxt->node; + oldInst = ctxt->inst; + oldPos = ctxt->xpathCtxt->proximityPosition; + oldSize = ctxt->xpathCtxt->contextSize; + oldNsNr = ctxt->xpathCtxt->nsNr; + oldNamespaces = ctxt->xpathCtxt->namespaces; + for (i = 0;i < len;i++) { + ctxt->inst = sort; + ctxt->xpathCtxt->contextSize = len; + ctxt->xpathCtxt->proximityPosition = i + 1; + ctxt->node = list->nodeTab[i]; + ctxt->xpathCtxt->node = ctxt->node; +#ifdef XSLT_REFACTORED + if (comp->inScopeNs != NULL) { + ctxt->xpathCtxt->namespaces = comp->inScopeNs->list; + ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber; + } else { + ctxt->xpathCtxt->namespaces = NULL; + ctxt->xpathCtxt->nsNr = 0; + } +#else + ctxt->xpathCtxt->namespaces = comp->nsList; + ctxt->xpathCtxt->nsNr = comp->nsNr; +#endif + res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt); + if (res != NULL) { + if (res->type != XPATH_STRING) + res = xmlXPathConvertString(res); + if (comp->number) + res = xmlXPathConvertNumber(res); + res->index = i; /* Save original pos for dupl resolv */ + if (comp->number) { + if (res->type == XPATH_NUMBER) { + results[i] = res; + } else { +#ifdef WITH_XSLT_DEBUG_PROCESS + xsltGenericDebug(xsltGenericDebugContext, + "xsltComputeSortResult: select didn't evaluate to a number\n"); +#endif + results[i] = NULL; + } + } else { + if (res->type == XPATH_STRING) { + if (comp->locale != (xsltLocale)0) { + xmlChar *str = res->stringval; + res->stringval = (xmlChar *) xsltStrxfrm(comp->locale, str); + xmlFree(str); + } + + results[i] = res; + } else { +#ifdef WITH_XSLT_DEBUG_PROCESS + xsltGenericDebug(xsltGenericDebugContext, + "xsltComputeSortResult: select didn't evaluate to a string\n"); +#endif + results[i] = NULL; + } + } + } else { + ctxt->state = XSLT_STATE_STOPPED; + results[i] = NULL; + } + } + ctxt->node = oldNode; + ctxt->inst = oldInst; + ctxt->xpathCtxt->contextSize = oldSize; + ctxt->xpathCtxt->proximityPosition = oldPos; + ctxt->xpathCtxt->nsNr = oldNsNr; + ctxt->xpathCtxt->namespaces = oldNamespaces; + + return(results); +} + +/** + * xsltDefaultSortFunction: + * @ctxt: a XSLT process context + * @sorts: array of sort nodes + * @nbsorts: the number of sorts in the array + * + * reorder the current node list accordingly to the set of sorting + * requirement provided by the arry of nodes. + */ +void +xsltDefaultSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts, + int nbsorts) { +#ifdef XSLT_REFACTORED + xsltStyleItemSortPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT]; + xmlXPathObjectPtr *results = NULL, *res; + xmlNodeSetPtr list = NULL; + int descending, number, desc, numb; + int len = 0; + int i, j, incr; + int tst; + int depth; + xmlNodePtr node; + xmlXPathObjectPtr tmp; + int tempstype[XSLT_MAX_SORT], temporder[XSLT_MAX_SORT]; + + if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) || + (nbsorts >= XSLT_MAX_SORT)) + return; + if (sorts[0] == NULL) + return; + comp = sorts[0]->psvi; + if (comp == NULL) + return; + + list = ctxt->nodeList; + if ((list == NULL) || (list->nodeNr <= 1)) + return; /* nothing to do */ + + for (j = 0; j < nbsorts; j++) { + comp = sorts[j]->psvi; + tempstype[j] = 0; + if ((comp->stype == NULL) && (comp->has_stype != 0)) { + comp->stype = + xsltEvalAttrValueTemplate(ctxt, sorts[j], + (const xmlChar *) "data-type", + XSLT_NAMESPACE); + if (comp->stype != NULL) { + tempstype[j] = 1; + if (xmlStrEqual(comp->stype, (const xmlChar *) "text")) + comp->number = 0; + else if (xmlStrEqual(comp->stype, (const xmlChar *) "number")) + comp->number = 1; + else { + xsltTransformError(ctxt, NULL, sorts[j], + "xsltDoSortFunction: no support for data-type = %s\n", + comp->stype); + comp->number = 0; /* use default */ + } + } + } + temporder[j] = 0; + if ((comp->order == NULL) && (comp->has_order != 0)) { + comp->order = xsltEvalAttrValueTemplate(ctxt, sorts[j], + (const xmlChar *) "order", + XSLT_NAMESPACE); + if (comp->order != NULL) { + temporder[j] = 1; + if (xmlStrEqual(comp->order, (const xmlChar *) "ascending")) + comp->descending = 0; + else if (xmlStrEqual(comp->order, + (const xmlChar *) "descending")) + comp->descending = 1; + else { + xsltTransformError(ctxt, NULL, sorts[j], + "xsltDoSortFunction: invalid value %s for order\n", + comp->order); + comp->descending = 0; /* use default */ + } + } + } + } + + len = list->nodeNr; + + resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]); + for (i = 1;i < XSLT_MAX_SORT;i++) + resultsTab[i] = NULL; + + results = resultsTab[0]; + + comp = sorts[0]->psvi; + descending = comp->descending; + number = comp->number; + if (results == NULL) + return; + + /* Shell's sort of node-set */ + for (incr = len / 2; incr > 0; incr /= 2) { + for (i = incr; i < len; i++) { + j = i - incr; + if (results[i] == NULL) + continue; + + while (j >= 0) { + if (results[j] == NULL) + tst = 1; + else { + if (number) { + /* We make NaN smaller than number in accordance + with XSLT spec */ + if (xmlXPathIsNaN(results[j]->floatval)) { + if (xmlXPathIsNaN(results[j + incr]->floatval)) + tst = 0; + else + tst = -1; + } else if (xmlXPathIsNaN(results[j + incr]->floatval)) + tst = 1; + else if (results[j]->floatval == + results[j + incr]->floatval) + tst = 0; + else if (results[j]->floatval > + results[j + incr]->floatval) + tst = 1; + else tst = -1; + } else if(comp->locale != (xsltLocale)0) { + tst = xsltLocaleStrcmp( + comp->locale, + (xsltLocaleChar *) results[j]->stringval, + (xsltLocaleChar *) results[j + incr]->stringval); + } else { + tst = xmlStrcmp(results[j]->stringval, + results[j + incr]->stringval); + } + if (descending) + tst = -tst; + } + if (tst == 0) { + /* + * Okay we need to use multi level sorts + */ + depth = 1; + while (depth < nbsorts) { + if (sorts[depth] == NULL) + break; + comp = sorts[depth]->psvi; + if (comp == NULL) + break; + desc = comp->descending; + numb = comp->number; + + /* + * Compute the result of the next level for the + * full set, this might be optimized ... or not + */ + if (resultsTab[depth] == NULL) + resultsTab[depth] = xsltComputeSortResult(ctxt, + sorts[depth]); + res = resultsTab[depth]; + if (res == NULL) + break; + if (res[j] == NULL) { + if (res[j+incr] != NULL) + tst = 1; + } else { + if (numb) { + /* We make NaN smaller than number in + accordance with XSLT spec */ + if (xmlXPathIsNaN(res[j]->floatval)) { + if (xmlXPathIsNaN(res[j + + incr]->floatval)) + tst = 0; + else + tst = -1; + } else if (xmlXPathIsNaN(res[j + incr]-> + floatval)) + tst = 1; + else if (res[j]->floatval == res[j + incr]-> + floatval) + tst = 0; + else if (res[j]->floatval > + res[j + incr]->floatval) + tst = 1; + else tst = -1; + } else if(comp->locale != (xsltLocale)0) { + tst = xsltLocaleStrcmp( + comp->locale, + (xsltLocaleChar *) res[j]->stringval, + (xsltLocaleChar *) res[j + incr]->stringval); + } else { + tst = xmlStrcmp(res[j]->stringval, + res[j + incr]->stringval); + } + if (desc) + tst = -tst; + } + + /* + * if we still can't differenciate at this level + * try one level deeper. + */ + if (tst != 0) + break; + depth++; + } + } + if (tst == 0) { + tst = results[j]->index > results[j + incr]->index; + } + if (tst > 0) { + tmp = results[j]; + results[j] = results[j + incr]; + results[j + incr] = tmp; + node = list->nodeTab[j]; + list->nodeTab[j] = list->nodeTab[j + incr]; + list->nodeTab[j + incr] = node; + depth = 1; + while (depth < nbsorts) { + if (sorts[depth] == NULL) + break; + if (resultsTab[depth] == NULL) + break; + res = resultsTab[depth]; + tmp = res[j]; + res[j] = res[j + incr]; + res[j + incr] = tmp; + depth++; + } + j -= incr; + } else + break; + } + } + } + + for (j = 0; j < nbsorts; j++) { + comp = sorts[j]->psvi; + if (tempstype[j] == 1) { + /* The data-type needs to be recomputed each time */ + xmlFree((void *)(comp->stype)); + comp->stype = NULL; + } + if (temporder[j] == 1) { + /* The order needs to be recomputed each time */ + xmlFree((void *)(comp->order)); + comp->order = NULL; + } + if (resultsTab[j] != NULL) { + for (i = 0;i < len;i++) + xmlXPathFreeObject(resultsTab[j][i]); + xmlFree(resultsTab[j]); + } + } +} + + +static xsltSortFunc xsltSortFunction = xsltDefaultSortFunction; + +/** + * xsltDoSortFunction: + * @ctxt: a XSLT process context + * @sorts: array of sort nodes + * @nbsorts: the number of sorts in the array + * + * reorder the current node list accordingly to the set of sorting + * requirement provided by the arry of nodes. + * This is a wrapper function, the actual function used is specified + * using xsltSetCtxtSortFunc() to set the context specific sort function, + * or xsltSetSortFunc() to set the global sort function. + * If a sort function is set on the context, this will get called. + * Otherwise the global sort function is called. + */ +void +xsltDoSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr * sorts, + int nbsorts) +{ + if (ctxt->sortfunc != NULL) + (ctxt->sortfunc)(ctxt, sorts, nbsorts); + else if (xsltSortFunction != NULL) + xsltSortFunction(ctxt, sorts, nbsorts); +} + +/** + * xsltSetSortFunc: + * @handler: the new handler function + * + * Function to reset the global handler for XSLT sorting. + * If the handler is NULL, the default sort function will be used. + */ +void +xsltSetSortFunc(xsltSortFunc handler) { + if (handler != NULL) + xsltSortFunction = handler; + else + xsltSortFunction = xsltDefaultSortFunction; +} + +/** + * xsltSetCtxtSortFunc: + * @ctxt: a XSLT process context + * @handler: the new handler function + * + * Function to set the handler for XSLT sorting + * for the specified context. + * If the handler is NULL, then the global + * sort function will be called + */ +void +xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt, xsltSortFunc handler) { + ctxt->sortfunc = handler; +} + +/************************************************************************ + * * + * Parsing options * + * * + ************************************************************************/ + +/** + * xsltSetCtxtParseOptions: + * @ctxt: a XSLT process context + * @options: a combination of libxml2 xmlParserOption + * + * Change the default parser option passed by the XSLT engine to the + * parser when using document() loading. + * + * Returns the previous options or -1 in case of error + */ +int +xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt, int options) +{ + int oldopts; + + if (ctxt == NULL) + return(-1); + oldopts = ctxt->parserOptions; + if (ctxt->xinclude) + oldopts |= XML_PARSE_XINCLUDE; + ctxt->parserOptions = options; + if (options & XML_PARSE_XINCLUDE) + ctxt->xinclude = 1; + else + ctxt->xinclude = 0; + return(oldopts); +} + +/************************************************************************ + * * + * Output * + * * + ************************************************************************/ + +/** + * xsltSaveResultTo: + * @buf: an output buffer + * @result: the result xmlDocPtr + * @style: the stylesheet + * + * Save the result @result obtained by applying the @style stylesheet + * to an I/O output channel @buf + * + * Returns the number of byte written or -1 in case of failure. + */ +int +xsltSaveResultTo(xmlOutputBufferPtr buf, xmlDocPtr result, + xsltStylesheetPtr style) { + const xmlChar *encoding; + int base; + const xmlChar *method; + int indent; + + if ((buf == NULL) || (result == NULL) || (style == NULL)) + return(-1); + if ((result->children == NULL) || + ((result->children->type == XML_DTD_NODE) && + (result->children->next == NULL))) + return(0); + + if ((style->methodURI != NULL) && + ((style->method == NULL) || + (!xmlStrEqual(style->method, (const xmlChar *) "xhtml")))) { + xsltGenericError(xsltGenericErrorContext, + "xsltSaveResultTo : unknown ouput method\n"); + return(-1); + } + + base = buf->written; + + XSLT_GET_IMPORT_PTR(method, style, method) + XSLT_GET_IMPORT_PTR(encoding, style, encoding) + XSLT_GET_IMPORT_INT(indent, style, indent); + + if ((method == NULL) && (result->type == XML_HTML_DOCUMENT_NODE)) + method = (const xmlChar *) "html"; + + if ((method != NULL) && + (xmlStrEqual(method, (const xmlChar *) "html"))) { + if (encoding != NULL) { + htmlSetMetaEncoding(result, (const xmlChar *) encoding); + } else { + htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8"); + } + if (indent == -1) + indent = 1; + htmlDocContentDumpFormatOutput(buf, result, (const char *) encoding, + indent); + xmlOutputBufferFlush(buf); + } else if ((method != NULL) && + (xmlStrEqual(method, (const xmlChar *) "xhtml"))) { + if (encoding != NULL) { + htmlSetMetaEncoding(result, (const xmlChar *) encoding); + } else { + htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8"); + } + htmlDocContentDumpOutput(buf, result, (const char *) encoding); + xmlOutputBufferFlush(buf); + } else if ((method != NULL) && + (xmlStrEqual(method, (const xmlChar *) "text"))) { + xmlNodePtr cur; + + cur = result->children; + while (cur != NULL) { + if (cur->type == XML_TEXT_NODE) + xmlOutputBufferWriteString(buf, (const char *) cur->content); + + /* + * Skip to next node + */ + if (cur->children != NULL) { + if ((cur->children->type != XML_ENTITY_DECL) && + (cur->children->type != XML_ENTITY_REF_NODE) && + (cur->children->type != XML_ENTITY_NODE)) { + cur = cur->children; + continue; + } + } + if (cur->next != NULL) { + cur = cur->next; + continue; + } + + do { + cur = cur->parent; + if (cur == NULL) + break; + if (cur == (xmlNodePtr) style->doc) { + cur = NULL; + break; + } + if (cur->next != NULL) { + cur = cur->next; + break; + } + } while (cur != NULL); + } + xmlOutputBufferFlush(buf); + } else { + int omitXmlDecl; + int standalone; + + XSLT_GET_IMPORT_INT(omitXmlDecl, style, omitXmlDeclaration); + XSLT_GET_IMPORT_INT(standalone, style, standalone); + + if (omitXmlDecl != 1) { + xmlOutputBufferWriteString(buf, "<?xml version="); + if (result->version != NULL) { + xmlOutputBufferWriteString(buf, "\""); + xmlOutputBufferWriteString(buf, (const char *)result->version); + xmlOutputBufferWriteString(buf, "\""); + } else + xmlOutputBufferWriteString(buf, "\"1.0\""); + if (encoding == NULL) { + if (result->encoding != NULL) + encoding = result->encoding; + else if (result->charset != XML_CHAR_ENCODING_UTF8) + encoding = (const xmlChar *) + xmlGetCharEncodingName((xmlCharEncoding) + result->charset); + } + if (encoding != NULL) { + xmlOutputBufferWriteString(buf, " encoding="); + xmlOutputBufferWriteString(buf, "\""); + xmlOutputBufferWriteString(buf, (const char *) encoding); + xmlOutputBufferWriteString(buf, "\""); + } + switch (standalone) { + case 0: + xmlOutputBufferWriteString(buf, " standalone=\"no\""); + break; + case 1: + xmlOutputBufferWriteString(buf, " standalone=\"yes\""); + break; + default: + break; + } + xmlOutputBufferWriteString(buf, "?>\n"); + } + if (result->children != NULL) { + xmlNodePtr child = result->children; + + while (child != NULL) { + xmlNodeDumpOutput(buf, result, child, 0, (indent == 1), + (const char *) encoding); + if (indent && ((child->type == XML_DTD_NODE) || + ((child->type == XML_COMMENT_NODE) && + (child->next != NULL)))) + xmlOutputBufferWriteString(buf, "\n"); + child = child->next; + } + if (indent) + xmlOutputBufferWriteString(buf, "\n"); + } + xmlOutputBufferFlush(buf); + } + return(buf->written - base); +} + +/** + * xsltSaveResultToFilename: + * @URL: a filename or URL + * @result: the result xmlDocPtr + * @style: the stylesheet + * @compression: the compression factor (0 - 9 included) + * + * Save the result @result obtained by applying the @style stylesheet + * to a file or @URL + * + * Returns the number of byte written or -1 in case of failure. + */ +int +xsltSaveResultToFilename(const char *URL, xmlDocPtr result, + xsltStylesheetPtr style, int compression) { + xmlOutputBufferPtr buf; + const xmlChar *encoding; + int ret; + + if ((URL == NULL) || (result == NULL) || (style == NULL)) + return(-1); + if (result->children == NULL) + return(0); + + XSLT_GET_IMPORT_PTR(encoding, style, encoding) + if (encoding != NULL) { + xmlCharEncodingHandlerPtr encoder; + + encoder = xmlFindCharEncodingHandler((char *)encoding); + if ((encoder != NULL) && + (xmlStrEqual((const xmlChar *)encoder->name, + (const xmlChar *) "UTF-8"))) + encoder = NULL; + buf = xmlOutputBufferCreateFilename(URL, encoder, compression); + } else { + buf = xmlOutputBufferCreateFilename(URL, NULL, compression); + } + if (buf == NULL) + return(-1); + xsltSaveResultTo(buf, result, style); + ret = xmlOutputBufferClose(buf); + return(ret); +} + +/** + * xsltSaveResultToFile: + * @file: a FILE * I/O + * @result: the result xmlDocPtr + * @style: the stylesheet + * + * Save the result @result obtained by applying the @style stylesheet + * to an open FILE * I/O. + * This does not close the FILE @file + * + * Returns the number of bytes written or -1 in case of failure. + */ +int +xsltSaveResultToFile(FILE *file, xmlDocPtr result, xsltStylesheetPtr style) { + xmlOutputBufferPtr buf; + const xmlChar *encoding; + int ret; + + if ((file == NULL) || (result == NULL) || (style == NULL)) + return(-1); + if (result->children == NULL) + return(0); + + XSLT_GET_IMPORT_PTR(encoding, style, encoding) + if (encoding != NULL) { + xmlCharEncodingHandlerPtr encoder; + + encoder = xmlFindCharEncodingHandler((char *)encoding); + if ((encoder != NULL) && + (xmlStrEqual((const xmlChar *)encoder->name, + (const xmlChar *) "UTF-8"))) + encoder = NULL; + buf = xmlOutputBufferCreateFile(file, encoder); + } else { + buf = xmlOutputBufferCreateFile(file, NULL); + } + + if (buf == NULL) + return(-1); + xsltSaveResultTo(buf, result, style); + ret = xmlOutputBufferClose(buf); + return(ret); +} + +/** + * xsltSaveResultToFd: + * @fd: a file descriptor + * @result: the result xmlDocPtr + * @style: the stylesheet + * + * Save the result @result obtained by applying the @style stylesheet + * to an open file descriptor + * This does not close the descriptor. + * + * Returns the number of bytes written or -1 in case of failure. + */ +int +xsltSaveResultToFd(int fd, xmlDocPtr result, xsltStylesheetPtr style) { + xmlOutputBufferPtr buf; + const xmlChar *encoding; + int ret; + + if ((fd < 0) || (result == NULL) || (style == NULL)) + return(-1); + if (result->children == NULL) + return(0); + + XSLT_GET_IMPORT_PTR(encoding, style, encoding) + if (encoding != NULL) { + xmlCharEncodingHandlerPtr encoder; + + encoder = xmlFindCharEncodingHandler((char *)encoding); + if ((encoder != NULL) && + (xmlStrEqual((const xmlChar *)encoder->name, + (const xmlChar *) "UTF-8"))) + encoder = NULL; + buf = xmlOutputBufferCreateFd(fd, encoder); + } else { + buf = xmlOutputBufferCreateFd(fd, NULL); + } + if (buf == NULL) + return(-1); + xsltSaveResultTo(buf, result, style); + ret = xmlOutputBufferClose(buf); + return(ret); +} + +/** + * xsltSaveResultToString: + * @doc_txt_ptr: Memory pointer for allocated XML text + * @doc_txt_len: Length of the generated XML text + * @result: the result xmlDocPtr + * @style: the stylesheet + * + * Save the result @result obtained by applying the @style stylesheet + * to a new allocated string. + * + * Returns 0 in case of success and -1 in case of error + */ +int +xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len, + xmlDocPtr result, xsltStylesheetPtr style) { + xmlOutputBufferPtr buf; + const xmlChar *encoding; + + *doc_txt_ptr = NULL; + *doc_txt_len = 0; + if (result->children == NULL) + return(0); + + XSLT_GET_IMPORT_PTR(encoding, style, encoding) + if (encoding != NULL) { + xmlCharEncodingHandlerPtr encoder; + + encoder = xmlFindCharEncodingHandler((char *)encoding); + if ((encoder != NULL) && + (xmlStrEqual((const xmlChar *)encoder->name, + (const xmlChar *) "UTF-8"))) + encoder = NULL; + buf = xmlAllocOutputBuffer(encoder); + } else { + buf = xmlAllocOutputBuffer(NULL); + } + if (buf == NULL) + return(-1); + xsltSaveResultTo(buf, result, style); +#ifdef LIBXML2_NEW_BUFFER + if (buf->conv != NULL) { + *doc_txt_len = xmlBufUse(buf->conv); + *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->conv), *doc_txt_len); + } else { + *doc_txt_len = xmlBufUse(buf->buffer); + *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->buffer), *doc_txt_len); + } +#else + if (buf->conv != NULL) { + *doc_txt_len = buf->conv->use; + *doc_txt_ptr = xmlStrndup(buf->conv->content, *doc_txt_len); + } else { + *doc_txt_len = buf->buffer->use; + *doc_txt_ptr = xmlStrndup(buf->buffer->content, *doc_txt_len); + } +#endif + (void)xmlOutputBufferClose(buf); + return 0; +} + +/************************************************************************ + * * + * Generating profiling informations * + * * + ************************************************************************/ + +static long calibration = -1; + +/** + * xsltCalibrateTimestamps: + * + * Used for to calibrate the xsltTimestamp() function + * Should work if launched at startup and we don't loose our quantum :-) + * + * Returns the number of milliseconds used by xsltTimestamp() + */ +static long +xsltCalibrateTimestamps(void) { + register int i; + + for (i = 0;i < 999;i++) + xsltTimestamp(); + return(xsltTimestamp() / 1000); +} + +/** + * xsltCalibrateAdjust: + * @delta: a negative dealy value found + * + * Used for to correct the calibration for xsltTimestamp() + */ +void +xsltCalibrateAdjust(long delta) { + calibration += delta; +} + +/** + * xsltTimestamp: + * + * Used for gathering profiling data + * + * Returns the number of tenth of milliseconds since the beginning of the + * profiling + */ +long +xsltTimestamp(void) +{ +#ifdef XSLT_WIN32_PERFORMANCE_COUNTER + BOOL ok; + LARGE_INTEGER performanceCount; + LARGE_INTEGER performanceFrequency; + LONGLONG quadCount; + double seconds; + static LONGLONG startupQuadCount = 0; + static LONGLONG startupQuadFreq = 0; + + ok = QueryPerformanceCounter(&performanceCount); + if (!ok) + return 0; + quadCount = performanceCount.QuadPart; + if (calibration < 0) { + calibration = 0; + ok = QueryPerformanceFrequency(&performanceFrequency); + if (!ok) + return 0; + startupQuadFreq = performanceFrequency.QuadPart; + startupQuadCount = quadCount; + return (0); + } + if (startupQuadFreq == 0) + return 0; + seconds = (quadCount - startupQuadCount) / (double) startupQuadFreq; + return (long) (seconds * XSLT_TIMESTAMP_TICS_PER_SEC); + +#else /* XSLT_WIN32_PERFORMANCE_COUNTER */ +#ifdef HAVE_CLOCK_GETTIME +# if defined(CLOCK_MONOTONIC) +# define XSLT_CLOCK CLOCK_MONOTONIC +# elif defined(CLOCK_HIGHRES) +# define XSLT_CLOCK CLOCK_HIGHRES +# else +# define XSLT_CLOCK CLOCK_REALTIME +# endif + static struct timespec startup; + struct timespec cur; + long tics; + + if (calibration < 0) { + clock_gettime(XSLT_CLOCK, &startup); + calibration = 0; + calibration = xsltCalibrateTimestamps(); + clock_gettime(XSLT_CLOCK, &startup); + return (0); + } + + clock_gettime(XSLT_CLOCK, &cur); + tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC; + tics += (cur.tv_nsec - startup.tv_nsec) / + (1000000000l / XSLT_TIMESTAMP_TICS_PER_SEC); + + tics -= calibration; + return(tics); + +#elif HAVE_GETTIMEOFDAY + static struct timeval startup; + struct timeval cur; + long tics; + + if (calibration < 0) { + gettimeofday(&startup, NULL); + calibration = 0; + calibration = xsltCalibrateTimestamps(); + gettimeofday(&startup, NULL); + return (0); + } + + gettimeofday(&cur, NULL); + tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC; + tics += (cur.tv_usec - startup.tv_usec) / + (1000000l / XSLT_TIMESTAMP_TICS_PER_SEC); + + tics -= calibration; + return(tics); +#else + + /* Neither gettimeofday() nor Win32 performance counter available */ + + return (0); + +#endif /* HAVE_GETTIMEOFDAY */ +#endif /* XSLT_WIN32_PERFORMANCE_COUNTER */ +} + +static char * +pretty_templ_match(xsltTemplatePtr templ) { + static char dst[1001]; + char *src = (char *)templ->match; + int i=0,j; + + /* strip white spaces */ + for (j=0; i<1000 && src[j]; i++,j++) { + for(;src[j]==' ';j++); + dst[i]=src[j]; + } + if(i<998 && templ->mode) { + /* append [mode] */ + dst[i++]='['; + src=(char *)templ->mode; + for (j=0; i<999 && src[j]; i++,j++) { + dst[i]=src[j]; + } + dst[i++]=']'; + } + dst[i]='\0'; + return dst; +} + +#define MAX_TEMPLATES 10000 + +/** + * xsltSaveProfiling: + * @ctxt: an XSLT context + * @output: a FILE * for saving the informations + * + * Save the profiling informations on @output + */ +void +xsltSaveProfiling(xsltTransformContextPtr ctxt, FILE *output) { + int nb, i,j,k,l; + int max; + int total; + unsigned long totalt; + xsltTemplatePtr *templates; + xsltStylesheetPtr style; + xsltTemplatePtr templ1,templ2; + int *childt; + + if ((output == NULL) || (ctxt == NULL)) + return; + if (ctxt->profile == 0) + return; + + nb = 0; + max = MAX_TEMPLATES; + templates = xmlMalloc(max * sizeof(xsltTemplatePtr)); + if (templates == NULL) + return; + + style = ctxt->style; + while (style != NULL) { + templ1 = style->templates; + while (templ1 != NULL) { + if (nb >= max) + break; + + if (templ1->nbCalls > 0) + templates[nb++] = templ1; + templ1 = templ1->next; + } + + style = xsltNextImport(style); + } + + for (i = 0;i < nb -1;i++) { + for (j = i + 1; j < nb; j++) { + if ((templates[i]->time <= templates[j]->time) || + ((templates[i]->time == templates[j]->time) && + (templates[i]->nbCalls <= templates[j]->nbCalls))) { + templ1 = templates[j]; + templates[j] = templates[i]; + templates[i] = templ1; + } + } + } + + + /* print flat profile */ + + fprintf(output, "%6s%20s%20s%10s Calls Tot 100us Avg\n\n", + "number", "match", "name", "mode"); + total = 0; + totalt = 0; + for (i = 0;i < nb;i++) { + templ1 = templates[i]; + fprintf(output, "%5d ", i); + if (templ1->match != NULL) { + if (xmlStrlen(templ1->match) > 20) + fprintf(output, "%s\n%26s", templ1->match, ""); + else + fprintf(output, "%20s", templ1->match); + } else { + fprintf(output, "%20s", ""); + } + if (templ1->name != NULL) { + if (xmlStrlen(templ1->name) > 20) + fprintf(output, "%s\n%46s", templ1->name, ""); + else + fprintf(output, "%20s", templ1->name); + } else { + fprintf(output, "%20s", ""); + } + if (templ1->mode != NULL) { + if (xmlStrlen(templ1->mode) > 10) + fprintf(output, "%s\n%56s", templ1->mode, ""); + else + fprintf(output, "%10s", templ1->mode); + } else { + fprintf(output, "%10s", ""); + } + fprintf(output, " %6d", templ1->nbCalls); + fprintf(output, " %6ld %6ld\n", templ1->time, + templ1->time / templ1->nbCalls); + total += templ1->nbCalls; + totalt += templ1->time; + } + fprintf(output, "\n%30s%26s %6d %6ld\n", "Total", "", total, totalt); + + + /* print call graph */ + + childt = xmlMalloc((nb + 1) * sizeof(int)); + if (childt == NULL) + return; + + /* precalculate children times */ + for (i = 0; i < nb; i++) { + templ1 = templates[i]; + + childt[i] = 0; + for (k = 0; k < nb; k++) { + templ2 = templates[k]; + for (l = 0; l < templ2->templNr; l++) { + if (templ2->templCalledTab[l] == templ1) { + childt[i] +=templ2->time; + } + } + } + } + childt[i] = 0; + + fprintf(output, "\nindex %% time self children called name\n"); + + for (i = 0; i < nb; i++) { + char ix_str[20], timep_str[20], times_str[20], timec_str[20], called_str[20]; + unsigned long t; + + templ1 = templates[i]; + /* callers */ + for (j = 0; j < templ1->templNr; j++) { + templ2 = templ1->templCalledTab[j]; + for (k = 0; k < nb; k++) { + if (templates[k] == templ2) + break; + } + t=templ2?templ2->time:totalt; + snprintf(times_str,sizeof(times_str),"%8.3f",(float)t/XSLT_TIMESTAMP_TICS_PER_SEC); + snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC); + snprintf(called_str,sizeof(called_str),"%6d/%d", + templ1->templCountTab[j], /* number of times caller calls 'this' */ + templ1->nbCalls); /* total number of calls to 'this' */ + + fprintf(output, " %-8s %-8s %-12s %s [%d]\n", + times_str,timec_str,called_str, + (templ2?(templ2->name?(char *)templ2->name:pretty_templ_match(templ2)):"-"),k); + } + /* this */ + snprintf(ix_str,sizeof(ix_str),"[%d]",i); + snprintf(timep_str,sizeof(timep_str),"%6.2f",(float)templ1->time*100.0/totalt); + snprintf(times_str,sizeof(times_str),"%8.3f",(float)templ1->time/XSLT_TIMESTAMP_TICS_PER_SEC); + snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[i]/XSLT_TIMESTAMP_TICS_PER_SEC); + fprintf(output, "%-5s %-6s %-8s %-8s %6d %s [%d]\n", + ix_str, timep_str,times_str,timec_str, + templ1->nbCalls, + templ1->name?(char *)templ1->name:pretty_templ_match(templ1),i); + /* callees + * - go over templates[0..nb] and their templCalledTab[] + * - print those where we in the the call-stack + */ + total = 0; + for (k = 0; k < nb; k++) { + templ2 = templates[k]; + for (l = 0; l < templ2->templNr; l++) { + if (templ2->templCalledTab[l] == templ1) { + total+=templ2->templCountTab[l]; + } + } + } + for (k = 0; k < nb; k++) { + templ2 = templates[k]; + for (l = 0; l < templ2->templNr; l++) { + if (templ2->templCalledTab[l] == templ1) { + snprintf(times_str,sizeof(times_str),"%8.3f",(float)templ2->time/XSLT_TIMESTAMP_TICS_PER_SEC); + snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC); + snprintf(called_str,sizeof(called_str),"%6d/%d", + templ2->templCountTab[l], /* number of times 'this' calls callee */ + total); /* total number of calls from 'this' */ + fprintf(output, " %-8s %-8s %-12s %s [%d]\n", + times_str,timec_str,called_str, + templ2->name?(char *)templ2->name:pretty_templ_match(templ2),k); + } + } + } + fprintf(output, "-----------------------------------------------\n"); + } + + fprintf(output, "\f\nIndex by function name\n"); + for (i = 0; i < nb; i++) { + templ1 = templates[i]; + fprintf(output, "[%d] %s (%s:%d)\n", + i, templ1->name?(char *)templ1->name:pretty_templ_match(templ1), + templ1->style->doc->URL,templ1->elem->line); + } + + fprintf(output, "\f\n"); + xmlFree(childt); + + xmlFree(templates); +} + +/************************************************************************ + * * + * Fetching profiling informations * + * * + ************************************************************************/ + +/** + * xsltGetProfileInformation: + * @ctxt: a transformation context + * + * This function should be called after the transformation completed + * to extract template processing profiling informations if availble. + * The informations are returned as an XML document tree like + * <?xml version="1.0"?> + * <profile> + * <template rank="1" match="*" name="" + * mode="" calls="6" time="48" average="8"/> + * <template rank="2" match="item2|item3" name="" + * mode="" calls="10" time="30" average="3"/> + * <template rank="3" match="item1" name="" + * mode="" calls="5" time="17" average="3"/> + * </profile> + * The caller will need to free up the returned tree with xmlFreeDoc() + * + * Returns the xmlDocPtr corresponding to the result or NULL if not available. + */ + +xmlDocPtr +xsltGetProfileInformation(xsltTransformContextPtr ctxt) +{ + xmlDocPtr ret = NULL; + xmlNodePtr root, child; + char buf[100]; + + xsltStylesheetPtr style; + xsltTemplatePtr *templates; + xsltTemplatePtr templ; + int nb = 0, max = 0, i, j; + + if (!ctxt) + return NULL; + + if (!ctxt->profile) + return NULL; + + nb = 0; + max = 10000; + templates = + (xsltTemplatePtr *) xmlMalloc(max * sizeof(xsltTemplatePtr)); + if (templates == NULL) + return NULL; + + /* + * collect all the templates in an array + */ + style = ctxt->style; + while (style != NULL) { + templ = style->templates; + while (templ != NULL) { + if (nb >= max) + break; + + if (templ->nbCalls > 0) + templates[nb++] = templ; + templ = templ->next; + } + + style = (xsltStylesheetPtr) xsltNextImport(style); + } + + /* + * Sort the array by time spent + */ + for (i = 0; i < nb - 1; i++) { + for (j = i + 1; j < nb; j++) { + if ((templates[i]->time <= templates[j]->time) || + ((templates[i]->time == templates[j]->time) && + (templates[i]->nbCalls <= templates[j]->nbCalls))) { + templ = templates[j]; + templates[j] = templates[i]; + templates[i] = templ; + } + } + } + + /* + * Generate a document corresponding to the results. + */ + ret = xmlNewDoc(BAD_CAST "1.0"); + root = xmlNewDocNode(ret, NULL, BAD_CAST "profile", NULL); + xmlDocSetRootElement(ret, root); + + for (i = 0; i < nb; i++) { + child = xmlNewChild(root, NULL, BAD_CAST "template", NULL); + snprintf(buf, sizeof(buf), "%d", i + 1); + xmlSetProp(child, BAD_CAST "rank", BAD_CAST buf); + xmlSetProp(child, BAD_CAST "match", BAD_CAST templates[i]->match); + xmlSetProp(child, BAD_CAST "name", BAD_CAST templates[i]->name); + xmlSetProp(child, BAD_CAST "mode", BAD_CAST templates[i]->mode); + + snprintf(buf, sizeof(buf), "%d", templates[i]->nbCalls); + xmlSetProp(child, BAD_CAST "calls", BAD_CAST buf); + + snprintf(buf, sizeof(buf), "%ld", templates[i]->time); + xmlSetProp(child, BAD_CAST "time", BAD_CAST buf); + + snprintf(buf, sizeof(buf), "%ld", templates[i]->time / templates[i]->nbCalls); + xmlSetProp(child, BAD_CAST "average", BAD_CAST buf); + }; + + xmlFree(templates); + + return ret; +} + +/************************************************************************ + * * + * Hooks for libxml2 XPath * + * * + ************************************************************************/ + +/** + * xsltXPathCompileFlags: + * @style: the stylesheet + * @str: the XPath expression + * @flags: extra compilation flags to pass down to libxml2 XPath + * + * Compile an XPath expression + * + * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL. + * the caller has to free the object. + */ +xmlXPathCompExprPtr +xsltXPathCompileFlags(xsltStylesheetPtr style, const xmlChar *str, int flags) { + xmlXPathContextPtr xpathCtxt; + xmlXPathCompExprPtr ret; + + if (style != NULL) { +#ifdef XSLT_REFACTORED_XPATHCOMP + if (XSLT_CCTXT(style)) { + /* + * Proposed by Jerome Pesenti + * -------------------------- + * For better efficiency we'll reuse the compilation + * context's XPath context. For the common stylesheet using + * XPath expressions this will reduce compilation time to + * about 50%. + * + * See http://mail.gnome.org/archives/xslt/2006-April/msg00037.html + */ + xpathCtxt = XSLT_CCTXT(style)->xpathCtxt; + xpathCtxt->doc = style->doc; + } else + xpathCtxt = xmlXPathNewContext(style->doc); +#else + xpathCtxt = xmlXPathNewContext(style->doc); +#endif + if (xpathCtxt == NULL) + return NULL; + xpathCtxt->dict = style->dict; + } else { + xpathCtxt = xmlXPathNewContext(NULL); + if (xpathCtxt == NULL) + return NULL; + } + xpathCtxt->flags = flags; + + /* + * Compile the expression. + */ + ret = xmlXPathCtxtCompile(xpathCtxt, str); + +#ifdef XSLT_REFACTORED_XPATHCOMP + if ((style == NULL) || (! XSLT_CCTXT(style))) { + xmlXPathFreeContext(xpathCtxt); + } +#else + xmlXPathFreeContext(xpathCtxt); +#endif + /* + * TODO: there is a lot of optimizations which should be possible + * like variable slot precomputations, function precomputations, etc. + */ + + return(ret); +} + +/** + * xsltXPathCompile: + * @style: the stylesheet + * @str: the XPath expression + * + * Compile an XPath expression + * + * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL. + * the caller has to free the object. + */ +xmlXPathCompExprPtr +xsltXPathCompile(xsltStylesheetPtr style, const xmlChar *str) { + return(xsltXPathCompileFlags(style, str, 0)); +} + +/************************************************************************ + * * + * Hooks for the debugger * + * * + ************************************************************************/ + +/* + * There is currently only 3 debugging callback defined + * Debugger callbacks are disabled by default + */ +#define XSLT_CALLBACK_NUMBER 3 + +typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks; +typedef xsltDebuggerCallbacks *xsltDebuggerCallbacksPtr; +struct _xsltDebuggerCallbacks { + xsltHandleDebuggerCallback handler; + xsltAddCallCallback add; + xsltDropCallCallback drop; +}; + +static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks = { + NULL, /* handler */ + NULL, /* add */ + NULL /* drop */ +}; + +int xslDebugStatus; + +/** + * xsltSetDebuggerStatus: + * @value : the value to be set + * + * This function sets the value of xslDebugStatus. + */ +void +xsltSetDebuggerStatus(int value) +{ + xslDebugStatus = value; +} + +/** + * xsltGetDebuggerStatus: + * + * Get xslDebugStatus. + * + * Returns the value of xslDebugStatus. + */ +int +xsltGetDebuggerStatus(void) +{ + return(xslDebugStatus); +} + +/** + * xsltSetDebuggerCallbacks: + * @no : number of callbacks + * @block : the block of callbacks + * + * This function allow to plug a debugger into the XSLT library + * @block points to a block of memory containing the address of @no + * callback routines. + * + * Returns 0 in case of success and -1 in case of error + */ +int +xsltSetDebuggerCallbacks(int no, void *block) +{ + xsltDebuggerCallbacksPtr callbacks; + + if ((block == NULL) || (no != XSLT_CALLBACK_NUMBER)) + return(-1); + + callbacks = (xsltDebuggerCallbacksPtr) block; + xsltDebuggerCurrentCallbacks.handler = callbacks->handler; + xsltDebuggerCurrentCallbacks.add = callbacks->add; + xsltDebuggerCurrentCallbacks.drop = callbacks->drop; + return(0); +} + +/** + * xslHandleDebugger: + * @cur : source node being executed + * @node : data node being processed + * @templ : temlate that applies to node + * @ctxt : the xslt transform context + * + * If either cur or node are a breakpoint, or xslDebugStatus in state + * where debugging must occcur at this time then transfer control + * to the xslDebugBreak function + */ +void +xslHandleDebugger(xmlNodePtr cur, xmlNodePtr node, xsltTemplatePtr templ, + xsltTransformContextPtr ctxt) +{ + if (xsltDebuggerCurrentCallbacks.handler != NULL) + xsltDebuggerCurrentCallbacks.handler(cur, node, templ, ctxt); +} + +/** + * xslAddCall: + * @templ : current template being applied + * @source : the source node being processed + * + * Add template "call" to call stack + * Returns : 1 on sucess 0 otherwise an error may be printed if + * WITH_XSLT_DEBUG_BREAKPOINTS is defined + */ +int +xslAddCall(xsltTemplatePtr templ, xmlNodePtr source) +{ + if (xsltDebuggerCurrentCallbacks.add != NULL) + return(xsltDebuggerCurrentCallbacks.add(templ, source)); + return(0); +} + +/** + * xslDropCall: + * + * Drop the topmost item off the call stack + */ +void +xslDropCall(void) +{ + if (xsltDebuggerCurrentCallbacks.drop != NULL) + xsltDebuggerCurrentCallbacks.drop(); +} + diff --git a/libxslt/xsltutils.h b/libxslt/xsltutils.h new file mode 100644 index 0000000..789865a --- /dev/null +++ b/libxslt/xsltutils.h @@ -0,0 +1,313 @@ +/* + * Summary: set of utilities for the XSLT engine + * Description: interfaces for the utilities module of the XSLT engine. + * things like message handling, profiling, and other + * generally useful routines. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLTUTILS_H__ +#define __XML_XSLTUTILS_H__ + +#include <libxslt/xsltconfig.h> +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#endif +#include <libxml/xpath.h> +#include <libxml/dict.h> +#include <libxml/xmlerror.h> +#include "xsltexports.h" +#include "xsltInternals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * XSLT_TODO: + * + * Macro to flag unimplemented blocks. + */ +#define XSLT_TODO \ + xsltGenericError(xsltGenericErrorContext, \ + "Unimplemented block at %s:%d\n", \ + __FILE__, __LINE__); + +/** + * XSLT_STRANGE: + * + * Macro to flag that a problem was detected internally. + */ +#define XSLT_STRANGE \ + xsltGenericError(xsltGenericErrorContext, \ + "Internal error at %s:%d\n", \ + __FILE__, __LINE__); + +/** + * IS_XSLT_ELEM: + * + * Checks that the element pertains to XSLT namespace. + */ +#define IS_XSLT_ELEM(n) \ + (((n) != NULL) && ((n)->type == XML_ELEMENT_NODE) && \ + ((n)->ns != NULL) && (xmlStrEqual((n)->ns->href, XSLT_NAMESPACE))) + +/** + * IS_XSLT_NAME: + * + * Checks the value of an element in XSLT namespace. + */ +#define IS_XSLT_NAME(n, val) \ + (xmlStrEqual((n)->name, (const xmlChar *) (val))) + +/** + * IS_XSLT_REAL_NODE: + * + * Check that a node is a 'real' one: document, element, text or attribute. + */ +#define IS_XSLT_REAL_NODE(n) \ + (((n) != NULL) && \ + (((n)->type == XML_ELEMENT_NODE) || \ + ((n)->type == XML_TEXT_NODE) || \ + ((n)->type == XML_CDATA_SECTION_NODE) || \ + ((n)->type == XML_ATTRIBUTE_NODE) || \ + ((n)->type == XML_DOCUMENT_NODE) || \ + ((n)->type == XML_HTML_DOCUMENT_NODE) || \ + ((n)->type == XML_COMMENT_NODE) || \ + ((n)->type == XML_PI_NODE))) + +/* + * Our own version of namespaced atributes lookup. + */ +XSLTPUBFUN xmlChar * XSLTCALL + xsltGetNsProp (xmlNodePtr node, + const xmlChar *name, + const xmlChar *nameSpace); +XSLTPUBFUN const xmlChar * XSLTCALL + xsltGetCNsProp (xsltStylesheetPtr style, + xmlNodePtr node, + const xmlChar *name, + const xmlChar *nameSpace); +XSLTPUBFUN int XSLTCALL + xsltGetUTF8Char (const unsigned char *utf, + int *len); + +/* + * XSLT Debug Tracing Tracing Types + */ +typedef enum { + XSLT_TRACE_ALL = -1, + XSLT_TRACE_NONE = 0, + XSLT_TRACE_COPY_TEXT = 1<<0, + XSLT_TRACE_PROCESS_NODE = 1<<1, + XSLT_TRACE_APPLY_TEMPLATE = 1<<2, + XSLT_TRACE_COPY = 1<<3, + XSLT_TRACE_COMMENT = 1<<4, + XSLT_TRACE_PI = 1<<5, + XSLT_TRACE_COPY_OF = 1<<6, + XSLT_TRACE_VALUE_OF = 1<<7, + XSLT_TRACE_CALL_TEMPLATE = 1<<8, + XSLT_TRACE_APPLY_TEMPLATES = 1<<9, + XSLT_TRACE_CHOOSE = 1<<10, + XSLT_TRACE_IF = 1<<11, + XSLT_TRACE_FOR_EACH = 1<<12, + XSLT_TRACE_STRIP_SPACES = 1<<13, + XSLT_TRACE_TEMPLATES = 1<<14, + XSLT_TRACE_KEYS = 1<<15, + XSLT_TRACE_VARIABLES = 1<<16 +} xsltDebugTraceCodes; + +/** + * XSLT_TRACE: + * + * Control the type of xsl debugtrace messages emitted. + */ +#define XSLT_TRACE(ctxt,code,call) \ + if (ctxt->traceCode && (*(ctxt->traceCode) & code)) \ + call + +XSLTPUBFUN void XSLTCALL + xsltDebugSetDefaultTrace(xsltDebugTraceCodes val); +XSLTPUBFUN xsltDebugTraceCodes XSLTCALL + xsltDebugGetDefaultTrace(void); + +/* + * XSLT specific error and debug reporting functions. + */ +XSLTPUBVAR xmlGenericErrorFunc xsltGenericError; +XSLTPUBVAR void *xsltGenericErrorContext; +XSLTPUBVAR xmlGenericErrorFunc xsltGenericDebug; +XSLTPUBVAR void *xsltGenericDebugContext; + +XSLTPUBFUN void XSLTCALL + xsltPrintErrorContext (xsltTransformContextPtr ctxt, + xsltStylesheetPtr style, + xmlNodePtr node); +XSLTPUBFUN void XSLTCALL + xsltMessage (xsltTransformContextPtr ctxt, + xmlNodePtr node, + xmlNodePtr inst); +XSLTPUBFUN void XSLTCALL + xsltSetGenericErrorFunc (void *ctx, + xmlGenericErrorFunc handler); +XSLTPUBFUN void XSLTCALL + xsltSetGenericDebugFunc (void *ctx, + xmlGenericErrorFunc handler); +XSLTPUBFUN void XSLTCALL + xsltSetTransformErrorFunc (xsltTransformContextPtr ctxt, + void *ctx, + xmlGenericErrorFunc handler); +XSLTPUBFUN void XSLTCALL + xsltTransformError (xsltTransformContextPtr ctxt, + xsltStylesheetPtr style, + xmlNodePtr node, + const char *msg, + ...) LIBXSLT_ATTR_FORMAT(4,5); + +XSLTPUBFUN int XSLTCALL + xsltSetCtxtParseOptions (xsltTransformContextPtr ctxt, + int options); +/* + * Sorting. + */ + +XSLTPUBFUN void XSLTCALL + xsltDocumentSortFunction (xmlNodeSetPtr list); +XSLTPUBFUN void XSLTCALL + xsltSetSortFunc (xsltSortFunc handler); +XSLTPUBFUN void XSLTCALL + xsltSetCtxtSortFunc (xsltTransformContextPtr ctxt, + xsltSortFunc handler); +XSLTPUBFUN void XSLTCALL + xsltDefaultSortFunction (xsltTransformContextPtr ctxt, + xmlNodePtr *sorts, + int nbsorts); +XSLTPUBFUN void XSLTCALL + xsltDoSortFunction (xsltTransformContextPtr ctxt, + xmlNodePtr * sorts, + int nbsorts); +XSLTPUBFUN xmlXPathObjectPtr * XSLTCALL + xsltComputeSortResult (xsltTransformContextPtr ctxt, + xmlNodePtr sort); + +/* + * QNames handling. + */ + +XSLTPUBFUN const xmlChar * XSLTCALL + xsltSplitQName (xmlDictPtr dict, + const xmlChar *name, + const xmlChar **prefix); +XSLTPUBFUN const xmlChar * XSLTCALL + xsltGetQNameURI (xmlNodePtr node, + xmlChar **name); + +XSLTPUBFUN const xmlChar * XSLTCALL + xsltGetQNameURI2 (xsltStylesheetPtr style, + xmlNodePtr node, + const xmlChar **name); + +/* + * Output, reuse libxml I/O buffers. + */ +XSLTPUBFUN int XSLTCALL + xsltSaveResultTo (xmlOutputBufferPtr buf, + xmlDocPtr result, + xsltStylesheetPtr style); +XSLTPUBFUN int XSLTCALL + xsltSaveResultToFilename (const char *URI, + xmlDocPtr result, + xsltStylesheetPtr style, + int compression); +XSLTPUBFUN int XSLTCALL + xsltSaveResultToFile (FILE *file, + xmlDocPtr result, + xsltStylesheetPtr style); +XSLTPUBFUN int XSLTCALL + xsltSaveResultToFd (int fd, + xmlDocPtr result, + xsltStylesheetPtr style); +XSLTPUBFUN int XSLTCALL + xsltSaveResultToString (xmlChar **doc_txt_ptr, + int * doc_txt_len, + xmlDocPtr result, + xsltStylesheetPtr style); + +/* + * XPath interface + */ +XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL + xsltXPathCompile (xsltStylesheetPtr style, + const xmlChar *str); +XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL + xsltXPathCompileFlags (xsltStylesheetPtr style, + const xmlChar *str, + int flags); + +/* + * Profiling. + */ +XSLTPUBFUN void XSLTCALL + xsltSaveProfiling (xsltTransformContextPtr ctxt, + FILE *output); +XSLTPUBFUN xmlDocPtr XSLTCALL + xsltGetProfileInformation (xsltTransformContextPtr ctxt); + +XSLTPUBFUN long XSLTCALL + xsltTimestamp (void); +XSLTPUBFUN void XSLTCALL + xsltCalibrateAdjust (long delta); + +/** + * XSLT_TIMESTAMP_TICS_PER_SEC: + * + * Sampling precision for profiling + */ +#define XSLT_TIMESTAMP_TICS_PER_SEC 100000l + +/* + * Hooks for the debugger. + */ + +typedef enum { + XSLT_DEBUG_NONE = 0, /* no debugging allowed */ + XSLT_DEBUG_INIT, + XSLT_DEBUG_STEP, + XSLT_DEBUG_STEPOUT, + XSLT_DEBUG_NEXT, + XSLT_DEBUG_STOP, + XSLT_DEBUG_CONT, + XSLT_DEBUG_RUN, + XSLT_DEBUG_RUN_RESTART, + XSLT_DEBUG_QUIT +} xsltDebugStatusCodes; + +XSLTPUBVAR int xslDebugStatus; + +typedef void (*xsltHandleDebuggerCallback) (xmlNodePtr cur, xmlNodePtr node, + xsltTemplatePtr templ, xsltTransformContextPtr ctxt); +typedef int (*xsltAddCallCallback) (xsltTemplatePtr templ, xmlNodePtr source); +typedef void (*xsltDropCallCallback) (void); + +XSLTPUBFUN void XSLTCALL + xsltSetDebuggerStatus (int value); +XSLTPUBFUN int XSLTCALL + xsltGetDebuggerStatus (void); +XSLTPUBFUN int XSLTCALL + xsltSetDebuggerCallbacks (int no, void *block); +XSLTPUBFUN int XSLTCALL + xslAddCall (xsltTemplatePtr templ, + xmlNodePtr source); +XSLTPUBFUN void XSLTCALL + xslDropCall (void); + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLTUTILS_H__ */ + + diff --git a/libxslt/xsltwin32config.h b/libxslt/xsltwin32config.h new file mode 100644 index 0000000..35c714e --- /dev/null +++ b/libxslt/xsltwin32config.h @@ -0,0 +1,105 @@ +/* + * Summary: compile-time version informations for the XSLT engine + * when compiled on windows + * Description: compile-time version informations for the XSLT engine + * when compiled on windows. This file is generated. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLTWIN32CONFIG_H__ +#define __XML_XSLTWIN32CONFIG_H__ + +#include "win32config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * LIBXSLT_DOTTED_VERSION: + * + * the version string like "1.2.3" + */ +#define LIBXSLT_DOTTED_VERSION "1.1.29" + +/** + * LIBXSLT_VERSION: + * + * the version number: 1.2.3 value is 1002003 + */ +#define LIBXSLT_VERSION 10129 + +/** + * LIBXSLT_VERSION_STRING: + * + * the version number string, 1.2.3 value is "1002003" + */ +#define LIBXSLT_VERSION_STRING "10129" + +/** + * LIBXSLT_VERSION_EXTRA: + * + * extra version information, used to show a CVS compilation + */ +#define LIBXSLT_VERSION_EXTRA "-win32" + +/** + * WITH_XSLT_DEBUG: + * + * Activate the compilation of the debug reporting. Speed penalty + * is insignifiant and being able to run xsltpoc -v is useful. On + * by default + */ +#if 1 +#define WITH_XSLT_DEBUG +#endif + +/** + * WITH_MODULES: + * + * Whether module support is configured into libxslt + */ +#if 1 +#ifndef WITH_MODULES +#define WITH_MODULES +#endif +#define LIBXSLT_PLUGINS_PATH() getenv("LIBXSLT_PLUGINS_PATH") +#endif + +#if 0 +/** + * DEBUG_MEMORY: + * + * should be activated only when debugging libxslt. It replaces the + * allocator with a collect and debug shell to the libc allocator. + * Use configure --with-mem-debug to activate it on both library + */ +#define DEBUG_MEMORY + +/** + * DEBUG_MEMORY_LOCATION: + * + * should be activated only when debugging libxslt. + * DEBUG_MEMORY_LOCATION should be activated only when libxml has + * been configured with --with-debug-mem too + */ +#define DEBUG_MEMORY_LOCATION +#endif + +/** + * ATTRIBUTE_UNUSED: + * + * This macro is used to flag unused function parameters to GCC, useless here + */ +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLTWIN32CONFIG_H__ */ diff --git a/libxslt/xsltwin32config.h.in b/libxslt/xsltwin32config.h.in new file mode 100644 index 0000000..cceeb6b --- /dev/null +++ b/libxslt/xsltwin32config.h.in @@ -0,0 +1,105 @@ +/* + * Summary: compile-time version informations for the XSLT engine + * when compiled on windows + * Description: compile-time version informations for the XSLT engine + * when compiled on windows. This file is generated. + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XSLTWIN32CONFIG_H__ +#define __XML_XSLTWIN32CONFIG_H__ + +#include "win32config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * LIBXSLT_DOTTED_VERSION: + * + * the version string like "1.2.3" + */ +#define LIBXSLT_DOTTED_VERSION "@VERSION@" + +/** + * LIBXSLT_VERSION: + * + * the version number: 1.2.3 value is 1002003 + */ +#define LIBXSLT_VERSION @LIBXSLT_VERSION_NUMBER@ + +/** + * LIBXSLT_VERSION_STRING: + * + * the version number string, 1.2.3 value is "1002003" + */ +#define LIBXSLT_VERSION_STRING "@LIBXSLT_VERSION_NUMBER@" + +/** + * LIBXSLT_VERSION_EXTRA: + * + * extra version information, used to show a CVS compilation + */ +#define LIBXSLT_VERSION_EXTRA "-win32" + +/** + * WITH_XSLT_DEBUG: + * + * Activate the compilation of the debug reporting. Speed penalty + * is insignifiant and being able to run xsltpoc -v is useful. On + * by default + */ +#if 1 +#define WITH_XSLT_DEBUG +#endif + +/** + * WITH_MODULES: + * + * Whether module support is configured into libxslt + */ +#if @WITH_MODULES@ +#ifndef WITH_MODULES +#define WITH_MODULES +#endif +#define LIBXSLT_PLUGINS_PATH() getenv("LIBXSLT_PLUGINS_PATH") +#endif + +#if 0 +/** + * DEBUG_MEMORY: + * + * should be activated only when debugging libxslt. It replaces the + * allocator with a collect and debug shell to the libc allocator. + * Use configure --with-mem-debug to activate it on both library + */ +#define DEBUG_MEMORY + +/** + * DEBUG_MEMORY_LOCATION: + * + * should be activated only when debugging libxslt. + * DEBUG_MEMORY_LOCATION should be activated only when libxml has + * been configured with --with-debug-mem too + */ +#define DEBUG_MEMORY_LOCATION +#endif + +/** + * ATTRIBUTE_UNUSED: + * + * This macro is used to flag unused function parameters to GCC, useless here + */ +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_XSLTWIN32CONFIG_H__ */ |