summaryrefslogtreecommitdiffstats
path: root/libxslt/xsltutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'libxslt/xsltutils.c')
-rw-r--r--libxslt/xsltutils.c2483
1 files changed, 2483 insertions, 0 deletions
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();
+}
+