root/third_party/libxslt/libxslt/xsltutils.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. xsltGetCNsProp
  2. xsltGetNsProp
  3. xsltGetUTF8Char
  4. xsltPointerListAddSize
  5. xsltPointerListCreate
  6. xsltPointerListFree
  7. xsltPointerListClear
  8. xsltMessage
  9. xsltGenericErrorDefaultFunc
  10. xsltSetGenericErrorFunc
  11. xsltGenericDebugDefaultFunc
  12. xsltSetGenericDebugFunc
  13. xsltPrintErrorContext
  14. xsltSetTransformErrorFunc
  15. xsltTransformError
  16. xsltSplitQName
  17. xsltGetQNameURI
  18. xsltGetQNameURI2
  19. xsltDocumentSortFunction
  20. xsltComputeSortResult
  21. xsltDefaultSortFunction
  22. xsltDoSortFunction
  23. xsltSetSortFunc
  24. xsltSetCtxtSortFunc
  25. xsltSetCtxtParseOptions
  26. xsltSaveResultTo
  27. xsltSaveResultToFilename
  28. xsltSaveResultToFile
  29. xsltSaveResultToFd
  30. xsltSaveResultToString
  31. xsltCalibrateTimestamps
  32. xsltCalibrateAdjust
  33. xsltTimestamp
  34. xsltSaveProfiling
  35. xsltGetProfileInformation
  36. xsltXPathCompile
  37. xsltSetDebuggerStatus
  38. xsltGetDebuggerStatus
  39. xsltSetDebuggerCallbacks
  40. xslHandleDebugger
  41. xslAddCall
  42. xslDropCall

/*
 * 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>
#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);

    prop = node->properties;
    if (nameSpace == NULL) {
        return xmlGetProp(node, name);
    }
    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);

    prop = node->properties;
    /*
    * 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.
    */
    if (nameSpace == NULL)
        return(xmlGetProp(node, name));
    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 {
            error(errctx,
                "xsl:message : terminate expecting 'yes' or 'no'\n");
            ctxt->state = XSLT_STATE_ERROR;
        }
        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
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
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) {
        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) {
        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) 
                xmlBufferWriteQuotedString(buf->buffer, result->version);
            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=");
                xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
            }
            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 ((child->type == XML_DTD_NODE) ||
                    ((child->type == XML_COMMENT_NODE) &&
                     (child->next != NULL)))
                    xmlOutputBufferWriteString(buf, "\n");
                child = child->next;
            }
            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);
    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);
    }
    (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_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 */
}

#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;
    int max;
    int total;
    long totalt;
    xsltTemplatePtr *templates;
    xsltStylesheetPtr style;
    xsltTemplatePtr template;

    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) {
        template = style->templates;
        while (template != NULL) {
            if (nb >= max)
                break;

            if (template->nbCalls > 0)
                templates[nb++] = template;
            template = template->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))) {
                template = templates[j];
                templates[j] = templates[i];
                templates[i] = template;
            }
        }
    }

    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++) {
        fprintf(output, "%5d ", i);
        if (templates[i]->match != NULL) {
            if (xmlStrlen(templates[i]->match) > 20)
                fprintf(output, "%s\n%26s", templates[i]->match, "");
            else
                fprintf(output, "%20s", templates[i]->match);
        } else {
            fprintf(output, "%20s", "");
        }
        if (templates[i]->name != NULL) {
            if (xmlStrlen(templates[i]->name) > 20)
                fprintf(output, "%s\n%46s", templates[i]->name, "");
            else
                fprintf(output, "%20s", templates[i]->name);
        } else {
            fprintf(output, "%20s", "");
        }
        if (templates[i]->mode != NULL) {
            if (xmlStrlen(templates[i]->mode) > 10)
                fprintf(output, "%s\n%56s", templates[i]->mode, "");
            else
                fprintf(output, "%10s", templates[i]->mode);
        } else {
            fprintf(output, "%10s", "");
        }
        fprintf(output, " %6d", templates[i]->nbCalls);
        fprintf(output, " %6ld %6ld\n", templates[i]->time,
                templates[i]->time / templates[i]->nbCalls);
        total += templates[i]->nbCalls;
        totalt += templates[i]->time;
    }
    fprintf(output, "\n%30s%26s %6d %6ld\n", "Total", "", total, totalt);

    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);
        sprintf(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);

        sprintf(buf, "%d", templates[i]->nbCalls);
        xmlSetProp(child, BAD_CAST "calls", BAD_CAST buf);

        sprintf(buf, "%ld", templates[i]->time);
        xmlSetProp(child, BAD_CAST "time", BAD_CAST buf);

        sprintf(buf, "%ld", templates[i]->time / templates[i]->nbCalls);
        xmlSetProp(child, BAD_CAST "average", BAD_CAST buf);
    };

    xmlFree(templates);

    return ret;
}

/************************************************************************
 *                                                                      *
 *              Hooks for libxml2 XPath                                 *
 *                                                                      *
 ************************************************************************/

/**
 * 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) {
    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;
    }
    /*
    * 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);
}

/************************************************************************
 *                                                                      *
 *              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();
}


/* [<][>][^][v][top][bottom][index][help] */