root/third_party/libxslt/libxslt/transform.c

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

DEFINITIONS

This source file includes following definitions.
  1. templPush
  2. templPop
  3. xsltLocalVariablePop
  4. xsltTemplateParamsCleanup
  5. profPush
  6. profPop
  7. xsltSetXIncludeDefault
  8. xsltGetXIncludeDefault
  9. xsltDebugSetDefaultTrace
  10. xsltDebugGetDefaultTrace
  11. xsltTransformCacheCreate
  12. xsltTransformCacheFree
  13. xsltNewTransformContext
  14. xsltFreeTransformContext
  15. xsltAddChild
  16. xsltAddTextString
  17. xsltCopyTextString
  18. xsltCopyText
  19. xsltShallowCopyAttr
  20. xsltCopyAttrListNoOverwrite
  21. xsltShallowCopyElem
  22. xsltCopyTreeList
  23. xsltCopyNamespaceListInternal
  24. xsltShallowCopyNsNode
  25. xsltCopyTreeInternal
  26. xsltCopyTree
  27. xsltApplyFallbacks
  28. xsltDefaultProcessOneNode
  29. xsltProcessOneNode
  30. xsltDebuggerStartSequenceConstructor
  31. xsltLocalVariablePush
  32. xsltReleaseLocalRVTs
  33. xsltApplySequenceConstructor
  34. xsltApplyXSLTTemplate
  35. xsltApplyOneTemplate
  36. xsltDocumentElem
  37. xsltSort
  38. xsltCopy
  39. xsltText
  40. xsltElement
  41. xsltComment
  42. xsltProcessingInstruction
  43. xsltCopyOf
  44. xsltValueOf
  45. xsltNumber
  46. xsltApplyImports
  47. xsltCallTemplate
  48. xsltApplyTemplates
  49. xsltChoose
  50. xsltIf
  51. xsltForEach
  52. xsltGetHTMLIDs
  53. xsltApplyStripSpaces
  54. xsltCountKeys
  55. xsltApplyStylesheetInternal
  56. xsltApplyStylesheet
  57. xsltProfileStylesheet
  58. xsltApplyStylesheetUser
  59. xsltRunStylesheetUser
  60. xsltRunStylesheet
  61. xsltRegisterAllElement

/*
 * transform.c: Implementation of the XSL Transformation 1.0 engine
 *              transform part, i.e. applying a Stylesheet to a document
 *
 * References:
 *   http://www.w3.org/TR/1999/REC-xslt-19991116
 *
 *   Michael Kay "XSLT Programmer's Reference" pp 637-643
 *   Writing Multiple Output Files
 *
 *   XSLT-1.1 Working Draft
 *   http://www.w3.org/TR/xslt11#multiple-output
 *
 * See Copyright for the status of this software.
 *
 * daniel@veillard.com
 */

#define IN_LIBXSLT
#include "libxslt.h"

#include <string.h>

#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/valid.h>
#include <libxml/hash.h>
#include <libxml/encoding.h>
#include <libxml/xmlerror.h>
#include <libxml/xpath.h>
#include <libxml/parserInternals.h>
#include <libxml/xpathInternals.h>
#include <libxml/HTMLtree.h>
#include <libxml/debugXML.h>
#include <libxml/uri.h>
#include "xslt.h"
#include "xsltInternals.h"
#include "xsltutils.h"
#include "pattern.h"
#include "transform.h"
#include "variables.h"
#include "numbersInternals.h"
#include "namespaces.h"
#include "attributes.h"
#include "templates.h"
#include "imports.h"
#include "keys.h"
#include "documents.h"
#include "extensions.h"
#include "extra.h"
#include "preproc.h"
#include "security.h"

#ifdef WITH_XSLT_DEBUG
#define WITH_XSLT_DEBUG_EXTRA
#define WITH_XSLT_DEBUG_PROCESS
#endif

#define XSLT_GENERATE_HTML_DOCTYPE
#ifdef XSLT_GENERATE_HTML_DOCTYPE
static int xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID,
                          const xmlChar **systemID);
#endif

int xsltMaxDepth = 3000;

/*
 * Useful macros
 */

#ifndef FALSE
# define FALSE (0 == 1)
# define TRUE (!FALSE)
#endif

#define IS_BLANK_NODE(n)                                                \
    (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))


/*
* Forward declarations
*/

static xmlNsPtr
xsltCopyNamespaceListInternal(xmlNodePtr node, xmlNsPtr cur);

static xmlNodePtr
xsltCopyTreeInternal(xsltTransformContextPtr ctxt,
                     xmlNodePtr invocNode,
                     xmlNodePtr node,
                     xmlNodePtr insert, int isLRE, int topElemVisited);

static void
xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
                             xmlNodePtr contextNode, xmlNodePtr list,
                             xsltTemplatePtr templ);

static void
xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt,
                      xmlNodePtr contextNode,
                      xmlNodePtr list,
                      xsltTemplatePtr templ,
                      xsltStackElemPtr withParams);

/**
 * templPush:
 * @ctxt: the transformation context
 * @value:  the template to push on the stack
 *
 * Push a template on the stack
 *
 * Returns the new index in the stack or 0 in case of error
 */
static int
templPush(xsltTransformContextPtr ctxt, xsltTemplatePtr value)
{
    if (ctxt->templMax == 0) {
        ctxt->templMax = 4;
        ctxt->templTab =
            (xsltTemplatePtr *) xmlMalloc(ctxt->templMax *
                                          sizeof(ctxt->templTab[0]));
        if (ctxt->templTab == NULL) {
            xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
            return (0);
        }
    }
    if (ctxt->templNr >= ctxt->templMax) {
        ctxt->templMax *= 2;
        ctxt->templTab =
            (xsltTemplatePtr *) xmlRealloc(ctxt->templTab,
                                           ctxt->templMax *
                                           sizeof(ctxt->templTab[0]));
        if (ctxt->templTab == NULL) {
            xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
            return (0);
        }
    }
    ctxt->templTab[ctxt->templNr] = value;
    ctxt->templ = value;
    return (ctxt->templNr++);
}
/**
 * templPop:
 * @ctxt: the transformation context
 *
 * Pop a template value from the stack
 *
 * Returns the stored template value
 */
static xsltTemplatePtr
templPop(xsltTransformContextPtr ctxt)
{
    xsltTemplatePtr ret;

    if (ctxt->templNr <= 0)
        return (0);
    ctxt->templNr--;
    if (ctxt->templNr > 0)
        ctxt->templ = ctxt->templTab[ctxt->templNr - 1];
    else
        ctxt->templ = (xsltTemplatePtr) 0;
    ret = ctxt->templTab[ctxt->templNr];
    ctxt->templTab[ctxt->templNr] = 0;
    return (ret);
}

/**
 * xsltLocalVariablePop:
 * @ctxt: the transformation context
 * @limitNr: number of variables which should remain
 * @level: the depth in the xsl:template's tree
 *
 * Pops all variable values at the given @depth from the stack.
 *
 * Returns the stored variable value
 * **NOTE:**
 * This is an internal routine and should not be called by users!
 */
void
xsltLocalVariablePop(xsltTransformContextPtr ctxt, int limitNr, int level)
{
    xsltStackElemPtr variable;

    if (ctxt->varsNr <= 0)
        return;

    do {
        if (ctxt->varsNr <= limitNr)
            break;
        variable = ctxt->varsTab[ctxt->varsNr - 1];
        if (variable->level <= level)
            break;
        if (variable->level >= 0)
            xsltFreeStackElemList(variable);
        ctxt->varsNr--;
    } while (ctxt->varsNr != 0);
    if (ctxt->varsNr > 0)
        ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1];
    else
        ctxt->vars = NULL;
}

/**
 * xsltTemplateParamsCleanup:
 *
 * Removes xsl:param and xsl:with-param items from the
 * variable-stack. Only xsl:with-param items are not freed.
 */
static void
xsltTemplateParamsCleanup(xsltTransformContextPtr ctxt)
{
    xsltStackElemPtr param;

    for (; ctxt->varsNr > ctxt->varsBase; ctxt->varsNr--) {
        param = ctxt->varsTab[ctxt->varsNr -1];
        /*
        * Free xsl:param items.
        * xsl:with-param items will have a level of -1 or -2.
        */
        if (param->level >= 0) {
            xsltFreeStackElemList(param);
        }
    }
    if (ctxt->varsNr > 0)
        ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1];
    else
        ctxt->vars = NULL;
}

/**
 * profPush:
 * @ctxt: the transformation context
 * @value:  the profiling value to push on the stack
 *
 * Push a profiling value on the stack
 *
 * Returns the new index in the stack or 0 in case of error
 */
static int
profPush(xsltTransformContextPtr ctxt, long value)
{
    if (ctxt->profMax == 0) {
        ctxt->profMax = 4;
        ctxt->profTab =
            (long *) xmlMalloc(ctxt->profMax * sizeof(ctxt->profTab[0]));
        if (ctxt->profTab == NULL) {
            xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
            return (0);
        }
    }
    if (ctxt->profNr >= ctxt->profMax) {
        ctxt->profMax *= 2;
        ctxt->profTab =
            (long *) xmlRealloc(ctxt->profTab,
                                ctxt->profMax * sizeof(ctxt->profTab[0]));
        if (ctxt->profTab == NULL) {
            xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
            return (0);
        }
    }
    ctxt->profTab[ctxt->profNr] = value;
    ctxt->prof = value;
    return (ctxt->profNr++);
}
/**
 * profPop:
 * @ctxt: the transformation context
 *
 * Pop a profiling value from the stack
 *
 * Returns the stored profiling value
 */
static long
profPop(xsltTransformContextPtr ctxt)
{
    long ret;

    if (ctxt->profNr <= 0)
        return (0);
    ctxt->profNr--;
    if (ctxt->profNr > 0)
        ctxt->prof = ctxt->profTab[ctxt->profNr - 1];
    else
        ctxt->prof = (long) 0;
    ret = ctxt->profTab[ctxt->profNr];
    ctxt->profTab[ctxt->profNr] = 0;
    return (ret);
}

/************************************************************************
 *                                                                      *
 *                      XInclude default settings                       *
 *                                                                      *
 ************************************************************************/

static int xsltDoXIncludeDefault = 0;

/**
 * xsltSetXIncludeDefault:
 * @xinclude: whether to do XInclude processing
 *
 * Set whether XInclude should be processed on document being loaded by default
 */
void
xsltSetXIncludeDefault(int xinclude) {
    xsltDoXIncludeDefault = (xinclude != 0);
}

/**
 * xsltGetXIncludeDefault:
 *
 * Provides the default state for XInclude processing
 *
 * Returns 0 if there is no processing 1 otherwise
 */
int
xsltGetXIncludeDefault(void) {
    return(xsltDoXIncludeDefault);
}

unsigned long xsltDefaultTrace = (unsigned long) XSLT_TRACE_ALL;

/**
 * xsltDebugSetDefaultTrace:
 * @val: tracing level mask
 *
 * Set the default debug tracing level mask
 */
void xsltDebugSetDefaultTrace(xsltDebugTraceCodes val) {
        xsltDefaultTrace = val;
}

/**
 * xsltDebugGetDefaultTrace:
 *
 * Get the current default debug tracing level mask
 *
 * Returns the current default debug tracing level mask
 */
xsltDebugTraceCodes xsltDebugGetDefaultTrace() {
        return xsltDefaultTrace;
}

/************************************************************************
 *                                                                      *
 *                      Handling of Transformation Contexts             *
 *                                                                      *
 ************************************************************************/

static xsltTransformCachePtr
xsltTransformCacheCreate(void)
{
    xsltTransformCachePtr ret;

    ret = (xsltTransformCachePtr) xmlMalloc(sizeof(xsltTransformCache));
    if (ret == NULL) {
        xsltTransformError(NULL, NULL, NULL,
            "xsltTransformCacheCreate : malloc failed\n");
        return(NULL);
    }
    memset(ret, 0, sizeof(xsltTransformCache));
    return(ret);
}

static void
xsltTransformCacheFree(xsltTransformCachePtr cache)
{
    if (cache == NULL)
        return;
    /*
    * Free tree fragments.
    */
    if (cache->RVT) {
        xmlDocPtr tmp, cur = cache->RVT;
        while (cur) {
            tmp = cur;
            cur = (xmlDocPtr) cur->next;
            if (tmp->_private != NULL) {
                /*
                * Tree the document info.
                */
                xsltFreeDocumentKeys((xsltDocumentPtr) tmp->_private);
                xmlFree(tmp->_private);
            }
            xmlFreeDoc(tmp);
        }
    }
    /*
    * Free vars/params.
    */
    if (cache->stackItems) {
        xsltStackElemPtr tmp, cur = cache->stackItems;
        while (cur) {
            tmp = cur;
            cur = cur->next;
            /*
            * REVISIT TODO: Should be call a destruction-function
            * instead?
            */
            xmlFree(tmp);
        }
    }
    xmlFree(cache);
}

/**
 * xsltNewTransformContext:
 * @style:  a parsed XSLT stylesheet
 * @doc:  the input document
 *
 * Create a new XSLT TransformContext
 *
 * Returns the newly allocated xsltTransformContextPtr or NULL in case of error
 */
xsltTransformContextPtr
xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) {
    xsltTransformContextPtr cur;
    xsltDocumentPtr docu;
    int i;

    xsltInitGlobals();

    cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext));
    if (cur == NULL) {
        xsltTransformError(NULL, NULL, (xmlNodePtr)doc,
                "xsltNewTransformContext : malloc failed\n");
        return(NULL);
    }
    memset(cur, 0, sizeof(xsltTransformContext));

    cur->cache = xsltTransformCacheCreate();
    if (cur->cache == NULL)
        goto internal_err;
    /*
     * setup of the dictionary must be done early as some of the
     * processing later like key handling may need it.
     */
    cur->dict = xmlDictCreateSub(style->dict);
    cur->internalized = ((style->internalized) && (cur->dict != NULL));
#ifdef WITH_XSLT_DEBUG
    xsltGenericDebug(xsltGenericDebugContext,
             "Creating sub-dictionary from stylesheet for transformation\n");
#endif

    /*
     * initialize the template stack
     */
    cur->templTab = (xsltTemplatePtr *)
                xmlMalloc(10 * sizeof(xsltTemplatePtr));
    if (cur->templTab == NULL) {
        xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
                "xsltNewTransformContext: out of memory\n");
        goto internal_err;
    }
    cur->templNr = 0;
    cur->templMax = 5;
    cur->templ = NULL;

    /*
     * initialize the variables stack
     */
    cur->varsTab = (xsltStackElemPtr *)
                xmlMalloc(10 * sizeof(xsltStackElemPtr));
    if (cur->varsTab == NULL) {
        xmlGenericError(xmlGenericErrorContext,
                "xsltNewTransformContext: out of memory\n");
        goto internal_err;
    }
    cur->varsNr = 0;
    cur->varsMax = 10;
    cur->vars = NULL;
    cur->varsBase = 0;

    /*
     * the profiling stack is not initialized by default
     */
    cur->profTab = NULL;
    cur->profNr = 0;
    cur->profMax = 0;
    cur->prof = 0;

    cur->style = style;
    xmlXPathInit();
    cur->xpathCtxt = xmlXPathNewContext(doc);
    if (cur->xpathCtxt == NULL) {
        xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
                "xsltNewTransformContext : xmlXPathNewContext failed\n");
        goto internal_err;
    }
    /*
    * Create an XPath cache.
    */
    if (xmlXPathContextSetCache(cur->xpathCtxt, 1, -1, 0) == -1)
        goto internal_err;
    /*
     * Initialize the extras array
     */
    if (style->extrasNr != 0) {
        cur->extrasMax = style->extrasNr + 20;
        cur->extras = (xsltRuntimeExtraPtr)
            xmlMalloc(cur->extrasMax * sizeof(xsltRuntimeExtra));
        if (cur->extras == NULL) {
            xmlGenericError(xmlGenericErrorContext,
                    "xsltNewTransformContext: out of memory\n");
            goto internal_err;
        }
        cur->extrasNr = style->extrasNr;
        for (i = 0;i < cur->extrasMax;i++) {
            cur->extras[i].info = NULL;
            cur->extras[i].deallocate = NULL;
            cur->extras[i].val.ptr = NULL;
        }
    } else {
        cur->extras = NULL;
        cur->extrasNr = 0;
        cur->extrasMax = 0;
    }

    XSLT_REGISTER_VARIABLE_LOOKUP(cur);
    XSLT_REGISTER_FUNCTION_LOOKUP(cur);
    cur->xpathCtxt->nsHash = style->nsHash;
    /*
     * Initialize the registered external modules
     */
    xsltInitCtxtExts(cur);
    /*
     * Setup document element ordering for later efficiencies
     * (bug 133289)
     */
    if (xslDebugStatus == XSLT_DEBUG_NONE)
        xmlXPathOrderDocElems(doc);
    /*
     * Must set parserOptions before calling xsltNewDocument
     * (bug 164530)
     */
    cur->parserOptions = XSLT_PARSE_OPTIONS;
    docu = xsltNewDocument(cur, doc);
    if (docu == NULL) {
        xsltTransformError(cur, NULL, (xmlNodePtr)doc,
                "xsltNewTransformContext : xsltNewDocument failed\n");
        goto internal_err;
    }
    docu->main = 1;
    cur->document = docu;
    cur->inst = NULL;
    cur->outputFile = NULL;
    cur->sec = xsltGetDefaultSecurityPrefs();
    cur->debugStatus = xslDebugStatus;
    cur->traceCode = (unsigned long*) &xsltDefaultTrace;
    cur->xinclude = xsltGetXIncludeDefault();
    cur->keyInitLevel = 0;

    return(cur);

internal_err:
    if (cur != NULL)
        xsltFreeTransformContext(cur);
    return(NULL);
}

/**
 * xsltFreeTransformContext:
 * @ctxt:  an XSLT parser context
 *
 * Free up the memory allocated by @ctxt
 */
void
xsltFreeTransformContext(xsltTransformContextPtr ctxt) {
    if (ctxt == NULL)
        return;

    /*
     * Shutdown the extension modules associated to the stylesheet
     * used if needed.
     */
    xsltShutdownCtxtExts(ctxt);

    if (ctxt->xpathCtxt != NULL) {
        ctxt->xpathCtxt->nsHash = NULL;
        xmlXPathFreeContext(ctxt->xpathCtxt);
    }
    if (ctxt->templTab != NULL)
        xmlFree(ctxt->templTab);
    if (ctxt->varsTab != NULL)
        xmlFree(ctxt->varsTab);
    if (ctxt->profTab != NULL)
        xmlFree(ctxt->profTab);
    if ((ctxt->extrasNr > 0) && (ctxt->extras != NULL)) {
        int i;

        for (i = 0;i < ctxt->extrasNr;i++) {
            if ((ctxt->extras[i].deallocate != NULL) &&
                (ctxt->extras[i].info != NULL))
                ctxt->extras[i].deallocate(ctxt->extras[i].info);
        }
        xmlFree(ctxt->extras);
    }
    xsltFreeGlobalVariables(ctxt);
    xsltFreeDocuments(ctxt);
    xsltFreeCtxtExts(ctxt);
    xsltFreeRVTs(ctxt);
    xsltTransformCacheFree(ctxt->cache);
    xmlDictFree(ctxt->dict);
#ifdef WITH_XSLT_DEBUG
    xsltGenericDebug(xsltGenericDebugContext,
                     "freeing transformation dictionary\n");
#endif
    memset(ctxt, -1, sizeof(xsltTransformContext));
    xmlFree(ctxt);
}

/************************************************************************
 *                                                                      *
 *                      Copy of Nodes in an XSLT fashion                *
 *                                                                      *
 ************************************************************************/

xmlNodePtr xsltCopyTree(xsltTransformContextPtr ctxt,
                        xmlNodePtr node, xmlNodePtr insert, int literal);

/**
 * xsltAddChild:
 * @parent:  the parent node
 * @cur:  the child node
 *
 * Wrapper version of xmlAddChild with a more consistent behaviour on
 * error. One expect the use to be child = xsltAddChild(parent, child);
 * and the routine will take care of not leaking on errors or node merge
 *
 * Returns the child is successfully attached or NULL if merged or freed
 */
static xmlNodePtr
xsltAddChild(xmlNodePtr parent, xmlNodePtr cur) {
   xmlNodePtr ret;

   if ((cur == NULL) || (parent == NULL))
       return(NULL);
   if (parent == NULL) {
       xmlFreeNode(cur);
       return(NULL);
   }
   ret = xmlAddChild(parent, cur);

   return(ret);
}

/**
 * xsltAddTextString:
 * @ctxt:  a XSLT process context
 * @target:  the text node where the text will be attached
 * @string:  the text string
 * @len:  the string length in byte
 *
 * Extend the current text node with the new string, it handles coalescing
 *
 * Returns: the text node
 */
static xmlNodePtr
xsltAddTextString(xsltTransformContextPtr ctxt, xmlNodePtr target,
                  const xmlChar *string, int len) {
    /*
     * optimization
     */
    if ((len <= 0) || (string == NULL) || (target == NULL))
        return(target);

    if (ctxt->lasttext == target->content) {

        if (ctxt->lasttuse + len >= ctxt->lasttsize) {
            xmlChar *newbuf;
            int size;

            size = ctxt->lasttsize + len + 100;
            size *= 2;
            newbuf = (xmlChar *) xmlRealloc(target->content,size);
            if (newbuf == NULL) {
                xsltTransformError(ctxt, NULL, target,
                 "xsltCopyText: text allocation failed\n");
                return(NULL);
            }
            ctxt->lasttsize = size;
            ctxt->lasttext = newbuf;
            target->content = newbuf;
        }
        memcpy(&(target->content[ctxt->lasttuse]), string, len);
        ctxt->lasttuse += len;
        target->content[ctxt->lasttuse] = 0;
    } else {
        xmlNodeAddContent(target, string);
        ctxt->lasttext = target->content;
        len = xmlStrlen(target->content);
        ctxt->lasttsize = len;
        ctxt->lasttuse = len;
    }
    return(target);
}

/**
 * xsltCopyTextString:
 * @ctxt:  a XSLT process context
 * @target:  the element where the text will be attached
 * @string:  the text string
 * @noescape:  should disable-escaping be activated for this text node.
 *
 * Adds @string to a newly created or an existent text node child of
 * @target.
 *
 * Returns: the text node, where the text content of @cur is copied to.
 *          NULL in case of API or internal errors.
 */
xmlNodePtr
xsltCopyTextString(xsltTransformContextPtr ctxt, xmlNodePtr target,
                   const xmlChar *string, int noescape)
{
    xmlNodePtr copy;
    int len;

    if (string == NULL)
        return(NULL);

#ifdef WITH_XSLT_DEBUG_PROCESS
    XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
                     "xsltCopyTextString: copy text %s\n",
                     string));
#endif

    /*
    * Play save and reset the merging mechanism for every new
    * target node.
    */
    if ((target == NULL) || (target->children == NULL)) {
        ctxt->lasttext = NULL;
    }

    /* handle coalescing of text nodes here */
    len = xmlStrlen(string);
    if ((ctxt->type == XSLT_OUTPUT_XML) &&
        (ctxt->style->cdataSection != NULL) &&
        (target != NULL) &&
        (target->type == XML_ELEMENT_NODE) &&
        (((target->ns == NULL) &&
          (xmlHashLookup2(ctxt->style->cdataSection,
                          target->name, NULL) != NULL)) ||
         ((target->ns != NULL) &&
          (xmlHashLookup2(ctxt->style->cdataSection,
                          target->name, target->ns->href) != NULL))))
    {
        /*
        * Process "cdata-section-elements".
        */
        if ((target->last != NULL) &&
            (target->last->type == XML_CDATA_SECTION_NODE))
        {
            return(xsltAddTextString(ctxt, target->last, string, len));
        }
        copy = xmlNewCDataBlock(ctxt->output, string, len);
    } else if (noescape) {
        /*
        * Process "disable-output-escaping".
        */
        if ((target != NULL) && (target->last != NULL) &&
            (target->last->type == XML_TEXT_NODE) &&
            (target->last->name == xmlStringTextNoenc))
        {
            return(xsltAddTextString(ctxt, target->last, string, len));
        }
        copy = xmlNewTextLen(string, len);
        if (copy != NULL)
            copy->name = xmlStringTextNoenc;
    } else {
        /*
        * Default processing.
        */
        if ((target != NULL) && (target->last != NULL) &&
            (target->last->type == XML_TEXT_NODE) &&
            (target->last->name == xmlStringText)) {
            return(xsltAddTextString(ctxt, target->last, string, len));
        }
        copy = xmlNewTextLen(string, len);
    }
    if (copy != NULL) {
        if (target != NULL)
            copy = xsltAddChild(target, copy);
        ctxt->lasttext = copy->content;
        ctxt->lasttsize = len;
        ctxt->lasttuse = len;
    } else {
        xsltTransformError(ctxt, NULL, target,
                         "xsltCopyTextString: text copy failed\n");
        ctxt->lasttext = NULL;
    }
    return(copy);
}

/**
 * xsltCopyText:
 * @ctxt:  a XSLT process context
 * @target:  the element where the text will be attached
 * @cur:  the text or CDATA node
 * @interned:  the string is in the target doc dictionary
 *
 * Copy the text content of @cur and append it to @target's children.
 *
 * Returns: the text node, where the text content of @cur is copied to.
 *          NULL in case of API or internal errors.
 */
static xmlNodePtr
xsltCopyText(xsltTransformContextPtr ctxt, xmlNodePtr target,
             xmlNodePtr cur, int interned)
{
    xmlNodePtr copy;

    if ((cur->type != XML_TEXT_NODE) &&
        (cur->type != XML_CDATA_SECTION_NODE))
        return(NULL);
    if (cur->content == NULL)
        return(NULL);

#ifdef WITH_XSLT_DEBUG_PROCESS
    if (cur->type == XML_CDATA_SECTION_NODE) {
        XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
                         "xsltCopyText: copy CDATA text %s\n",
                         cur->content));
    } else if (cur->name == xmlStringTextNoenc) {
        XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
                     "xsltCopyText: copy unescaped text %s\n",
                         cur->content));
    } else {
        XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
                         "xsltCopyText: copy text %s\n",
                         cur->content));
    }
#endif

    /*
    * Play save and reset the merging mechanism for every new
    * target node.
    */
    if ((target == NULL) || (target->children == NULL)) {
        ctxt->lasttext = NULL;
    }

    if ((ctxt->style->cdataSection != NULL) &&
        (ctxt->type == XSLT_OUTPUT_XML) &&
        (target != NULL) &&
        (target->type == XML_ELEMENT_NODE) &&
        (((target->ns == NULL) &&
          (xmlHashLookup2(ctxt->style->cdataSection,
                          target->name, NULL) != NULL)) ||
         ((target->ns != NULL) &&
          (xmlHashLookup2(ctxt->style->cdataSection,
                          target->name, target->ns->href) != NULL))))
    {
        /*
        * Process "cdata-section-elements".
        */
        /*
        * OPTIMIZE TODO: xsltCopyText() is also used for attribute content.
        */
        /*
        * TODO: Since this doesn't merge adjacent CDATA-section nodes,
        * we'll get: <![CDATA[x]]><!CDATA[y]]>.
        * TODO: Reported in #321505.
        */
        if ((target->last != NULL) &&
             (target->last->type == XML_CDATA_SECTION_NODE))
        {
            /*
            * Append to existing CDATA-section node.
            */
            copy = xsltAddTextString(ctxt, target->last, cur->content,
                xmlStrlen(cur->content));
            goto exit;
        } else {
            unsigned int len;

            len = xmlStrlen(cur->content);
            copy = xmlNewCDataBlock(ctxt->output, cur->content, len);
            if (copy == NULL)
                goto exit;
            ctxt->lasttext = copy->content;
            ctxt->lasttsize = len;
            ctxt->lasttuse = len;
        }
    } else if ((target != NULL) &&
        (target->last != NULL) &&
        /* both escaped or both non-escaped text-nodes */
        (((target->last->type == XML_TEXT_NODE) &&
        (target->last->name == cur->name)) ||
        /* non-escaped text nodes and CDATA-section nodes */
        (((target->last->type == XML_CDATA_SECTION_NODE) &&
        (cur->name == xmlStringTextNoenc)))))
    {
        /*
         * we are appending to an existing text node
         */
        copy = xsltAddTextString(ctxt, target->last, cur->content,
            xmlStrlen(cur->content));
        goto exit;
    } else if ((interned) && (target != NULL) &&
        (target->doc != NULL) &&
        (target->doc->dict == ctxt->dict))
    {
        /*
        * TODO: DO we want to use this also for "text" output?
        */
        copy = xmlNewTextLen(NULL, 0);
        if (copy == NULL)
            goto exit;
        if (cur->name == xmlStringTextNoenc)
            copy->name = xmlStringTextNoenc;

        /*
         * Must confirm that content is in dict (bug 302821)
         * TODO: This check should be not needed for text coming
         * from the stylesheets
         */
        if (xmlDictOwns(ctxt->dict, cur->content))
            copy->content = cur->content;
        else {
            if ((copy->content = xmlStrdup(cur->content)) == NULL)
                return NULL;
        }
    } else {
        /*
         * normal processing. keep counters to extend the text node
         * in xsltAddTextString if needed.
         */
        unsigned int len;

        len = xmlStrlen(cur->content);
        copy = xmlNewTextLen(cur->content, len);
        if (copy == NULL)
            goto exit;
        if (cur->name == xmlStringTextNoenc)
            copy->name = xmlStringTextNoenc;
        ctxt->lasttext = copy->content;
        ctxt->lasttsize = len;
        ctxt->lasttuse = len;
    }
    if (copy != NULL) {
        if (target != NULL) {
            copy->doc = target->doc;
            /*
            * MAYBE TODO: Maybe we should reset the ctxt->lasttext here
            *  to ensure that the optimized text-merging mechanism
            *  won't interfere with normal node-merging in any case.
            */
            copy = xsltAddChild(target, copy);
        }
    } else {
        xsltTransformError(ctxt, NULL, target,
                         "xsltCopyText: text copy failed\n");
    }

exit:
    if ((copy == NULL) || (copy->content == NULL)) {
        xsltTransformError(ctxt, NULL, target,
            "Internal error in xsltCopyText(): "
            "Failed to copy the string.\n");
        ctxt->state = XSLT_STATE_STOPPED;
    }
    return(copy);
}

/**
 * xsltShallowCopyAttr:
 * @ctxt:  a XSLT process context
 * @invocNode: responsible node in the stylesheet; used for error reports
 * @target:  the element where the attribute will be grafted
 * @attr: the attribute to be copied
 *
 * Do a copy of an attribute.
 * Called by:
 *  - xsltCopyTreeInternal()
 *  - xsltCopyOf()
 *  - xsltCopy()
 *
 * Returns: a new xmlAttrPtr, or NULL in case of error.
 */
static xmlAttrPtr
xsltShallowCopyAttr(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
             xmlNodePtr target, xmlAttrPtr attr)
{
    xmlAttrPtr copy;
    xmlChar *value;

    if (attr == NULL)
        return(NULL);

    if (target->type != XML_ELEMENT_NODE) {
        xsltTransformError(ctxt, NULL, invocNode,
            "Cannot add an attribute node to a non-element node.\n");
        return(NULL);
    }

    if (target->children != NULL) {
        xsltTransformError(ctxt, NULL, invocNode,
            "Attribute nodes must be added before "
            "any child nodes to an element.\n");
        return(NULL);
    }

    value = xmlNodeListGetString(attr->doc, attr->children, 1);
    if (attr->ns != NULL) {
        xmlNsPtr ns;

        ns = xsltGetSpecialNamespace(ctxt, invocNode,
            attr->ns->href, attr->ns->prefix, target);
        if (ns == NULL) {
            xsltTransformError(ctxt, NULL, invocNode,
                "Namespace fixup error: Failed to acquire an in-scope "
                "namespace binding of the copied attribute '{%s}%s'.\n",
                attr->ns->href, attr->name);
            /*
            * TODO: Should we just stop here?
            */
        }
        /*
        * Note that xmlSetNsProp() will take care of duplicates
        * and assigns the new namespace even to a duplicate.
        */
        copy = xmlSetNsProp(target, ns, attr->name, value);
    } else {
        copy = xmlSetNsProp(target, NULL, attr->name, value);
    }
    if (value != NULL)
        xmlFree(value);

    if (copy == NULL)
        return(NULL);

#if 0
    /*
    * NOTE: This was optimized according to bug #342695.
    * TODO: Can this further be optimized, if source and target
    *  share the same dict and attr->children is just 1 text node
    *  which is in the dict? How probable is such a case?
    */
    /*
    * TODO: Do we need to create an empty text node if the value
    *  is the empty string?
    */
    value = xmlNodeListGetString(attr->doc, attr->children, 1);
    if (value != NULL) {
        txtNode = xmlNewDocText(target->doc, NULL);
        if (txtNode == NULL)
            return(NULL);
        if ((target->doc != NULL) &&
            (target->doc->dict != NULL))
        {
            txtNode->content =
                (xmlChar *) xmlDictLookup(target->doc->dict,
                    BAD_CAST value, -1);
            xmlFree(value);
        } else
            txtNode->content = value;
        copy->children = txtNode;
    }
#endif

    return(copy);
}

/**
 * xsltCopyAttrListNoOverwrite:
 * @ctxt:  a XSLT process context
 * @invocNode: responsible node in the stylesheet; used for error reports
 * @target:  the element where the new attributes will be grafted
 * @attr:  the first attribute in the list to be copied
 *
 * Copies a list of attribute nodes, starting with @attr, over to the
 * @target element node.
 *
 * Called by:
 *  - xsltCopyTreeInternal()
 *
 * Returns 0 on success and -1 on errors and internal errors.
 */
static int
xsltCopyAttrListNoOverwrite(xsltTransformContextPtr ctxt,
                            xmlNodePtr invocNode,
                            xmlNodePtr target, xmlAttrPtr attr)
{
    xmlAttrPtr copy;
    xmlNsPtr origNs = NULL, copyNs = NULL;
    xmlChar *value;

    /*
    * Don't use xmlCopyProp() here, since it will try to
    * reconciliate namespaces.
    */
    while (attr != NULL) {
        /*
        * Find a namespace node in the tree of @target.
        * Avoid searching for the same ns.
        */
        if (attr->ns != origNs) {
            origNs = attr->ns;
            if (attr->ns != NULL) {
                copyNs = xsltGetSpecialNamespace(ctxt, invocNode,
                    attr->ns->href, attr->ns->prefix, target);
                if (copyNs == NULL)
                    return(-1);
            } else
                copyNs = NULL;
        }
        /*
         * If attribute has a value, we need to copy it (watching out
         * for possible entities)
         */
        if ((attr->children) && (attr->children->type == XML_TEXT_NODE) &&
            (attr->children->next == NULL)) {
            copy = xmlNewNsProp(target, copyNs, attr->name,
                                attr->children->content);
        } else if (attr->children != NULL) {
            value = xmlNodeListGetString(attr->doc, attr->children, 1);
            copy = xmlNewNsProp(target, copyNs, attr->name, BAD_CAST value);
            xmlFree(value);
        } else {
            copy = xmlNewNsProp(target, copyNs, attr->name, NULL);
        }

        if (copy == NULL)
            return(-1);

        attr = attr->next;
    }
    return(0);
}

/**
 * xsltShallowCopyElem:
 * @ctxt:  the XSLT process context
 * @node:  the element node in the source tree
 *         or the Literal Result Element
 * @insert:  the parent in the result tree
 * @isLRE: if @node is a Literal Result Element
 *
 * Make a copy of the element node @node
 * and insert it as last child of @insert.
 *
 * URGENT TODO: The problem with this one (for the non-refactored code)
 * is that it is used for both, Literal Result Elements *and*
 * copying input nodes.
 *
 * BIG NOTE: This is only called for XML_ELEMENT_NODEs.
 *
 * Called from:
 *   xsltApplySequenceConstructor()
 *    (for Literal Result Elements - which is a problem)
 *   xsltCopy() (for shallow-copying elements via xsl:copy)
 *
 * Returns a pointer to the new node, or NULL in case of error
 */
static xmlNodePtr
xsltShallowCopyElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
                    xmlNodePtr insert, int isLRE)
{
    xmlNodePtr copy;

    if ((node->type == XML_DTD_NODE) || (insert == NULL))
        return(NULL);
    if ((node->type == XML_TEXT_NODE) ||
        (node->type == XML_CDATA_SECTION_NODE))
        return(xsltCopyText(ctxt, insert, node, 0));

    copy = xmlDocCopyNode(node, insert->doc, 0);
    if (copy != NULL) {
        copy->doc = ctxt->output;
        copy = xsltAddChild(insert, copy);

        if (node->type == XML_ELEMENT_NODE) {
            /*
             * Add namespaces as they are needed
             */
            if (node->nsDef != NULL) {
                /*
                * TODO: Remove the LRE case in the refactored code
                * gets enabled.
                */
                if (isLRE)
                    xsltCopyNamespaceList(ctxt, copy, node->nsDef);
                else
                    xsltCopyNamespaceListInternal(copy, node->nsDef);
            }

            /*
            * URGENT TODO: The problem with this is that it does not
            *  copy over all namespace nodes in scope.
            *  The damn thing about this is, that we would need to
            *  use the xmlGetNsList(), for every single node; this is
            *  also done in xsltCopyTreeInternal(), but only for the top node.
            */
            if (node->ns != NULL) {
                if (isLRE) {
                    /*
                    * REVISIT TODO: Since the non-refactored code still does
                    *  ns-aliasing, we need to call xsltGetNamespace() here.
                    *  Remove this when ready.
                    */
                    copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy);
                } else {
                    copy->ns = xsltGetSpecialNamespace(ctxt,
                        node, node->ns->href, node->ns->prefix, copy);

                }
            } else if ((insert->type == XML_ELEMENT_NODE) &&
                       (insert->ns != NULL))
            {
                /*
                * "Undeclare" the default namespace.
                */
                xsltGetSpecialNamespace(ctxt, node, NULL, NULL, copy);
            }
        }
    } else {
        xsltTransformError(ctxt, NULL, node,
                "xsltShallowCopyElem: copy %s failed\n", node->name);
    }
    return(copy);
}

/**
 * xsltCopyTreeList:
 * @ctxt:  a XSLT process context
 * @invocNode: responsible node in the stylesheet; used for error reports
 * @list:  the list of element nodes in the source tree.
 * @insert:  the parent in the result tree.
 * @isLRE:  is this a literal result element list
 * @topElemVisited: indicates if a top-most element was already processed
 *
 * Make a copy of the full list of tree @list
 * and insert it as last children of @insert
 *
 * NOTE: Not to be used for Literal Result Elements.
 *
 * Used by:
 *  - xsltCopyOf()
 *
 * Returns a pointer to the new list, or NULL in case of error
 */
static xmlNodePtr
xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
                 xmlNodePtr list,
                 xmlNodePtr insert, int isLRE, int topElemVisited)
{
    xmlNodePtr copy, ret = NULL;

    while (list != NULL) {
        copy = xsltCopyTreeInternal(ctxt, invocNode,
            list, insert, isLRE, topElemVisited);
        if (copy != NULL) {
            if (ret == NULL) {
                ret = copy;
            }
        }
        list = list->next;
    }
    return(ret);
}

/**
 * xsltCopyNamespaceListInternal:
 * @node:  the target node
 * @cur:  the first namespace
 *
 * Do a copy of a namespace list. If @node is non-NULL the
 * new namespaces are added automatically.
 * Called by:
 *   xsltCopyTreeInternal()
 *
 * QUESTION: What is the exact difference between this function
 *  and xsltCopyNamespaceList() in "namespaces.c"?
 * ANSWER: xsltCopyNamespaceList() tries to apply ns-aliases.
 *
 * Returns: a new xmlNsPtr, or NULL in case of error.
 */
static xmlNsPtr
xsltCopyNamespaceListInternal(xmlNodePtr elem, xmlNsPtr ns) {
    xmlNsPtr ret = NULL;
    xmlNsPtr p = NULL, q, luNs;

    if (ns == NULL)
        return(NULL);
    /*
     * One can add namespaces only on element nodes
     */
    if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE))
        elem = NULL;

    do {
        if (ns->type != XML_NAMESPACE_DECL)
            break;
        /*
         * Avoid duplicating namespace declarations on the tree.
         */
        if (elem != NULL) {
            if ((elem->ns != NULL) &&
                xmlStrEqual(elem->ns->prefix, ns->prefix) &&
                xmlStrEqual(elem->ns->href, ns->href))
            {
                ns = ns->next;
                continue;
            }
            luNs = xmlSearchNs(elem->doc, elem, ns->prefix);
            if ((luNs != NULL) && (xmlStrEqual(luNs->href, ns->href)))
            {
                ns = ns->next;
                continue;
            }
        }
        q = xmlNewNs(elem, ns->href, ns->prefix);
        if (p == NULL) {
            ret = p = q;
        } else if (q != NULL) {
            p->next = q;
            p = q;
        }
        ns = ns->next;
    } while (ns != NULL);
    return(ret);
}

/**
 * xsltShallowCopyNsNode:
 * @ctxt:  the XSLT transformation context
 * @invocNode: responsible node in the stylesheet; used for error reports
 * @insert:  the target element node in the result tree
 * @ns: the namespace node
 *
 * This is used for copying ns-nodes with xsl:copy-of and xsl:copy.
 *
 * Returns a new/existing ns-node, or NULL.
 */
static xmlNsPtr
xsltShallowCopyNsNode(xsltTransformContextPtr ctxt,
                      xmlNodePtr invocNode,
                      xmlNodePtr insert,
                      xmlNsPtr ns)
{
    /*
     * TODO: Contrary to header comments, this is declared as int.
     * be modified to return a node pointer, or NULL if any error
     */
    xmlNsPtr tmpns;

    if ((insert == NULL) || (insert->type != XML_ELEMENT_NODE))
        return(NULL);

    if (insert->children != NULL) {
        xsltTransformError(ctxt, NULL, invocNode,
            "Namespace nodes must be added before "
            "any child nodes are added to an element.\n");
        return(NULL);
    }
    /*
     * BIG NOTE: Xalan-J simply overwrites any ns-decls with
     * an equal prefix. We definitively won't do that.
     *
     * MSXML 4.0 and the .NET ignores ns-decls for which an
     * equal prefix is already in use.
     *
     * Saxon raises an error like:
     * "net.sf.saxon.xpath.DynamicError: Cannot create two namespace
     * nodes with the same name".
     *
     * NOTE: We'll currently follow MSXML here.
     * REVISIT TODO: Check if it's better to follow Saxon here.
     */
    if (ns->prefix == NULL) {
        /*
        * If we are adding ns-nodes to an element using e.g.
        * <xsl:copy-of select="/foo/namespace::*">, then we need
        * to ensure that we don't incorrectly declare a default
        * namespace on an element in no namespace, which otherwise
        * would move the element incorrectly into a namespace, if
        * the node tree is serialized.
        */
        if (insert->ns == NULL)
            goto occupied;
    } else if ((ns->prefix[0] == 'x') &&
        xmlStrEqual(ns->prefix, BAD_CAST "xml"))
    {
        /*
        * The XML namespace is built in.
        */
        return(NULL);
    }

    if (insert->nsDef != NULL) {
        tmpns = insert->nsDef;
        do {
            if ((tmpns->prefix == NULL) == (ns->prefix == NULL)) {
                if ((tmpns->prefix == ns->prefix) ||
                    xmlStrEqual(tmpns->prefix, ns->prefix))
                {
                    /*
                    * Same prefix.
                    */
                    if (xmlStrEqual(tmpns->href, ns->href))
                        return(NULL);
                    goto occupied;
                }
            }
            tmpns = tmpns->next;
        } while (tmpns != NULL);
    }
    tmpns = xmlSearchNs(insert->doc, insert, ns->prefix);
    if ((tmpns != NULL) && xmlStrEqual(tmpns->href, ns->href))
        return(NULL);
    /*
    * Declare a new namespace.
    * TODO: The problem (wrt efficiency) with this xmlNewNs() is
    * that it will again search the already declared namespaces
    * for a duplicate :-/
    */
    return(xmlNewNs(insert, ns->href, ns->prefix));

occupied:
    /*
    * TODO: We could as well raise an error here (like Saxon does),
    * or at least generate a warning.
    */
    return(NULL);
}

/**
 * xsltCopyTreeInternal:
 * @ctxt:  the XSLT transformation context
 * @invocNode: responsible node in the stylesheet; used for error reports
 * @node:  the element node in the source tree
 * @insert:  the parent in the result tree
 * @isLRE:  indicates if @node is a Literal Result Element
 * @topElemVisited: indicates if a top-most element was already processed
 *
 * Make a copy of the full tree under the element node @node
 * and insert it as last child of @insert
 *
 * NOTE: Not to be used for Literal Result Elements.
 *
 * Used by:
 *  - xsltCopyOf()
 *
 * Returns a pointer to the new tree, or NULL in case of error
 */
static xmlNodePtr
xsltCopyTreeInternal(xsltTransformContextPtr ctxt,
                     xmlNodePtr invocNode,
                     xmlNodePtr node,
                     xmlNodePtr insert, int isLRE, int topElemVisited)
{
    xmlNodePtr copy;

    if (node == NULL)
        return(NULL);
    switch (node->type) {
        case XML_ELEMENT_NODE:
        case XML_ENTITY_REF_NODE:
        case XML_ENTITY_NODE:
        case XML_PI_NODE:
        case XML_COMMENT_NODE:
        case XML_DOCUMENT_NODE:
        case XML_HTML_DOCUMENT_NODE:
#ifdef LIBXML_DOCB_ENABLED
        case XML_DOCB_DOCUMENT_NODE:
#endif
            break;
        case XML_TEXT_NODE: {
            int noenc = (node->name == xmlStringTextNoenc);
            return(xsltCopyTextString(ctxt, insert, node->content, noenc));
            }
        case XML_CDATA_SECTION_NODE:
            return(xsltCopyTextString(ctxt, insert, node->content, 0));
        case XML_ATTRIBUTE_NODE:
            return((xmlNodePtr)
                xsltShallowCopyAttr(ctxt, invocNode, insert, (xmlAttrPtr) node));
        case XML_NAMESPACE_DECL:
            return((xmlNodePtr) xsltShallowCopyNsNode(ctxt, invocNode,
                insert, (xmlNsPtr) node));

        case XML_DOCUMENT_TYPE_NODE:
        case XML_DOCUMENT_FRAG_NODE:
        case XML_NOTATION_NODE:
        case XML_DTD_NODE:
        case XML_ELEMENT_DECL:
        case XML_ATTRIBUTE_DECL:
        case XML_ENTITY_DECL:
        case XML_XINCLUDE_START:
        case XML_XINCLUDE_END:
            return(NULL);
    }
    if (XSLT_IS_RES_TREE_FRAG(node)) {
        if (node->children != NULL)
            copy = xsltCopyTreeList(ctxt, invocNode,
                node->children, insert, 0, 0);
        else
            copy = NULL;
        return(copy);
    }
    copy = xmlDocCopyNode(node, insert->doc, 0);
    if (copy != NULL) {
        copy->doc = ctxt->output;
        copy = xsltAddChild(insert, copy);
        /*
         * The node may have been coalesced into another text node.
         */
        if (insert->last != copy)
            return(insert->last);
        copy->next = NULL;

        if (node->type == XML_ELEMENT_NODE) {
            /*
            * Copy in-scope namespace nodes.
            *
            * REVISIT: Since we try to reuse existing in-scope ns-decls by
            *  using xmlSearchNsByHref(), this will eventually change
            *  the prefix of an original ns-binding; thus it might
            *  break QNames in element/attribute content.
            * OPTIMIZE TODO: If we had a xmlNsPtr * on the transformation
            *  context, plus a ns-lookup function, which writes directly
            *  to a given list, then we wouldn't need to create/free the
            *  nsList every time.
            */
            if ((topElemVisited == 0) &&
                (node->parent != NULL) &&
                (node->parent->type != XML_DOCUMENT_NODE) &&
                (node->parent->type != XML_HTML_DOCUMENT_NODE))
            {
                xmlNsPtr *nsList, *curns, ns;

                /*
                * If this is a top-most element in a tree to be
                * copied, then we need to ensure that all in-scope
                * namespaces are copied over. For nodes deeper in the
                * tree, it is sufficient to reconcile only the ns-decls
                * (node->nsDef entries).
                */

                nsList = xmlGetNsList(node->doc, node);
                if (nsList != NULL) {
                    curns = nsList;
                    do {
                        /*
                        * Search by prefix first in order to break as less
                        * QNames in element/attribute content as possible.
                        */
                        ns = xmlSearchNs(insert->doc, insert,
                            (*curns)->prefix);

                        if ((ns == NULL) ||
                            (! xmlStrEqual(ns->href, (*curns)->href)))
                        {
                            ns = NULL;
                            /*
                            * Search by namespace name.
                            * REVISIT TODO: Currently disabled.
                            */
#if 0
                            ns = xmlSearchNsByHref(insert->doc,
                                insert, (*curns)->href);
#endif
                        }
                        if (ns == NULL) {
                            /*
                            * Declare a new namespace on the copied element.
                            */
                            ns = xmlNewNs(copy, (*curns)->href,
                                (*curns)->prefix);
                            /* TODO: Handle errors */
                        }
                        if (node->ns == *curns) {
                            /*
                            * If this was the original's namespace then set
                            * the generated counterpart on the copy.
                            */
                            copy->ns = ns;
                        }
                        curns++;
                    } while (*curns != NULL);
                    xmlFree(nsList);
                }
            } else if (node->nsDef != NULL) {
                /*
                * Copy over all namespace declaration attributes.
                */
                if (node->nsDef != NULL) {
                    if (isLRE)
                        xsltCopyNamespaceList(ctxt, copy, node->nsDef);
                    else
                        xsltCopyNamespaceListInternal(copy, node->nsDef);
                }
            }
            /*
            * Set the namespace.
            */
            if (node->ns != NULL) {
                if (copy->ns == NULL) {
                    /*
                    * This will map copy->ns to one of the newly created
                    * in-scope ns-decls, OR create a new ns-decl on @copy.
                    */
                    copy->ns = xsltGetSpecialNamespace(ctxt, invocNode,
                        node->ns->href, node->ns->prefix, copy);
                }
            } else if ((insert->type == XML_ELEMENT_NODE) &&
                (insert->ns != NULL))
            {
                /*
                * "Undeclare" the default namespace on @copy with xmlns="".
                */
                xsltGetSpecialNamespace(ctxt, invocNode, NULL, NULL, copy);
            }
            /*
            * Copy attribute nodes.
            */
            if (node->properties != NULL) {
                xsltCopyAttrListNoOverwrite(ctxt, invocNode,
                    copy, node->properties);
            }
            if (topElemVisited == 0)
                topElemVisited = 1;
        }
        /*
        * Copy the subtree.
        */
        if (node->children != NULL) {
            xsltCopyTreeList(ctxt, invocNode,
                node->children, copy, isLRE, topElemVisited);
        }
    } else {
        xsltTransformError(ctxt, NULL, invocNode,
            "xsltCopyTreeInternal: Copying of '%s' failed.\n", node->name);
    }
    return(copy);
}

/**
 * xsltCopyTree:
 * @ctxt:  the XSLT transformation context
 * @node:  the element node in the source tree
 * @insert:  the parent in the result tree
 * @literal:  indicates if @node is a Literal Result Element
 *
 * Make a copy of the full tree under the element node @node
 * and insert it as last child of @insert
 * For literal result element, some of the namespaces may not be copied
 * over according to section 7.1.
 * TODO: Why is this a public function?
 *
 * Returns a pointer to the new tree, or NULL in case of error
 */
xmlNodePtr
xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
             xmlNodePtr insert, int literal)
{
    return(xsltCopyTreeInternal(ctxt, node, node, insert, literal, 0));

}

/************************************************************************
 *                                                                      *
 *              Error/fallback processing                               *
 *                                                                      *
 ************************************************************************/

/**
 * xsltApplyFallbacks:
 * @ctxt:  a XSLT process context
 * @node:  the node in the source tree.
 * @inst:  the node generating the error
 *
 * Process possible xsl:fallback nodes present under @inst
 *
 * Returns the number of xsl:fallback element found and processed
 */
static int
xsltApplyFallbacks(xsltTransformContextPtr ctxt, xmlNodePtr node,
                   xmlNodePtr inst) {

    xmlNodePtr child;
    int ret = 0;

    if ((ctxt == NULL) || (node == NULL) || (inst == NULL) ||
        (inst->children == NULL))
        return(0);

    child = inst->children;
    while (child != NULL) {
        if ((IS_XSLT_ELEM(child)) &&
            (xmlStrEqual(child->name, BAD_CAST "fallback"))) {
#ifdef WITH_XSLT_DEBUG_PARSING
            xsltGenericDebug(xsltGenericDebugContext,
                             "applying xsl:fallback\n");
#endif
            ret++;
            xsltApplySequenceConstructor(ctxt, node, child->children,
                NULL);
        }
        child = child->next;
    }
    return(ret);
}

/************************************************************************
 *                                                                      *
 *                      Default processing                              *
 *                                                                      *
 ************************************************************************/

/**
 * xsltDefaultProcessOneNode:
 * @ctxt:  a XSLT process context
 * @node:  the node in the source tree.
 * @params: extra parameters passed to the template if any
 *
 * Process the source node with the default built-in template rule:
 * <xsl:template match="*|/">
 *   <xsl:apply-templates/>
 * </xsl:template>
 *
 * and
 *
 * <xsl:template match="text()|@*">
 *   <xsl:value-of select="."/>
 * </xsl:template>
 *
 * Note also that namespace declarations are copied directly:
 *
 * the built-in template rule is the only template rule that is applied
 * for namespace nodes.
 */
static void
xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
                          xsltStackElemPtr params) {
    xmlNodePtr copy;
    xmlNodePtr delete = NULL, cur;
    int nbchild = 0, oldSize;
    int childno = 0, oldPos;
    xsltTemplatePtr template;

    CHECK_STOPPED;
    /*
     * Handling of leaves
     */
    switch (node->type) {
        case XML_DOCUMENT_NODE:
        case XML_HTML_DOCUMENT_NODE:
        case XML_ELEMENT_NODE:
            break;
        case XML_CDATA_SECTION_NODE:
#ifdef WITH_XSLT_DEBUG_PROCESS
            XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
             "xsltDefaultProcessOneNode: copy CDATA %s\n",
                node->content));
#endif
            copy = xsltCopyText(ctxt, ctxt->insert, node, 0);
            if (copy == NULL) {
                xsltTransformError(ctxt, NULL, node,
                 "xsltDefaultProcessOneNode: cdata copy failed\n");
            }
            return;
        case XML_TEXT_NODE:
#ifdef WITH_XSLT_DEBUG_PROCESS
            if (node->content == NULL) {
                XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
                 "xsltDefaultProcessOneNode: copy empty text\n"));
                return;
            } else {
                XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
                 "xsltDefaultProcessOneNode: copy text %s\n",
                        node->content));
            }
#endif
            copy = xsltCopyText(ctxt, ctxt->insert, node, 0);
            if (copy == NULL) {
                xsltTransformError(ctxt, NULL, node,
                 "xsltDefaultProcessOneNode: text copy failed\n");
            }
            return;
        case XML_ATTRIBUTE_NODE:
            cur = node->children;
            while ((cur != NULL) && (cur->type != XML_TEXT_NODE))
                cur = cur->next;
            if (cur == NULL) {
                xsltTransformError(ctxt, NULL, node,
                 "xsltDefaultProcessOneNode: no text for attribute\n");
            } else {
#ifdef WITH_XSLT_DEBUG_PROCESS
                if (cur->content == NULL) {
                    XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
                     "xsltDefaultProcessOneNode: copy empty text\n"));
                } else {
                    XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
                     "xsltDefaultProcessOneNode: copy text %s\n",
                        cur->content));
                }
#endif
                copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
                if (copy == NULL) {
                    xsltTransformError(ctxt, NULL, node,
                     "xsltDefaultProcessOneNode: text copy failed\n");
                }
            }
            return;
        default:
            return;
    }
    /*
     * Handling of Elements: first pass, cleanup and counting
     */
    cur = node->children;
    while (cur != NULL) {
        switch (cur->type) {
            case XML_TEXT_NODE:
            case XML_CDATA_SECTION_NODE:
            case XML_DOCUMENT_NODE:
            case XML_HTML_DOCUMENT_NODE:
            case XML_ELEMENT_NODE:
            case XML_PI_NODE:
            case XML_COMMENT_NODE:
                nbchild++;
                break;
            case XML_DTD_NODE:
                /* Unlink the DTD, it's still reachable using doc->intSubset */
                if (cur->next != NULL)
                    cur->next->prev = cur->prev;
                if (cur->prev != NULL)
                    cur->prev->next = cur->next;
                break;
            default:
#ifdef WITH_XSLT_DEBUG_PROCESS
                XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
                 "xsltDefaultProcessOneNode: skipping node type %d\n",
                                 cur->type));
#endif
                delete = cur;
        }
        cur = cur->next;
        if (delete != NULL) {
#ifdef WITH_XSLT_DEBUG_PROCESS
            XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
                 "xsltDefaultProcessOneNode: removing ignorable blank node\n"));
#endif
            xmlUnlinkNode(delete);
            xmlFreeNode(delete);
            delete = NULL;
        }
    }
    if (delete != NULL) {
#ifdef WITH_XSLT_DEBUG_PROCESS
        XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
             "xsltDefaultProcessOneNode: removing ignorable blank node\n"));
#endif
        xmlUnlinkNode(delete);
        xmlFreeNode(delete);
        delete = NULL;
    }

    /*
     * Handling of Elements: second pass, actual processing
     */
    oldSize = ctxt->xpathCtxt->contextSize;
    oldPos = ctxt->xpathCtxt->proximityPosition;
    cur = node->children;
    while (cur != NULL) {
        childno++;
        switch (cur->type) {
            case XML_DOCUMENT_NODE:
            case XML_HTML_DOCUMENT_NODE:
            case XML_ELEMENT_NODE:
                ctxt->xpathCtxt->contextSize = nbchild;
                ctxt->xpathCtxt->proximityPosition = childno;
                xsltProcessOneNode(ctxt, cur, params);
                break;
            case XML_CDATA_SECTION_NODE:
                template = xsltGetTemplate(ctxt, cur, NULL);
                if (template) {
#ifdef WITH_XSLT_DEBUG_PROCESS
                    XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
                 "xsltDefaultProcessOneNode: applying template for CDATA %s\n",
                                     cur->content));
#endif
                    /*
                    * Instantiate the xsl:template.
                    */
                    xsltApplyXSLTTemplate(ctxt, cur, template->content,
                        template, params);
                } else /* if (ctxt->mode == NULL) */ {
#ifdef WITH_XSLT_DEBUG_PROCESS
                    XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
                     "xsltDefaultProcessOneNode: copy CDATA %s\n",
                                     cur->content));
#endif
                    copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
                    if (copy == NULL) {
                        xsltTransformError(ctxt, NULL, cur,
                            "xsltDefaultProcessOneNode: cdata copy failed\n");
                    }
                }
                break;
            case XML_TEXT_NODE:
                template = xsltGetTemplate(ctxt, cur, NULL);
                if (template) {
#ifdef WITH_XSLT_DEBUG_PROCESS
                    XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
             "xsltDefaultProcessOneNode: applying template for text %s\n",
                                     cur->content));
#endif
                    ctxt->xpathCtxt->contextSize = nbchild;
                    ctxt->xpathCtxt->proximityPosition = childno;
                    /*
                    * Instantiate the xsl:template.
                    */
                    xsltApplyXSLTTemplate(ctxt, cur, template->content,
                        template, params);
                } else /* if (ctxt->mode == NULL) */ {
#ifdef WITH_XSLT_DEBUG_PROCESS
                    if (cur->content == NULL) {
                        XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
                         "xsltDefaultProcessOneNode: copy empty text\n"));
                    } else {
                        XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
                     "xsltDefaultProcessOneNode: copy text %s\n",
                                         cur->content));
                    }
#endif
                    copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
                    if (copy == NULL) {
                        xsltTransformError(ctxt, NULL, cur,
                            "xsltDefaultProcessOneNode: text copy failed\n");
                    }
                }
                break;
            case XML_PI_NODE:
            case XML_COMMENT_NODE:
                template = xsltGetTemplate(ctxt, cur, NULL);
                if (template) {
#ifdef WITH_XSLT_DEBUG_PROCESS
                    if (cur->type == XML_PI_NODE) {
                        XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
                     "xsltDefaultProcessOneNode: template found for PI %s\n",
                                         cur->name));
                    } else if (cur->type == XML_COMMENT_NODE) {
                        XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
                     "xsltDefaultProcessOneNode: template found for comment\n"));
                    }
#endif
                    ctxt->xpathCtxt->contextSize = nbchild;
                    ctxt->xpathCtxt->proximityPosition = childno;
                    /*
                    * Instantiate the xsl:template.
                    */
                    xsltApplyXSLTTemplate(ctxt, cur, template->content,
                        template, params);
                }
                break;
            default:
                break;
        }
        cur = cur->next;
    }
    ctxt->xpathCtxt->contextSize = oldSize;
    ctxt->xpathCtxt->proximityPosition = oldPos;
}

/**
 * xsltProcessOneNode:
 * @ctxt:  a XSLT process context
 * @contextNode:  the "current node" in the source tree
 * @withParams:  extra parameters (e.g. xsl:with-param) passed to the
 *               template if any
 *
 * Process the source node.
 */
void
xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
                   xsltStackElemPtr withParams)
{
    xsltTemplatePtr templ;
    xmlNodePtr oldNode;

    templ = xsltGetTemplate(ctxt, contextNode, NULL);
    /*
     * If no template is found, apply the default rule.
     */
    if (templ == NULL) {
#ifdef WITH_XSLT_DEBUG_PROCESS
        if (contextNode->type == XML_DOCUMENT_NODE) {
            XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
             "xsltProcessOneNode: no template found for /\n"));
        } else if (contextNode->type == XML_CDATA_SECTION_NODE) {
            XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
             "xsltProcessOneNode: no template found for CDATA\n"));
        } else if (contextNode->type == XML_ATTRIBUTE_NODE) {
            XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
             "xsltProcessOneNode: no template found for attribute %s\n",
                             ((xmlAttrPtr) contextNode)->name));
        } else  {
            XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
             "xsltProcessOneNode: no template found for %s\n", contextNode->name));
        }
#endif
        oldNode = ctxt->node;
        ctxt->node = contextNode;
        xsltDefaultProcessOneNode(ctxt, contextNode, withParams);
        ctxt->node = oldNode;
        return;
    }

    if (contextNode->type == XML_ATTRIBUTE_NODE) {
        xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule;
        /*
        * Set the "current template rule".
        */
        ctxt->currentTemplateRule = templ;

#ifdef WITH_XSLT_DEBUG_PROCESS
        XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
             "xsltProcessOneNode: applying template '%s' for attribute %s\n",
                         templ->match, contextNode->name));
#endif
        xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams);

        ctxt->currentTemplateRule = oldCurTempRule;
    } else {
        xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule;
        /*
        * Set the "current template rule".
        */
        ctxt->currentTemplateRule = templ;

#ifdef WITH_XSLT_DEBUG_PROCESS
        if (contextNode->type == XML_DOCUMENT_NODE) {
            XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
             "xsltProcessOneNode: applying template '%s' for /\n",
                             templ->match));
        } else {
            XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
             "xsltProcessOneNode: applying template '%s' for %s\n",
                             templ->match, contextNode->name));
        }
#endif
        xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams);

        ctxt->currentTemplateRule = oldCurTempRule;
    }
}

static xmlNodePtr
xsltDebuggerStartSequenceConstructor(xsltTransformContextPtr ctxt,
                                     xmlNodePtr contextNode,
                                     xmlNodePtr list,
                                     xsltTemplatePtr templ,
                                     int *addCallResult)
{
    xmlNodePtr debugedNode = NULL;

    if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
        if (templ) {
            *addCallResult = xslAddCall(templ, templ->elem);
        } else {
            *addCallResult = xslAddCall(NULL, list);
        }
        switch (ctxt->debugStatus) {
            case XSLT_DEBUG_RUN_RESTART:
            case XSLT_DEBUG_QUIT:
                if (*addCallResult)
                    xslDropCall();
                return(NULL);
        }
        if (templ) {
            xslHandleDebugger(templ->elem, contextNode, templ, ctxt);
            debugedNode = templ->elem;
        } else if (list) {
            xslHandleDebugger(list, contextNode, templ, ctxt);
            debugedNode = list;
        } else if (ctxt->inst) {
            xslHandleDebugger(ctxt->inst, contextNode, templ, ctxt);
            debugedNode = ctxt->inst;
        }
    }
    return(debugedNode);
}

/**
 * xsltLocalVariablePush:
 * @ctxt: the transformation context
 * @variable: variable to be pushed to the variable stack
 * @level: new value for variable's level
 *
 * Places the variable onto the local variable stack
 *
 * Returns: 0 for success, -1 for any error
 * **NOTE:**
 * This is an internal routine and should not be called by users!
 */
int
xsltLocalVariablePush(xsltTransformContextPtr ctxt,
                      xsltStackElemPtr variable,
                      int level)
{
    if (ctxt->varsMax == 0) {
        ctxt->varsMax = 10;
        ctxt->varsTab =
            (xsltStackElemPtr *) xmlMalloc(ctxt->varsMax *
            sizeof(ctxt->varsTab[0]));
        if (ctxt->varsTab == NULL) {
            xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
            return (-1);
        }
    }
    if (ctxt->varsNr >= ctxt->varsMax) {
        ctxt->varsMax *= 2;
        ctxt->varsTab =
            (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab,
            ctxt->varsMax *
            sizeof(ctxt->varsTab[0]));
        if (ctxt->varsTab == NULL) {
            xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
            return (-1);
        }
    }
    ctxt->varsTab[ctxt->varsNr++] = variable;
    ctxt->vars = variable;
    variable->level = level;
    return(0);
}

/**
 * xsltReleaseLocalRVTs:
 *
 * Fragments which are results of extension instructions
 * are preserved; all other fragments are freed/cached.
 */
static void
xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt, xmlDocPtr base)
{
    xmlDocPtr cur = ctxt->localRVT, tmp;

    while ((cur != NULL) && (cur != base)) {
        if (cur->psvi == (void *) ((long) 1)) {
            cur = (xmlDocPtr) cur->next;
        } else {
            tmp = cur;
            cur = (xmlDocPtr) cur->next;

            if (tmp == ctxt->localRVT)
                ctxt->localRVT = cur;

            /*
            * We need ctxt->localRVTBase for extension instructions
            * which return values (like EXSLT's function).
            */
            if (tmp == ctxt->localRVTBase)
                ctxt->localRVTBase = cur;

            if (tmp->prev)
                tmp->prev->next = (xmlNodePtr) cur;
            if (cur)
                cur->prev = tmp->prev;
            xsltReleaseRVT(ctxt, tmp);
        }
    }
}

/**
 * xsltApplySequenceConstructor:
 * @ctxt:  a XSLT process context
 * @contextNode:  the "current node" in the source tree
 * @list:  the nodes of a sequence constructor;
 *         (plus leading xsl:param elements)
 * @templ: the compiled xsl:template (optional)
 *
 * Processes a sequence constructor.
 *
 * NOTE: ctxt->currentTemplateRule was introduced to reflect the
 * semantics of "current template rule". I.e. the field ctxt->templ
 * is not intended to reflect this, thus always pushed onto the
 * template stack.
 */
static void
xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
                             xmlNodePtr contextNode, xmlNodePtr list,
                             xsltTemplatePtr templ)
{
    xmlNodePtr oldInsert, oldInst, oldCurInst, oldContextNode;
    xmlNodePtr cur, insert, copy = NULL;
    int level = 0, oldVarsNr;
    xmlDocPtr oldLocalFragmentTop, oldLocalFragmentBase;

#ifdef XSLT_REFACTORED
    xsltStylePreCompPtr info;
#endif

#ifdef WITH_DEBUGGER
    int addCallResult = 0;
    xmlNodePtr debuggedNode = NULL;
#endif

    if (ctxt == NULL)
        return;

#ifdef WITH_DEBUGGER
    if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
        debuggedNode =
            xsltDebuggerStartSequenceConstructor(ctxt, contextNode,
                list, templ, &addCallResult);
        if (debuggedNode == NULL)
            return;
    }
#endif

    if (list == NULL)
        return;
    CHECK_STOPPED;

    oldLocalFragmentTop = ctxt->localRVT;
    oldInsert = insert = ctxt->insert;
    oldInst = oldCurInst = ctxt->inst;
    oldContextNode = ctxt->node;
    /*
    * Save current number of variables on the stack; new vars are popped when
    * exiting.
    */
    oldVarsNr = ctxt->varsNr;
    /*
    * Process the sequence constructor.
    */
    cur = list;
    while (cur != NULL) {
        ctxt->inst = cur;

#ifdef WITH_DEBUGGER
        switch (ctxt->debugStatus) {
            case XSLT_DEBUG_RUN_RESTART:
            case XSLT_DEBUG_QUIT:
                break;

        }
#endif
        /*
         * Test; we must have a valid insertion point.
         */
        if (insert == NULL) {

#ifdef WITH_XSLT_DEBUG_PROCESS
            XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
                "xsltApplySequenceConstructor: insert == NULL !\n"));
#endif
            goto error;
        }

#ifdef WITH_DEBUGGER
        if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (debuggedNode != cur))
            xslHandleDebugger(cur, contextNode, templ, ctxt);
#endif

#ifdef XSLT_REFACTORED
        if (cur->type == XML_ELEMENT_NODE) {
            info = (xsltStylePreCompPtr) cur->psvi;
            /*
            * We expect a compiled representation on:
            * 1) XSLT instructions of this XSLT version (1.0)
            *    (with a few exceptions)
            * 2) Literal result elements
            * 3) Extension instructions
            * 4) XSLT instructions of future XSLT versions
            *    (forwards-compatible mode).
            */
            if (info == NULL) {
                /*
                * Handle the rare cases where we don't expect a compiled
                * representation on an XSLT element.
                */
                if (IS_XSLT_ELEM_FAST(cur) && IS_XSLT_NAME(cur, "message")) {
                    xsltMessage(ctxt, contextNode, cur);
                    goto skip_children;
                }
                /*
                * Something really went wrong:
                */
                xsltTransformError(ctxt, NULL, cur,
                    "Internal error in xsltApplySequenceConstructor(): "
                    "The element '%s' in the stylesheet has no compiled "
                    "representation.\n",
                    cur->name);
                goto skip_children;
            }

            if (info->type == XSLT_FUNC_LITERAL_RESULT_ELEMENT) {
                xsltStyleItemLRElementInfoPtr lrInfo =
                    (xsltStyleItemLRElementInfoPtr) info;
                /*
                * Literal result elements
                * --------------------------------------------------------
                */
#ifdef WITH_XSLT_DEBUG_PROCESS
                XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
                    xsltGenericDebug(xsltGenericDebugContext,
                    "xsltApplySequenceConstructor: copy literal result "
                    "element '%s'\n", cur->name));
#endif
                /*
                * Copy the raw element-node.
                * OLD: if ((copy = xsltShallowCopyElem(ctxt, cur, insert))
                *     == NULL)
                *   goto error;
                */
                copy = xmlDocCopyNode(cur, insert->doc, 0);
                if (copy == NULL) {
                    xsltTransformError(ctxt, NULL, cur,
                        "Internal error in xsltApplySequenceConstructor(): "
                        "Failed to copy literal result element '%s'.\n",
                        cur->name);
                    goto error;
                } else {
                    /*
                    * Add the element-node to the result tree.
                    */
                    copy->doc = ctxt->output;
                    copy = xsltAddChild(insert, copy);
                    /*
                    * Create effective namespaces declarations.
                    * OLD: xsltCopyNamespaceList(ctxt, copy, cur->nsDef);
                    */
                    if (lrInfo->effectiveNs != NULL) {
                        xsltEffectiveNsPtr effNs = lrInfo->effectiveNs;
                        xmlNsPtr ns, lastns = NULL;

                        while (effNs != NULL) {
                            /*
                            * Avoid generating redundant namespace
                            * declarations; thus lookup if there is already
                            * such a ns-decl in the result.
                            */
                            ns = xmlSearchNs(copy->doc, copy, effNs->prefix);
                            if ((ns != NULL) &&
                                (xmlStrEqual(ns->href, effNs->nsName)))
                            {
                                effNs = effNs->next;
                                continue;
                            }
                            ns = xmlNewNs(copy, effNs->nsName, effNs->prefix);
                            if (ns == NULL) {
                                xsltTransformError(ctxt, NULL, cur,
                                    "Internal error in "
                                    "xsltApplySequenceConstructor(): "
                                    "Failed to copy a namespace "
                                    "declaration.\n");
                                goto error;
                            }

                            if (lastns == NULL)
                                copy->nsDef = ns;
                            else
                                lastns->next =ns;
                            lastns = ns;

                            effNs = effNs->next;
                        }

                    }
                    /*
                    * NOTE that we don't need to apply ns-alising: this was
                    *  already done at compile-time.
                    */
                    if (cur->ns != NULL) {
                        /*
                        * If there's no such ns-decl in the result tree,
                        * then xsltGetSpecialNamespace() will
                        * create a ns-decl on the copied node.
                        */
                        copy->ns = xsltGetSpecialNamespace(ctxt, cur,
                            cur->ns->href, cur->ns->prefix, copy);
                    } else {
                        /*
                        * Undeclare the default namespace if needed.
                        * This can be skipped, if the result element has
                        *  no ns-decls, in which case the result element
                        *  obviously does not declare a default namespace;
                        *  AND there's either no parent, or the parent
                        *  element is in no namespace; this means there's no
                        *  default namespace is scope to care about.
                        *
                        * REVISIT: This might result in massive
                        *  generation of ns-decls if nodes in a default
                        *  namespaces are mixed with nodes in no namespace.
                        *
                        */
                        if (copy->nsDef ||
                            ((insert != NULL) &&
                             (insert->type == XML_ELEMENT_NODE) &&
                             (insert->ns != NULL)))
                        {
                            xsltGetSpecialNamespace(ctxt, cur,
                                NULL, NULL, copy);
                        }
                    }
                }
                /*
                * SPEC XSLT 2.0 "Each attribute of the literal result
                *  element, other than an attribute in the XSLT namespace,
                *  is processed to produce an attribute for the element in
                *  the result tree."
                * NOTE: See bug #341325.
                */
                if (cur->properties != NULL) {
                    xsltAttrListTemplateProcess(ctxt, copy, cur->properties);
                }
            } else if (IS_XSLT_ELEM_FAST(cur)) {
                /*
                * XSLT instructions
                * --------------------------------------------------------
                */
                if (info->type == XSLT_FUNC_UNKOWN_FORWARDS_COMPAT) {
                    /*
                    * We hit an unknown XSLT element.
                    * Try to apply one of the fallback cases.
                    */
                    ctxt->insert = insert;
                    if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
                        xsltTransformError(ctxt, NULL, cur,
                            "The is no fallback behaviour defined for "
                            "the unknown XSLT element '%s'.\n",
                            cur->name);
                    }
                    ctxt->insert = oldInsert;
                } else if (info->func != NULL) {
                    /*
                    * Execute the XSLT instruction.
                    */
                    ctxt->insert = insert;

                    info->func(ctxt, contextNode, cur,
                        (xsltElemPreCompPtr) info);

                    /*
                    * Cleanup temporary tree fragments.
                    */
                    if (oldLocalFragmentTop != ctxt->localRVT)
                        xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);

                    ctxt->insert = oldInsert;
                } else if (info->type == XSLT_FUNC_VARIABLE) {
                    xsltStackElemPtr tmpvar = ctxt->vars;

                    xsltParseStylesheetVariable(ctxt, cur);

                    if (tmpvar != ctxt->vars) {
                        /*
                        * TODO: Using a @tmpvar is an annoying workaround, but
                        *  the current mechanisms do not provide any other way
                        *  of knowing if the var was really pushed onto the
                        *  stack.
                        */
                        ctxt->vars->level = level;
                    }
                } else if (info->type == XSLT_FUNC_MESSAGE) {
                    /*
                    * TODO: Won't be hit, since we don't compile xsl:message.
                    */
                    xsltMessage(ctxt, contextNode, cur);
                } else {
                    xsltTransformError(ctxt, NULL, cur,
                        "Unexpected XSLT element '%s'.\n", cur->name);
                }
                goto skip_children;

            } else {
                xsltTransformFunction func;
                /*
                * Extension intructions (elements)
                * --------------------------------------------------------
                */
                if (cur->psvi == xsltExtMarker) {
                    /*
                    * The xsltExtMarker was set during the compilation
                    * of extension instructions if there was no registered
                    * handler for this specific extension function at
                    * compile-time.
                    * Libxslt will now lookup if a handler is
                    * registered in the context of this transformation.
                    */
                    func = (xsltTransformFunction)
                        xsltExtElementLookup(ctxt, cur->name, cur->ns->href);
                } else
                    func = ((xsltElemPreCompPtr) cur->psvi)->func;

                if (func == NULL) {
                    /*
                    * No handler available.
                    * Try to execute fallback behaviour via xsl:fallback.
                    */
#ifdef WITH_XSLT_DEBUG_PROCESS
                    XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
                        xsltGenericDebug(xsltGenericDebugContext,
                            "xsltApplySequenceConstructor: unknown extension %s\n",
                            cur->name));
#endif
                    ctxt->insert = insert;
                    if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
                        xsltTransformError(ctxt, NULL, cur,
                            "Unknown extension instruction '{%s}%s'.\n",
                            cur->ns->href, cur->name);
                    }
                    ctxt->insert = oldInsert;
                } else {
                    /*
                    * Execute the handler-callback.
                    */
#ifdef WITH_XSLT_DEBUG_PROCESS
                    XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
                        "xsltApplySequenceConstructor: extension construct %s\n",
                        cur->name));
#endif
                    ctxt->insert = insert;
                    /*
                    * We need the fragment base for extension instructions
                    * which return values (like EXSLT's function).
                    */
                    oldLocalFragmentBase = ctxt->localRVTBase;
                    ctxt->localRVTBase = NULL;

                    func(ctxt, contextNode, cur, cur->psvi);

                    ctxt->localRVTBase = oldLocalFragmentBase;
                    /*
                    * Cleanup temporary tree fragments.
                    */
                    if (oldLocalFragmentTop != ctxt->localRVT)
                        xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);

                    ctxt->insert = oldInsert;
                }
                goto skip_children;
            }

        } else if (XSLT_IS_TEXT_NODE(cur)) {
            /*
            * Text
            * ------------------------------------------------------------
            */
#ifdef WITH_XSLT_DEBUG_PROCESS
            if (cur->name == xmlStringTextNoenc) {
                XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
                    xsltGenericDebug(xsltGenericDebugContext,
                    "xsltApplySequenceConstructor: copy unescaped text '%s'\n",
                    cur->content));
            } else {
                XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
                    xsltGenericDebug(xsltGenericDebugContext,
                    "xsltApplySequenceConstructor: copy text '%s'\n",
                    cur->content));
            }
#endif
            if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL)
                goto error;
        }

#else /* XSLT_REFACTORED */

        if (IS_XSLT_ELEM(cur)) {
            /*
             * This is an XSLT node
             */
            xsltStylePreCompPtr info = (xsltStylePreCompPtr) cur->psvi;

            if (info == NULL) {
                if (IS_XSLT_NAME(cur, "message")) {
                    xsltMessage(ctxt, contextNode, cur);
                } else {
                    /*
                     * That's an error try to apply one of the fallback cases
                     */
                    ctxt->insert = insert;
                    if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
                        xsltGenericError(xsltGenericErrorContext,
                            "xsltApplySequenceConstructor: %s was not compiled\n",
                            cur->name);
                    }
                    ctxt->insert = oldInsert;
                }
                goto skip_children;
            }

            if (info->func != NULL) {
                oldCurInst = ctxt->inst;
                ctxt->inst = cur;
                ctxt->insert = insert;
                oldLocalFragmentBase = ctxt->localRVTBase;
                ctxt->localRVTBase = NULL;

                info->func(ctxt, contextNode, cur, (xsltElemPreCompPtr) info);

                ctxt->localRVTBase = oldLocalFragmentBase;
                /*
                * Cleanup temporary tree fragments.
                */
                if (oldLocalFragmentTop != ctxt->localRVT)
                    xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);

                ctxt->insert = oldInsert;
                ctxt->inst = oldCurInst;
                goto skip_children;
            }

            if (IS_XSLT_NAME(cur, "variable")) {
                xsltStackElemPtr tmpvar = ctxt->vars;

                oldCurInst = ctxt->inst;
                ctxt->inst = cur;

                xsltParseStylesheetVariable(ctxt, cur);

                ctxt->inst = oldCurInst;

                if (tmpvar != ctxt->vars) {
                    /*
                    * TODO: Using a @tmpvar is an annoying workaround, but
                    *  the current mechanisms do not provide any other way
                    *  of knowing if the var was really pushed onto the
                    *  stack.
                    */
                    ctxt->vars->level = level;
                }
            } else if (IS_XSLT_NAME(cur, "message")) {
                xsltMessage(ctxt, contextNode, cur);
            } else {
                xsltTransformError(ctxt, NULL, cur,
                    "Unexpected XSLT element '%s'.\n", cur->name);
            }
            goto skip_children;
        } else if ((cur->type == XML_TEXT_NODE) ||
                   (cur->type == XML_CDATA_SECTION_NODE)) {

            /*
             * This text comes from the stylesheet
             * For stylesheets, the set of whitespace-preserving
             * element names consists of just xsl:text.
             */
#ifdef WITH_XSLT_DEBUG_PROCESS
            if (cur->type == XML_CDATA_SECTION_NODE) {
                XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
                                 "xsltApplySequenceConstructor: copy CDATA text %s\n",
                                 cur->content));
            } else if (cur->name == xmlStringTextNoenc) {
                XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
                                 "xsltApplySequenceConstructor: copy unescaped text %s\n",
                                 cur->content));
            } else {
                XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
                                 "xsltApplySequenceConstructor: copy text %s\n",
                                 cur->content));
            }
#endif
            if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL)
                goto error;
        } else if ((cur->type == XML_ELEMENT_NODE) &&
                   (cur->ns != NULL) && (cur->psvi != NULL)) {
            xsltTransformFunction function;

            oldCurInst = ctxt->inst;
            ctxt->inst = cur;
            /*
             * Flagged as an extension element
             */
            if (cur->psvi == xsltExtMarker)
                function = (xsltTransformFunction)
                    xsltExtElementLookup(ctxt, cur->name, cur->ns->href);
            else
                function = ((xsltElemPreCompPtr) cur->psvi)->func;

            if (function == NULL) {
                xmlNodePtr child;
                int found = 0;

#ifdef WITH_XSLT_DEBUG_PROCESS
                XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
                    "xsltApplySequenceConstructor: unknown extension %s\n",
                    cur->name));
#endif
                /*
                 * Search if there are fallbacks
                 */
                child = cur->children;
                while (child != NULL) {
                    if ((IS_XSLT_ELEM(child)) &&
                        (IS_XSLT_NAME(child, "fallback")))
                    {
                        found = 1;
                        xsltApplySequenceConstructor(ctxt, contextNode,
                            child->children, NULL);
                    }
                    child = child->next;
                }

                if (!found) {
                    xsltTransformError(ctxt, NULL, cur,
                        "xsltApplySequenceConstructor: failed to find extension %s\n",
                        cur->name);
                }
            } else {
#ifdef WITH_XSLT_DEBUG_PROCESS
                XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
                    "xsltApplySequenceConstructor: extension construct %s\n",
                    cur->name));
#endif

                ctxt->insert = insert;
                /*
                * We need the fragment base for extension instructions
                * which return values (like EXSLT's function).
                */
                oldLocalFragmentBase = ctxt->localRVTBase;
                ctxt->localRVTBase = NULL;

                function(ctxt, contextNode, cur, cur->psvi);
                /*
                * Cleanup temporary tree fragments.
                */
                if (oldLocalFragmentTop != ctxt->localRVT)
                    xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);

                ctxt->localRVTBase = oldLocalFragmentBase;
                ctxt->insert = oldInsert;

            }
            ctxt->inst = oldCurInst;
            goto skip_children;
        } else if (cur->type == XML_ELEMENT_NODE) {
#ifdef WITH_XSLT_DEBUG_PROCESS
            XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
                "xsltApplySequenceConstructor: copy node %s\n",
                cur->name));
#endif
            oldCurInst = ctxt->inst;
            ctxt->inst = cur;

            if ((copy = xsltShallowCopyElem(ctxt, cur, insert, 1)) == NULL)
                goto error;
            /*
             * Add extra namespaces inherited from the current template
             * if we are in the first level children and this is a
             * "real" template.
             */
            if ((templ != NULL) && (oldInsert == insert) &&
                (ctxt->templ != NULL) && (ctxt->templ->inheritedNs != NULL)) {
                int i;
                xmlNsPtr ns, ret;

                for (i = 0; i < ctxt->templ->inheritedNsNr; i++) {
                    const xmlChar *URI = NULL;
                    xsltStylesheetPtr style;
                    ns = ctxt->templ->inheritedNs[i];

                    /* Note that the XSLT namespace was already excluded
                    * in xsltGetInheritedNsList().
                    */
#if 0
                    if (xmlStrEqual(ns->href, XSLT_NAMESPACE))
                        continue;
#endif
                    style = ctxt->style;
                    while (style != NULL) {
                        if (style->nsAliases != NULL)
                            URI = (const xmlChar *)
                                xmlHashLookup(style->nsAliases, ns->href);
                        if (URI != NULL)
                            break;

                        style = xsltNextImport(style);
                    }
                    if (URI == UNDEFINED_DEFAULT_NS)
                        continue;
                    if (URI == NULL)
                        URI = ns->href;
                    /*
                    * TODO: The following will still be buggy for the
                    * non-refactored code.
                    */
                    ret = xmlSearchNs(copy->doc, copy, ns->prefix);
                    if ((ret == NULL) || (!xmlStrEqual(ret->href, URI)))
                    {
                        xmlNewNs(copy, URI, ns->prefix);
                    }
                }
                if (copy->ns != NULL) {
                    /*
                     * Fix the node namespace if needed
                     */
                    copy->ns = xsltGetNamespace(ctxt, cur, copy->ns, copy);
                }
            }
            /*
             * all the attributes are directly inherited
             */
            if (cur->properties != NULL) {
                xsltAttrListTemplateProcess(ctxt, copy, cur->properties);
            }
            ctxt->inst = oldCurInst;
        }
#endif /* else of XSLT_REFACTORED */

        /*
         * Descend into content in document order.
         */
        if (cur->children != NULL) {
            if (cur->children->type != XML_ENTITY_DECL) {
                cur = cur->children;
                level++;
                if (copy != NULL)
                    insert = copy;
                continue;
            }
        }

skip_children:
        /*
        * If xslt:message was just processed, we might have hit a
        * terminate='yes'; if so, then break the loop and clean up.
        * TODO: Do we need to check this also before trying to descend
        *  into the content?
        */
        if (ctxt->state == XSLT_STATE_STOPPED)
            break;
        if (cur->next != NULL) {
            cur = cur->next;
            continue;
        }

        do {
            cur = cur->parent;
            level--;
            /*
            * Pop variables/params (xsl:variable and xsl:param).
            */
            if ((ctxt->varsNr > oldVarsNr) && (ctxt->vars->level > level)) {
                xsltLocalVariablePop(ctxt, oldVarsNr, level);
            }

            insert = insert->parent;
            if (cur == NULL)
                break;
            if (cur == list->parent) {
                cur = NULL;
                break;
            }
            if (cur->next != NULL) {
                cur = cur->next;
                break;
            }
        } while (cur != NULL);
    }

error:
    /*
    * In case of errors: pop remaining variables.
    */
    if (ctxt->varsNr > oldVarsNr)
        xsltLocalVariablePop(ctxt, oldVarsNr, -1);

    ctxt->node = oldContextNode;
    ctxt->inst = oldInst;
    ctxt->insert = oldInsert;

#ifdef WITH_DEBUGGER
    if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) {
        xslDropCall();
    }
#endif
}

/*
* xsltApplyXSLTTemplate:
* @ctxt:  a XSLT transformation context
* @contextNode:  the node in the source tree.
* @list:  the nodes of a sequence constructor;
*         (plus leading xsl:param elements)
* @templ: the compiled xsl:template declaration;
*         NULL if a sequence constructor
* @withParams:  a set of caller-parameters (xsl:with-param) or NULL
*
* Called by:
* - xsltApplyImports()
* - xsltCallTemplate()
* - xsltDefaultProcessOneNode()
* - xsltProcessOneNode()
*/
static void
xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt,
                      xmlNodePtr contextNode,
                      xmlNodePtr list,
                      xsltTemplatePtr templ,
                      xsltStackElemPtr withParams)
{
    int oldVarsBase = 0;
    long start = 0;
    xmlNodePtr cur;
    xsltStackElemPtr tmpParam = NULL;
    xmlDocPtr oldUserFragmentTop, oldLocalFragmentTop;

#ifdef XSLT_REFACTORED
    xsltStyleItemParamPtr iparam;
#else
    xsltStylePreCompPtr iparam;
#endif

#ifdef WITH_DEBUGGER
    int addCallResult = 0;
#endif

    if (ctxt == NULL)
        return;
    if (templ == NULL) {
        xsltTransformError(ctxt, NULL, list,
            "xsltApplyXSLTTemplate: Bad arguments; @templ is mandatory.\n");
        return;
    }

#ifdef WITH_DEBUGGER
    if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
        if (xsltDebuggerStartSequenceConstructor(ctxt, contextNode,
                list, templ, &addCallResult) == NULL)
            return;
    }
#endif

    if (list == NULL)
        return;
    CHECK_STOPPED;

    /*
    * Check for infinite recursion: stop if the maximum of nested templates
    * is excceeded. Adjust xsltMaxDepth if you need more.
    */
    if (((ctxt->templNr >= xsltMaxDepth) ||
        (ctxt->varsNr >= 5 * xsltMaxDepth)))
    {
        xsltTransformError(ctxt, NULL, list,
            "xsltApplyXSLTTemplate: A potential infinite template recursion "
            "was detected.\n"
            "You can adjust xsltMaxDepth (--maxdepth) in order to "
            "raise the maximum number of nested template calls and "
            "variables/params (currently set to %d).\n",
            xsltMaxDepth);
        xsltDebug(ctxt, contextNode, list, NULL);
        return;
    }

    oldUserFragmentTop = ctxt->tmpRVT;
    ctxt->tmpRVT = NULL;
    oldLocalFragmentTop = ctxt->localRVT;

    /*
    * Initiate a distinct scope of local params/variables.
    */
    oldVarsBase = ctxt->varsBase;
    ctxt->varsBase = ctxt->varsNr;

    ctxt->node = contextNode;
    if (ctxt->profile) {
        templ->nbCalls++;
        start = xsltTimestamp();
        profPush(ctxt, 0);
    }
    /*
    * Push the xsl:template declaration onto the stack.
    */
    templPush(ctxt, templ);

#ifdef WITH_XSLT_DEBUG_PROCESS
    if (templ->name != NULL)
        XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
        "applying xsl:template '%s'\n", templ->name));
#endif
    /*
    * Process xsl:param instructions and skip those elements for
    * further processing.
    */
    cur = list;
    do {
        if (cur->type == XML_TEXT_NODE) {
            cur = cur->next;
            continue;
        }
        if ((cur->type != XML_ELEMENT_NODE) ||
            (cur->name[0] != 'p') ||
            (cur->psvi == NULL) ||
            (! xmlStrEqual(cur->name, BAD_CAST "param")) ||
            (! IS_XSLT_ELEM(cur)))
        {
            break;
        }

        list = cur->next;

#ifdef XSLT_REFACTORED
        iparam = (xsltStyleItemParamPtr) cur->psvi;
#else
        iparam = (xsltStylePreCompPtr) cur->psvi;
#endif

        /*
        * Substitute xsl:param for a given xsl:with-param.
        * Since the XPath expression will reference the params/vars
        * by index, we need to slot the xsl:with-params in the
        * order of encountered xsl:params to keep the sequence of
        * params/variables in the stack exactly as it was at
        * compile time,
        */
        tmpParam = NULL;
        if (withParams) {
            tmpParam = withParams;
            do {
                if ((tmpParam->name == (iparam->name)) &&
                    (tmpParam->nameURI == (iparam->ns)))
                {
                    /*
                    * Push the caller-parameter.
                    */
                    xsltLocalVariablePush(ctxt, tmpParam, -1);
                    break;
                }
                tmpParam = tmpParam->next;
            } while (tmpParam != NULL);
        }
        /*
        * Push the xsl:param.
        */
        if (tmpParam == NULL) {
            /*
            * Note that we must assume that the added parameter
            * has a @depth of 0.
            */
            xsltParseStylesheetParam(ctxt, cur);
        }
        cur = cur->next;
    } while (cur != NULL);
    /*
    * Process the sequence constructor.
    */
    xsltApplySequenceConstructor(ctxt, contextNode, list, templ);

    /*
    * Remove remaining xsl:param and xsl:with-param items from
    * the stack. Don't free xsl:with-param items.
    */
    if (ctxt->varsNr > ctxt->varsBase)
        xsltTemplateParamsCleanup(ctxt);
    ctxt->varsBase = oldVarsBase;

    /*
    * Clean up remaining local tree fragments.
    * This also frees fragments which are the result of
    * extension instructions. Should normally not be hit; but
    * just for the case xsltExtensionInstructionResultFinalize()
    * was not called by the extension author.
    */
    if (oldLocalFragmentTop != ctxt->localRVT) {
        xmlDocPtr curdoc = ctxt->localRVT, tmp;

        do {
            tmp = curdoc;
            curdoc = (xmlDocPtr) curdoc->next;
            /* Need to housekeep localRVTBase */
            if (tmp == ctxt->localRVTBase)
                ctxt->localRVTBase = curdoc;
            if (tmp->prev)
                tmp->prev->next = (xmlNodePtr) curdoc;
            if (curdoc)
                curdoc->prev = tmp->prev;
            xsltReleaseRVT(ctxt, tmp);
        } while (curdoc != oldLocalFragmentTop);
    }
    ctxt->localRVT = oldLocalFragmentTop;

    /*
    * Release user-created fragments stored in the scope
    * of xsl:template. Note that this mechanism is deprecated:
    * user code should now use xsltRegisterLocalRVT() instead
    * of the obsolete xsltRegisterTmpRVT().
    */
    if (ctxt->tmpRVT) {
        xmlDocPtr curdoc = ctxt->tmpRVT, tmp;

        while (curdoc != NULL) {
            tmp = curdoc;
            curdoc = (xmlDocPtr) curdoc->next;
            xsltReleaseRVT(ctxt, tmp);
        }
    }
    ctxt->tmpRVT = oldUserFragmentTop;

    /*
    * Pop the xsl:template declaration from the stack.
    */
    templPop(ctxt);
    if (ctxt->profile) {
        long spent, child, total, end;

        end = xsltTimestamp();
        child = profPop(ctxt);
        total = end - start;
        spent = total - child;
        if (spent <= 0) {
            /*
            * Not possible unless the original calibration failed
            * we can try to correct it on the fly.
            */
            xsltCalibrateAdjust(spent);
            spent = 0;
        }

        templ->time += spent;
        if (ctxt->profNr > 0)
            ctxt->profTab[ctxt->profNr - 1] += total;
    }

#ifdef WITH_DEBUGGER
    if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) {
        xslDropCall();
    }
#endif
}


/**
 * xsltApplyOneTemplate:
 * @ctxt:  a XSLT process context
 * @contextNode:  the node in the source tree.
 * @list:  the nodes of a sequence constructor
 * @templ: not used
 * @params:  a set of parameters (xsl:param) or NULL
 *
 * Processes a sequence constructor on the current node in the source tree.
 *
 * @params are the already computed variable stack items; this function
 * pushes them on the variable stack, and pops them before exiting; it's
 * left to the caller to free or reuse @params afterwards. The initial
 * states of the variable stack will always be restored before this
 * function exits.
 * NOTE that this does *not* initiate a new distinct variable scope; i.e.
 * variables already on the stack are visible to the process. The caller's
 * side needs to start a new variable scope if needed (e.g. in exsl:function).
 *
 * @templ is obsolete and not used anymore (e.g. <exslt:function> does not
 * provide a @templ); a non-NULL @templ might raise an error in the future.
 *
 * BIG NOTE: This function is not intended to process the content of an
 * xsl:template; it does not expect xsl:param instructions in @list and
 * will report errors if found.
 *
 * Called by:
 *  - xsltEvalVariable() (variables.c)
 *  - exsltFuncFunctionFunction() (libexsl/functions.c)
 */
void
xsltApplyOneTemplate(xsltTransformContextPtr ctxt,
                     xmlNodePtr contextNode,
                     xmlNodePtr list,
                     xsltTemplatePtr templ ATTRIBUTE_UNUSED,
                     xsltStackElemPtr params)
{
    if ((ctxt == NULL) || (list == NULL))
        return;
    CHECK_STOPPED;

    if (params) {
        /*
         * This code should be obsolete - was previously used
         * by libexslt/functions.c, but due to bug 381319 the
         * logic there was changed.
         */
        int oldVarsNr = ctxt->varsNr;

        /*
        * Push the given xsl:param(s) onto the variable stack.
        */
        while (params != NULL) {
            xsltLocalVariablePush(ctxt, params, -1);
            params = params->next;
        }
        xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
        /*
        * Pop the given xsl:param(s) from the stack but don't free them.
        */
        xsltLocalVariablePop(ctxt, oldVarsNr, -2);
    } else
        xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
}

/************************************************************************
 *                                                                      *
 *                  XSLT-1.1 extensions                                 *
 *                                                                      *
 ************************************************************************/

/**
 * xsltDocumentElem:
 * @ctxt:  an XSLT processing context
 * @node:  The current node
 * @inst:  the instruction in the stylesheet
 * @castedComp:  precomputed information
 *
 * Process an EXSLT/XSLT-1.1 document element
 */
void
xsltDocumentElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
                 xmlNodePtr inst, xsltStylePreCompPtr castedComp)
{
#ifdef XSLT_REFACTORED
    xsltStyleItemDocumentPtr comp = (xsltStyleItemDocumentPtr) castedComp;
#else
    xsltStylePreCompPtr comp = castedComp;
#endif
    xsltStylesheetPtr style = NULL;
    int ret;
    xmlChar *filename = NULL, *prop, *elements;
    xmlChar *element, *end;
    xmlDocPtr res = NULL;
    xmlDocPtr oldOutput;
    xmlNodePtr oldInsert, root;
    const char *oldOutputFile;
    xsltOutputType oldType;
    xmlChar *URL = NULL;
    const xmlChar *method;
    const xmlChar *doctypePublic;
    const xmlChar *doctypeSystem;
    const xmlChar *version;
    const xmlChar *encoding;

    if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
        return;

    if (comp->filename == NULL) {

        if (xmlStrEqual(inst->name, (const xmlChar *) "output")) {
            /*
            * The element "output" is in the namespace XSLT_SAXON_NAMESPACE
            *   (http://icl.com/saxon)
            * The @file is in no namespace.
            */
#ifdef WITH_XSLT_DEBUG_EXTRA
            xsltGenericDebug(xsltGenericDebugContext,
                             "Found saxon:output extension\n");
#endif
            URL = xsltEvalAttrValueTemplate(ctxt, inst,
                                                 (const xmlChar *) "file",
                                                 XSLT_SAXON_NAMESPACE);

            if (URL == NULL)
                URL = xsltEvalAttrValueTemplate(ctxt, inst,
                                                 (const xmlChar *) "href",
                                                 XSLT_SAXON_NAMESPACE);
        } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) {
#ifdef WITH_XSLT_DEBUG_EXTRA
            xsltGenericDebug(xsltGenericDebugContext,
                             "Found xalan:write extension\n");
#endif
            URL = xsltEvalAttrValueTemplate(ctxt, inst,
                                                 (const xmlChar *)
                                                 "select",
                                                 XSLT_XALAN_NAMESPACE);
            if (URL != NULL) {
                xmlXPathCompExprPtr cmp;
                xmlChar *val;

                /*
                 * Trying to handle bug #59212
                 * The value of the "select" attribute is an
                 * XPath expression.
                 * (see http://xml.apache.org/xalan-j/extensionslib.html#redirect)
                 */
                cmp = xmlXPathCompile(URL);
                val = xsltEvalXPathString(ctxt, cmp);
                xmlXPathFreeCompExpr(cmp);
                xmlFree(URL);
                URL = val;
            }
            if (URL == NULL)
                URL = xsltEvalAttrValueTemplate(ctxt, inst,
                                                     (const xmlChar *)
                                                     "file",
                                                     XSLT_XALAN_NAMESPACE);
            if (URL == NULL)
                URL = xsltEvalAttrValueTemplate(ctxt, inst,
                                                     (const xmlChar *)
                                                     "href",
                                                     XSLT_XALAN_NAMESPACE);
        } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) {
            URL = xsltEvalAttrValueTemplate(ctxt, inst,
                                                 (const xmlChar *) "href",
                                                 NULL);
        }

    } else {
        URL = xmlStrdup(comp->filename);
    }

    if (URL == NULL) {
        xsltTransformError(ctxt, NULL, inst,
                         "xsltDocumentElem: href/URI-Reference not found\n");
        return;
    }

    /*
     * If the computation failed, it's likely that the URL wasn't escaped
     */
    filename = xmlBuildURI(URL, (const xmlChar *) ctxt->outputFile);
    if (filename == NULL) {
        xmlChar *escURL;

        escURL=xmlURIEscapeStr(URL, BAD_CAST ":/.?,");
        if (escURL != NULL) {
            filename = xmlBuildURI(escURL, (const xmlChar *) ctxt->outputFile);
            xmlFree(escURL);
        }
    }

    if (filename == NULL) {
        xsltTransformError(ctxt, NULL, inst,
                         "xsltDocumentElem: URL computation failed for %s\n",
                         URL);
        xmlFree(URL);
        return;
    }

    /*
     * Security checking: can we write to this resource
     */
    if (ctxt->sec != NULL) {
        ret = xsltCheckWrite(ctxt->sec, ctxt, filename);
        if (ret == 0) {
            xsltTransformError(ctxt, NULL, inst,
                 "xsltDocumentElem: write rights for %s denied\n",
                             filename);
            xmlFree(URL);
            xmlFree(filename);
            return;
        }
    }

    oldOutputFile = ctxt->outputFile;
    oldOutput = ctxt->output;
    oldInsert = ctxt->insert;
    oldType = ctxt->type;
    ctxt->outputFile = (const char *) filename;

    style = xsltNewStylesheet();
    if (style == NULL) {
        xsltTransformError(ctxt, NULL, inst,
                         "xsltDocumentElem: out of memory\n");
        goto error;
    }

    /*
     * Version described in 1.1 draft allows full parameterization
     * of the output.
     */
    prop = xsltEvalAttrValueTemplate(ctxt, inst,
                                     (const xmlChar *) "version",
                                     NULL);
    if (prop != NULL) {
        if (style->version != NULL)
            xmlFree(style->version);
        style->version = prop;
    }
    prop = xsltEvalAttrValueTemplate(ctxt, inst,
                                     (const xmlChar *) "encoding",
                                     NULL);
    if (prop != NULL) {
        if (style->encoding != NULL)
            xmlFree(style->encoding);
        style->encoding = prop;
    }
    prop = xsltEvalAttrValueTemplate(ctxt, inst,
                                     (const xmlChar *) "method",
                                     NULL);
    if (prop != NULL) {
        const xmlChar *URI;

        if (style->method != NULL)
            xmlFree(style->method);
        style->method = NULL;
        if (style->methodURI != NULL)
            xmlFree(style->methodURI);
        style->methodURI = NULL;

        URI = xsltGetQNameURI(inst, &prop);
        if (prop == NULL) {
            if (style != NULL) style->errors++;
        } else if (URI == NULL) {
            if ((xmlStrEqual(prop, (const xmlChar *) "xml")) ||
                (xmlStrEqual(prop, (const xmlChar *) "html")) ||
                (xmlStrEqual(prop, (const xmlChar *) "text"))) {
                style->method = prop;
            } else {
                xsltTransformError(ctxt, NULL, inst,
                                 "invalid value for method: %s\n", prop);
                if (style != NULL) style->warnings++;
            }
        } else {
            style->method = prop;
            style->methodURI = xmlStrdup(URI);
        }
    }
    prop = xsltEvalAttrValueTemplate(ctxt, inst,
                                     (const xmlChar *)
                                     "doctype-system", NULL);
    if (prop != NULL) {
        if (style->doctypeSystem != NULL)
            xmlFree(style->doctypeSystem);
        style->doctypeSystem = prop;
    }
    prop = xsltEvalAttrValueTemplate(ctxt, inst,
                                     (const xmlChar *)
                                     "doctype-public", NULL);
    if (prop != NULL) {
        if (style->doctypePublic != NULL)
            xmlFree(style->doctypePublic);
        style->doctypePublic = prop;
    }
    prop = xsltEvalAttrValueTemplate(ctxt, inst,
                                     (const xmlChar *) "standalone",
                                     NULL);
    if (prop != NULL) {
        if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
            style->standalone = 1;
        } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
            style->standalone = 0;
        } else {
            xsltTransformError(ctxt, NULL, inst,
                             "invalid value for standalone: %s\n",
                             prop);
            if (style != NULL) style->warnings++;
        }
        xmlFree(prop);
    }

    prop = xsltEvalAttrValueTemplate(ctxt, inst,
                                     (const xmlChar *) "indent",
                                     NULL);
    if (prop != NULL) {
        if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
            style->indent = 1;
        } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
            style->indent = 0;
        } else {
            xsltTransformError(ctxt, NULL, inst,
                             "invalid value for indent: %s\n", prop);
            if (style != NULL) style->warnings++;
        }
        xmlFree(prop);
    }

    prop = xsltEvalAttrValueTemplate(ctxt, inst,
                                     (const xmlChar *)
                                     "omit-xml-declaration",
                                     NULL);
    if (prop != NULL) {
        if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
            style->omitXmlDeclaration = 1;
        } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
            style->omitXmlDeclaration = 0;
        } else {
            xsltTransformError(ctxt, NULL, inst,
                             "invalid value for omit-xml-declaration: %s\n",
                             prop);
            if (style != NULL) style->warnings++;
        }
        xmlFree(prop);
    }

    elements = xsltEvalAttrValueTemplate(ctxt, inst,
                                         (const xmlChar *)
                                         "cdata-section-elements",
                                         NULL);
    if (elements != NULL) {
        if (style->stripSpaces == NULL)
            style->stripSpaces = xmlHashCreate(10);
        if (style->stripSpaces == NULL)
            return;

        element = elements;
        while (*element != 0) {
            while (IS_BLANK_CH(*element))
                element++;
            if (*element == 0)
                break;
            end = element;
            while ((*end != 0) && (!IS_BLANK_CH(*end)))
                end++;
            element = xmlStrndup(element, end - element);
            if (element) {
                const xmlChar *URI;

#ifdef WITH_XSLT_DEBUG_PARSING
                xsltGenericDebug(xsltGenericDebugContext,
                                 "add cdata section output element %s\n",
                                 element);
#endif
                URI = xsltGetQNameURI(inst, &element);

                xmlHashAddEntry2(style->stripSpaces, element, URI,
                                (xmlChar *) "cdata");
                xmlFree(element);
            }
            element = end;
        }
        xmlFree(elements);
    }

    /*
     * Create a new document tree and process the element template
     */
    XSLT_GET_IMPORT_PTR(method, style, method)
    XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
    XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
    XSLT_GET_IMPORT_PTR(version, style, version)
    XSLT_GET_IMPORT_PTR(encoding, style, encoding)

    if ((method != NULL) &&
        (!xmlStrEqual(method, (const xmlChar *) "xml"))) {
        if (xmlStrEqual(method, (const xmlChar *) "html")) {
            ctxt->type = XSLT_OUTPUT_HTML;
            if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
                res = htmlNewDoc(doctypeSystem, doctypePublic);
            else {
                if (version != NULL) {
#ifdef XSLT_GENERATE_HTML_DOCTYPE
                    xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
#endif
                }
                res = htmlNewDocNoDtD(doctypeSystem, doctypePublic);
            }
            if (res == NULL)
                goto error;
            res->dict = ctxt->dict;
            xmlDictReference(res->dict);
        } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
            xsltTransformError(ctxt, NULL, inst,
             "xsltDocumentElem: unsupported method xhtml\n",
                             style->method);
            ctxt->type = XSLT_OUTPUT_HTML;
            res = htmlNewDocNoDtD(doctypeSystem, doctypePublic);
            if (res == NULL)
                goto error;
            res->dict = ctxt->dict;
            xmlDictReference(res->dict);
        } else if (xmlStrEqual(method, (const xmlChar *) "text")) {
            ctxt->type = XSLT_OUTPUT_TEXT;
            res = xmlNewDoc(style->version);
            if (res == NULL)
                goto error;
            res->dict = ctxt->dict;
            xmlDictReference(res->dict);
#ifdef WITH_XSLT_DEBUG
            xsltGenericDebug(xsltGenericDebugContext,
                     "reusing transformation dict for output\n");
#endif
        } else {
            xsltTransformError(ctxt, NULL, inst,
                             "xsltDocumentElem: unsupported method %s\n",
                             style->method);
            goto error;
        }
    } else {
        ctxt->type = XSLT_OUTPUT_XML;
        res = xmlNewDoc(style->version);
        if (res == NULL)
            goto error;
        res->dict = ctxt->dict;
        xmlDictReference(res->dict);
#ifdef WITH_XSLT_DEBUG
        xsltGenericDebug(xsltGenericDebugContext,
                     "reusing transformation dict for output\n");
#endif
    }
    res->charset = XML_CHAR_ENCODING_UTF8;
    if (encoding != NULL)
        res->encoding = xmlStrdup(encoding);
    ctxt->output = res;
    ctxt->insert = (xmlNodePtr) res;
    xsltApplySequenceConstructor(ctxt, node, inst->children, NULL);

    /*
     * Do some post processing work depending on the generated output
     */
    root = xmlDocGetRootElement(res);
    if (root != NULL) {
        const xmlChar *doctype = NULL;

        if ((root->ns != NULL) && (root->ns->prefix != NULL))
            doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name);
        if (doctype == NULL)
            doctype = root->name;

        /*
         * Apply the default selection of the method
         */
        if ((method == NULL) &&
            (root->ns == NULL) &&
            (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) {
            xmlNodePtr tmp;

            tmp = res->children;
            while ((tmp != NULL) && (tmp != root)) {
                if (tmp->type == XML_ELEMENT_NODE)
                    break;
                if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp)))
                    break;
                tmp = tmp->next;
            }
            if (tmp == root) {
                ctxt->type = XSLT_OUTPUT_HTML;
                res->type = XML_HTML_DOCUMENT_NODE;
                if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
                    res->intSubset = xmlCreateIntSubset(res, doctype,
                                                        doctypePublic,
                                                        doctypeSystem);
#ifdef XSLT_GENERATE_HTML_DOCTYPE
                } else if (version != NULL) {
                    xsltGetHTMLIDs(version, &doctypePublic,
                                   &doctypeSystem);
                    if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
                        res->intSubset =
                            xmlCreateIntSubset(res, doctype,
                                               doctypePublic,
                                               doctypeSystem);
#endif
                }
            }

        }
        if (ctxt->type == XSLT_OUTPUT_XML) {
            XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
                XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
                if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
                res->intSubset = xmlCreateIntSubset(res, doctype,
                                                    doctypePublic,
                                                    doctypeSystem);
        }
    }

    /*
     * Save the result
     */
    ret = xsltSaveResultToFilename((const char *) filename,
                                   res, style, 0);
    if (ret < 0) {
        xsltTransformError(ctxt, NULL, inst,
                         "xsltDocumentElem: unable to save to %s\n",
                         filename);
        ctxt->state = XSLT_STATE_ERROR;
#ifdef WITH_XSLT_DEBUG_EXTRA
    } else {
        xsltGenericDebug(xsltGenericDebugContext,
                         "Wrote %d bytes to %s\n", ret, filename);
#endif
    }

  error:
    ctxt->output = oldOutput;
    ctxt->insert = oldInsert;
    ctxt->type = oldType;
    ctxt->outputFile = oldOutputFile;
    if (URL != NULL)
        xmlFree(URL);
    if (filename != NULL)
        xmlFree(filename);
    if (style != NULL)
        xsltFreeStylesheet(style);
    if (res != NULL)
        xmlFreeDoc(res);
}

/************************************************************************
 *                                                                      *
 *              Most of the XSLT-1.0 transformations                    *
 *                                                                      *
 ************************************************************************/

/**
 * xsltSort:
 * @ctxt:  a XSLT process context
 * @node:  the node in the source tree.
 * @inst:  the xslt sort node
 * @comp:  precomputed information
 *
 * function attached to xsl:sort nodes, but this should not be
 * called directly
 */
void
xsltSort(xsltTransformContextPtr ctxt,
        xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst,
        xsltStylePreCompPtr comp) {
    if (comp == NULL) {
        xsltTransformError(ctxt, NULL, inst,
             "xsl:sort : compilation failed\n");
        return;
    }
    xsltTransformError(ctxt, NULL, inst,
         "xsl:sort : improper use this should not be reached\n");
}

/**
 * xsltCopy:
 * @ctxt:  an XSLT process context
 * @node:  the node in the source tree
 * @inst:  the element node of the XSLT-copy instruction
 * @castedComp:  computed information of the XSLT-copy instruction
 *
 * Execute the XSLT-copy instruction on the source node.
 */
void
xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node,
         xmlNodePtr inst, xsltStylePreCompPtr castedComp)
{
#ifdef XSLT_REFACTORED
    xsltStyleItemCopyPtr comp = (xsltStyleItemCopyPtr) castedComp;
#else
    xsltStylePreCompPtr comp = castedComp;
#endif
    xmlNodePtr copy, oldInsert;

    oldInsert = ctxt->insert;
    if (ctxt->insert != NULL) {
        switch (node->type) {
            case XML_TEXT_NODE:
            case XML_CDATA_SECTION_NODE:
                /*
                 * This text comes from the stylesheet
                 * For stylesheets, the set of whitespace-preserving
                 * element names consists of just xsl:text.
                 */
#ifdef WITH_XSLT_DEBUG_PROCESS
                if (node->type == XML_CDATA_SECTION_NODE) {
                    XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
                         "xsltCopy: CDATA text %s\n", node->content));
                } else {
                    XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
                         "xsltCopy: text %s\n", node->content));
                }
#endif
                xsltCopyText(ctxt, ctxt->insert, node, 0);
                break;
            case XML_DOCUMENT_NODE:
            case XML_HTML_DOCUMENT_NODE:
                break;
            case XML_ELEMENT_NODE:
                /*
                * REVISIT NOTE: The "fake" is a doc-node, not an element node.
                * REMOVED:
                *   if (xmlStrEqual(node->name, BAD_CAST " fake node libxslt"))
                *    return;
                */

#ifdef WITH_XSLT_DEBUG_PROCESS
                XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
                                 "xsltCopy: node %s\n", node->name));
#endif
                copy = xsltShallowCopyElem(ctxt, node, ctxt->insert, 0);
                ctxt->insert = copy;
                if (comp->use != NULL) {
                    xsltApplyAttributeSet(ctxt, node, inst, comp->use);
                }
                break;
            case XML_ATTRIBUTE_NODE: {
#ifdef WITH_XSLT_DEBUG_PROCESS
                XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
                                 "xsltCopy: attribute %s\n", node->name));
#endif
                /*
                * REVISIT: We could also raise an error if the parent is not
                * an element node.
                * OPTIMIZE TODO: Can we set the value/children of the
                * attribute without an intermediate copy of the string value?
                */
                xsltShallowCopyAttr(ctxt, inst, ctxt->insert, (xmlAttrPtr) node);
                break;
            }
            case XML_PI_NODE:
#ifdef WITH_XSLT_DEBUG_PROCESS
                XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
                                 "xsltCopy: PI %s\n", node->name));
#endif
                copy = xmlNewDocPI(ctxt->insert->doc, node->name,
                                   node->content);
                copy = xsltAddChild(ctxt->insert, copy);
                break;
            case XML_COMMENT_NODE:
#ifdef WITH_XSLT_DEBUG_PROCESS
                XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
                                 "xsltCopy: comment\n"));
#endif
                copy = xmlNewComment(node->content);
                copy = xsltAddChild(ctxt->insert, copy);
                break;
            case XML_NAMESPACE_DECL:
#ifdef WITH_XSLT_DEBUG_PROCESS
                XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
                                 "xsltCopy: namespace declaration\n"));
#endif
                xsltShallowCopyNsNode(ctxt, inst, ctxt->insert, (xmlNsPtr)node);
                break;
            default:
                break;

        }
    }

    switch (node->type) {
        case XML_DOCUMENT_NODE:
        case XML_HTML_DOCUMENT_NODE:
        case XML_ELEMENT_NODE:
            xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children,
                NULL);
            break;
        default:
            break;
    }
    ctxt->insert = oldInsert;
}

/**
 * xsltText:
 * @ctxt:  a XSLT process context
 * @node:  the node in the source tree.
 * @inst:  the xslt text node
 * @comp:  precomputed information
 *
 * Process the xslt text node on the source node
 */
void
xsltText(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED,
            xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
    if ((inst->children != NULL) && (comp != NULL)) {
        xmlNodePtr text = inst->children;
        xmlNodePtr copy;

        while (text != NULL) {
            if ((text->type != XML_TEXT_NODE) &&
                 (text->type != XML_CDATA_SECTION_NODE)) {
                xsltTransformError(ctxt, NULL, inst,
                                 "xsl:text content problem\n");
                break;
            }
            copy = xmlNewDocText(ctxt->output, text->content);
            if (text->type != XML_CDATA_SECTION_NODE) {
#ifdef WITH_XSLT_DEBUG_PARSING
                xsltGenericDebug(xsltGenericDebugContext,
                     "Disable escaping: %s\n", text->content);
#endif
                copy->name = xmlStringTextNoenc;
            }
            copy = xsltAddChild(ctxt->insert, copy);
            text = text->next;
        }
    }
}

/**
 * xsltElement:
 * @ctxt:  a XSLT process context
 * @node:  the node in the source tree.
 * @inst:  the xslt element node
 * @castedComp:  precomputed information
 *
 * Process the xslt element node on the source node
 */
void
xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node,
            xmlNodePtr inst, xsltStylePreCompPtr castedComp) {
#ifdef XSLT_REFACTORED
    xsltStyleItemElementPtr comp = (xsltStyleItemElementPtr) castedComp;
#else
    xsltStylePreCompPtr comp = castedComp;
#endif
    xmlChar *prop = NULL;
    const xmlChar *name, *prefix = NULL, *nsName = NULL;
    xmlNodePtr copy;
    xmlNodePtr oldInsert;

    if (ctxt->insert == NULL)
        return;

    /*
    * A comp->has_name == 0 indicates that we need to skip this instruction,
    * since it was evaluated to be invalid already during compilation.
    */
    if (!comp->has_name)
        return;

    /*
     * stack and saves
     */
    oldInsert = ctxt->insert;

    if (comp->name == NULL) {
        /* TODO: fix attr acquisition wrt to the XSLT namespace */
        prop = xsltEvalAttrValueTemplate(ctxt, inst,
            (const xmlChar *) "name", XSLT_NAMESPACE);
        if (prop == NULL) {
            xsltTransformError(ctxt, NULL, inst,
                "xsl:element: The attribute 'name' is missing.\n");
            goto error;
        }
        if (xmlValidateQName(prop, 0)) {
            xsltTransformError(ctxt, NULL, inst,
                "xsl:element: The effective name '%s' is not a "
                "valid QName.\n", prop);
            /* we fall through to catch any further errors, if possible */
        }
        name = xsltSplitQName(ctxt->dict, prop, &prefix);
        xmlFree(prop);
        if ((prefix != NULL) &&
            (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3)))
        {
            /*
            * TODO: Should we really disallow an "xml" prefix?
            */
            goto error;
        }
    } else {
        /*
        * The "name" value was static.
        */
#ifdef XSLT_REFACTORED
        prefix = comp->nsPrefix;
        name = comp->name;
#else
        name = xsltSplitQName(ctxt->dict, comp->name, &prefix);
#endif
    }

    /*
     * Create the new element
     */
    if (ctxt->output->dict == ctxt->dict) {
        copy = xmlNewDocNodeEatName(ctxt->output, NULL, (xmlChar *)name, NULL);
    } else {
        copy = xmlNewDocNode(ctxt->output, NULL, (xmlChar *)name, NULL);
    }
    if (copy == NULL) {
        xsltTransformError(ctxt, NULL, inst,
            "xsl:element : creation of %s failed\n", name);
        return;
    }
    copy = xsltAddChild(ctxt->insert, copy);

    /*
    * Namespace
    * ---------
    */
    if (comp->has_ns) {
        if (comp->ns != NULL) {
            /*
            * No AVT; just plain text for the namespace name.
            */
            if (comp->ns[0] != 0)
                nsName = comp->ns;
        } else {
            xmlChar *tmpNsName;
            /*
            * Eval the AVT.
            */
            /* TODO: check attr acquisition wrt to the XSLT namespace */
            tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst,
                (const xmlChar *) "namespace", XSLT_NAMESPACE);
            /*
            * SPEC XSLT 1.0:
            *  "If the string is empty, then the expanded-name of the
            *  attribute has a null namespace URI."
            */
            if ((tmpNsName != NULL) && (tmpNsName[0] != 0))
                nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1);
            xmlFree(tmpNsName);
        };
    } else {
        xmlNsPtr ns;
        /*
        * SPEC XSLT 1.0:
        *  "If the namespace attribute is not present, then the QName is
        *  expanded into an expanded-name using the namespace declarations
        *  in effect for the xsl:element element, including any default
        *  namespace declaration.
        */
        ns = xmlSearchNs(inst->doc, inst, prefix);
        if (ns == NULL) {
            /*
            * TODO: Check this in the compilation layer in case it's a
            * static value.
            */
            if (prefix != NULL) {
                xsltTransformError(ctxt, NULL, inst,
                    "xsl:element: The QName '%s:%s' has no "
                    "namespace binding in scope in the stylesheet; "
                    "this is an error, since the namespace was not "
                    "specified by the instruction itself.\n", prefix, name);
            }
        } else
            nsName = ns->href;
    }
    /*
    * Find/create a matching ns-decl in the result tree.
    */
    if (nsName != NULL) {
        copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix, copy);
    } else if ((copy->parent != NULL) &&
        (copy->parent->type == XML_ELEMENT_NODE) &&
        (copy->parent->ns != NULL))
    {
        /*
        * "Undeclare" the default namespace.
        */
        xsltGetSpecialNamespace(ctxt, inst, NULL, NULL, copy);
    }

    ctxt->insert = copy;

    if (comp->has_use) {
        if (comp->use != NULL) {
            xsltApplyAttributeSet(ctxt, node, inst, comp->use);
        } else {
            xmlChar *attrSets = NULL;
            /*
            * BUG TODO: use-attribute-sets is not a value template.
            *  use-attribute-sets = qnames
            */
            attrSets = xsltEvalAttrValueTemplate(ctxt, inst,
                (const xmlChar *)"use-attribute-sets", NULL);
            if (attrSets != NULL) {
                xsltApplyAttributeSet(ctxt, node, inst, attrSets);
                xmlFree(attrSets);
            }
        }
    }
    /*
    * Instantiate the sequence constructor.
    */
    if (inst->children != NULL)
        xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children,
            NULL);

error:
    ctxt->insert = oldInsert;
    return;
}


/**
 * xsltComment:
 * @ctxt:  a XSLT process context
 * @node:  the node in the source tree.
 * @inst:  the xslt comment node
 * @comp:  precomputed information
 *
 * Process the xslt comment node on the source node
 */
void
xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node,
                   xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
    xmlChar *value = NULL;
    xmlNodePtr commentNode;
    int len;

    value = xsltEvalTemplateString(ctxt, node, inst);
    /* TODO: use or generate the compiled form */
    len = xmlStrlen(value);
    if (len > 0) {
        if ((value[len-1] == '-') ||
            (xmlStrstr(value, BAD_CAST "--"))) {
            xsltTransformError(ctxt, NULL, inst,
                    "xsl:comment : '--' or ending '-' not allowed in comment\n");
            /* fall through to try to catch further errors */
        }
    }
#ifdef WITH_XSLT_DEBUG_PROCESS
    if (value == NULL) {
        XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext,
             "xsltComment: empty\n"));
    } else {
        XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext,
             "xsltComment: content %s\n", value));
    }
#endif

    commentNode = xmlNewComment(value);
    commentNode = xsltAddChild(ctxt->insert, commentNode);

    if (value != NULL)
        xmlFree(value);
}

/**
 * xsltProcessingInstruction:
 * @ctxt:  a XSLT process context
 * @node:  the node in the source tree.
 * @inst:  the xslt processing-instruction node
 * @castedComp:  precomputed information
 *
 * Process the xslt processing-instruction node on the source node
 */
void
xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node,
                   xmlNodePtr inst, xsltStylePreCompPtr castedComp) {
#ifdef XSLT_REFACTORED
    xsltStyleItemPIPtr comp = (xsltStyleItemPIPtr) castedComp;
#else
    xsltStylePreCompPtr comp = castedComp;
#endif
    const xmlChar *name;
    xmlChar *value = NULL;
    xmlNodePtr pi;


    if (ctxt->insert == NULL)
        return;
    if (comp->has_name == 0)
        return;
    if (comp->name == NULL) {
        name = xsltEvalAttrValueTemplate(ctxt, inst,
                            (const xmlChar *)"name", NULL);
        if (name == NULL) {
            xsltTransformError(ctxt, NULL, inst,
                 "xsl:processing-instruction : name is missing\n");
            goto error;
        }
    } else {
        name = comp->name;
    }
    /* TODO: check that it's both an an NCName and a PITarget. */


    value = xsltEvalTemplateString(ctxt, node, inst);
    if (xmlStrstr(value, BAD_CAST "?>") != NULL) {
        xsltTransformError(ctxt, NULL, inst,
             "xsl:processing-instruction: '?>' not allowed within PI content\n");
        goto error;
    }
#ifdef WITH_XSLT_DEBUG_PROCESS
    if (value == NULL) {
        XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext,
             "xsltProcessingInstruction: %s empty\n", name));
    } else {
        XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext,
             "xsltProcessingInstruction: %s content %s\n", name, value));
    }
#endif

    pi = xmlNewDocPI(ctxt->insert->doc, name, value);
    pi = xsltAddChild(ctxt->insert, pi);

error:
    if ((name != NULL) && (name != comp->name))
        xmlFree((xmlChar *) name);
    if (value != NULL)
        xmlFree(value);
}

/**
 * xsltCopyOf:
 * @ctxt:  an XSLT transformation context
 * @node:  the current node in the source tree
 * @inst:  the element node of the XSLT copy-of instruction
 * @castedComp:  precomputed information of the XSLT copy-of instruction
 *
 * Process the XSLT copy-of instruction.
 */
void
xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
                   xmlNodePtr inst, xsltStylePreCompPtr castedComp) {
#ifdef XSLT_REFACTORED
    xsltStyleItemCopyOfPtr comp = (xsltStyleItemCopyOfPtr) castedComp;
#else
    xsltStylePreCompPtr comp = castedComp;
#endif
    xmlXPathObjectPtr res = NULL;
    xmlNodeSetPtr list = NULL;
    int i;
    xmlDocPtr oldXPContextDoc;
    xmlNsPtr *oldXPNamespaces;
    xmlNodePtr oldXPContextNode;
    int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
    xmlXPathContextPtr xpctxt;

    if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
        return;
    if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
        xsltTransformError(ctxt, NULL, inst,
             "xsl:copy-of : compilation failed\n");
        return;
    }

     /*
    * SPEC XSLT 1.0:
    *  "The xsl:copy-of element can be used to insert a result tree
    *  fragment into the result tree, without first converting it to
    *  a string as xsl:value-of does (see [7.6.1 Generating Text with
    *  xsl:value-of]). The required select attribute contains an
    *  expression. When the result of evaluating the expression is a
    *  result tree fragment, the complete fragment is copied into the
    *  result tree. When the result is a node-set, all the nodes in the
    *  set are copied in document order into the result tree; copying
    *  an element node copies the attribute nodes, namespace nodes and
    *  children of the element node as well as the element node itself;
    *  a root node is copied by copying its children. When the result
    *  is neither a node-set nor a result tree fragment, the result is
    *  converted to a string and then inserted into the result tree,
    *  as with xsl:value-of.
    */

#ifdef WITH_XSLT_DEBUG_PROCESS
    XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
         "xsltCopyOf: select %s\n", comp->select));
#endif

    /*
    * Evaluate the "select" expression.
    */
    xpctxt = ctxt->xpathCtxt;
    oldXPContextDoc = xpctxt->doc;
    oldXPContextNode = xpctxt->node;
    oldXPProximityPosition = xpctxt->proximityPosition;
    oldXPContextSize = xpctxt->contextSize;
    oldXPNsNr = xpctxt->nsNr;
    oldXPNamespaces = xpctxt->namespaces;

    xpctxt->node = node;
    if (comp != NULL) {

#ifdef XSLT_REFACTORED
        if (comp->inScopeNs != NULL) {
            xpctxt->namespaces = comp->inScopeNs->list;
            xpctxt->nsNr = comp->inScopeNs->xpathNumber;
        } else {
            xpctxt->namespaces = NULL;
            xpctxt->nsNr = 0;
        }
#else
        xpctxt->namespaces = comp->nsList;
        xpctxt->nsNr = comp->nsNr;
#endif
    } else {
        xpctxt->namespaces = NULL;
        xpctxt->nsNr = 0;
    }

    res = xmlXPathCompiledEval(comp->comp, xpctxt);

    xpctxt->doc = oldXPContextDoc;
    xpctxt->node = oldXPContextNode;
    xpctxt->contextSize = oldXPContextSize;
    xpctxt->proximityPosition = oldXPProximityPosition;
    xpctxt->nsNr = oldXPNsNr;
    xpctxt->namespaces = oldXPNamespaces;

    if (res != NULL) {
        if (res->type == XPATH_NODESET) {
            /*
            * Node-set
            * --------
            */
#ifdef WITH_XSLT_DEBUG_PROCESS
            XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
                 "xsltCopyOf: result is a node set\n"));
#endif
            list = res->nodesetval;
            if (list != NULL) {
                xmlNodePtr cur;
                /*
                * The list is already sorted in document order by XPath.
                * Append everything in this order under ctxt->insert.
                */
                for (i = 0;i < list->nodeNr;i++) {
                    cur = list->nodeTab[i];
                    if (cur == NULL)
                        continue;
                    if ((cur->type == XML_DOCUMENT_NODE) ||
                        (cur->type == XML_HTML_DOCUMENT_NODE))
                    {
                        xsltCopyTreeList(ctxt, inst,
                            cur->children, ctxt->insert, 0, 0);
                    } else if (cur->type == XML_ATTRIBUTE_NODE) {
                        xsltShallowCopyAttr(ctxt, inst,
                            ctxt->insert, (xmlAttrPtr) cur);
                    } else {
                        xsltCopyTreeInternal(ctxt, inst,
                            cur, ctxt->insert, 0, 0);
                    }
                }
            }
        } else if (res->type == XPATH_XSLT_TREE) {
            /*
            * Result tree fragment
            * --------------------
            * E.g. via <xsl:variable ...><foo/></xsl:variable>
            * Note that the root node of such trees is an xmlDocPtr in Libxslt.
            */
#ifdef WITH_XSLT_DEBUG_PROCESS
            XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
                 "xsltCopyOf: result is a result tree fragment\n"));
#endif
            list = res->nodesetval;
            if ((list != NULL) && (list->nodeTab != NULL) &&
                (list->nodeTab[0] != NULL) &&
                (IS_XSLT_REAL_NODE(list->nodeTab[0])))
            {
                xsltCopyTreeList(ctxt, inst,
                    list->nodeTab[0]->children, ctxt->insert, 0, 0);
            }
        } else {
            xmlChar *value = NULL;
            /*
            * Convert to a string.
            */
            value = xmlXPathCastToString(res);
            if (value == NULL) {
                xsltTransformError(ctxt, NULL, inst,
                    "Internal error in xsltCopyOf(): "
                    "failed to cast an XPath object to string.\n");
                ctxt->state = XSLT_STATE_STOPPED;
            } else {
                if (value[0] != 0) {
                    /*
                    * Append content as text node.
                    */
                    xsltCopyTextString(ctxt, ctxt->insert, value, 0);
                }
                xmlFree(value);

#ifdef WITH_XSLT_DEBUG_PROCESS
                XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
                    "xsltCopyOf: result %s\n", res->stringval));
#endif
            }
        }
    } else {
        ctxt->state = XSLT_STATE_STOPPED;
    }

    if (res != NULL)
        xmlXPathFreeObject(res);
}

/**
 * xsltValueOf:
 * @ctxt:  a XSLT process context
 * @node:  the node in the source tree.
 * @inst:  the xslt value-of node
 * @castedComp:  precomputed information
 *
 * Process the xslt value-of node on the source node
 */
void
xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
                   xmlNodePtr inst, xsltStylePreCompPtr castedComp)
{
#ifdef XSLT_REFACTORED
    xsltStyleItemValueOfPtr comp = (xsltStyleItemValueOfPtr) castedComp;
#else
    xsltStylePreCompPtr comp = castedComp;
#endif
    xmlXPathObjectPtr res = NULL;
    xmlNodePtr copy = NULL;
    xmlChar *value = NULL;
    xmlDocPtr oldXPContextDoc;
    xmlNsPtr *oldXPNamespaces;
    xmlNodePtr oldXPContextNode;
    int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
    xmlXPathContextPtr xpctxt;

    if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
        return;

    if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
        xsltTransformError(ctxt, NULL, inst,
            "Internal error in xsltValueOf(): "
            "The XSLT 'value-of' instruction was not compiled.\n");
        return;
    }

#ifdef WITH_XSLT_DEBUG_PROCESS
    XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext,
         "xsltValueOf: select %s\n", comp->select));
#endif

    xpctxt = ctxt->xpathCtxt;
    oldXPContextDoc = xpctxt->doc;
    oldXPContextNode = xpctxt->node;
    oldXPProximityPosition = xpctxt->proximityPosition;
    oldXPContextSize = xpctxt->contextSize;
    oldXPNsNr = xpctxt->nsNr;
    oldXPNamespaces = xpctxt->namespaces;

    xpctxt->node = node;
    if (comp != NULL) {

#ifdef XSLT_REFACTORED
        if (comp->inScopeNs != NULL) {
            xpctxt->namespaces = comp->inScopeNs->list;
            xpctxt->nsNr = comp->inScopeNs->xpathNumber;
        } else {
            xpctxt->namespaces = NULL;
            xpctxt->nsNr = 0;
        }
#else
        xpctxt->namespaces = comp->nsList;
        xpctxt->nsNr = comp->nsNr;
#endif
    } else {
        xpctxt->namespaces = NULL;
        xpctxt->nsNr = 0;
    }

    res = xmlXPathCompiledEval(comp->comp, xpctxt);

    xpctxt->doc = oldXPContextDoc;
    xpctxt->node = oldXPContextNode;
    xpctxt->contextSize = oldXPContextSize;
    xpctxt->proximityPosition = oldXPProximityPosition;
    xpctxt->nsNr = oldXPNsNr;
    xpctxt->namespaces = oldXPNamespaces;

    /*
    * Cast the XPath object to string.
    */
    if (res != NULL) {
        value = xmlXPathCastToString(res);
        if (value == NULL) {
            xsltTransformError(ctxt, NULL, inst,
                "Internal error in xsltValueOf(): "
                "failed to cast an XPath object to string.\n");
            ctxt->state = XSLT_STATE_STOPPED;
            goto error;
        }
        if (value[0] != 0) {
            copy = xsltCopyTextString(ctxt,
                ctxt->insert, value, comp->noescape);
        }
    } else {
        xsltTransformError(ctxt, NULL, inst,
            "XPath evaluation returned no result.\n");
        ctxt->state = XSLT_STATE_STOPPED;
        goto error;
    }

#ifdef WITH_XSLT_DEBUG_PROCESS
    if (value) {
        XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext,
             "xsltValueOf: result '%s'\n", value));
    }
#endif

error:
    if (value != NULL)
        xmlFree(value);
    if (res != NULL)
        xmlXPathFreeObject(res);
}

/**
 * xsltNumber:
 * @ctxt:  a XSLT process context
 * @node:  the node in the source tree.
 * @inst:  the xslt number node
 * @castedComp:  precomputed information
 *
 * Process the xslt number node on the source node
 */
void
xsltNumber(xsltTransformContextPtr ctxt, xmlNodePtr node,
           xmlNodePtr inst, xsltStylePreCompPtr castedComp)
{
#ifdef XSLT_REFACTORED
    xsltStyleItemNumberPtr comp = (xsltStyleItemNumberPtr) castedComp;
#else
    xsltStylePreCompPtr comp = castedComp;
#endif
    if (comp == NULL) {
        xsltTransformError(ctxt, NULL, inst,
             "xsl:number : compilation failed\n");
        return;
    }

    if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
        return;

    comp->numdata.doc = inst->doc;
    comp->numdata.node = inst;

    xsltNumberFormat(ctxt, &comp->numdata, node);
}

/**
 * xsltApplyImports:
 * @ctxt:  an XSLT transformation context
 * @contextNode:  the current node in the source tree.
 * @inst:  the element node of the XSLT 'apply-imports' instruction
 * @comp:  the compiled instruction
 *
 * Process the XSLT apply-imports element.
 */
void
xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
                 xmlNodePtr inst,
                 xsltStylePreCompPtr comp ATTRIBUTE_UNUSED)
{
    xsltTemplatePtr templ;

    if ((ctxt == NULL) || (inst == NULL))
        return;

    if (comp == NULL) {
        xsltTransformError(ctxt, NULL, inst,
            "Internal error in xsltApplyImports(): "
            "The XSLT 'apply-imports' instruction was not compiled.\n");
        return;
    }
    /*
    * NOTE that ctxt->currentTemplateRule and ctxt->templ is not the
    * same; the former is the "Current Template Rule" as defined by the
    * XSLT spec, the latter is simply the template struct being
    * currently processed.
    */
    if (ctxt->currentTemplateRule == NULL) {
        /*
        * SPEC XSLT 2.0:
        * "[ERR XTDE0560] It is a non-recoverable dynamic error if
        *  xsl:apply-imports or xsl:next-match is evaluated when the
        *  current template rule is null."
        */
        xsltTransformError(ctxt, NULL, inst,
             "It is an error to call 'apply-imports' "
             "when there's no current template rule.\n");
        return;
    }
    /*
    * TODO: Check if this is correct.
    */
    templ = xsltGetTemplate(ctxt, contextNode,
        ctxt->currentTemplateRule->style);

    if (templ != NULL) {
        xsltTemplatePtr oldCurTemplRule = ctxt->currentTemplateRule;
        /*
        * Set the current template rule.
        */
        ctxt->currentTemplateRule = templ;
        /*
        * URGENT TODO: Need xsl:with-param be handled somehow here?
        */
        xsltApplyXSLTTemplate(ctxt, contextNode, templ->content,
            templ, NULL);

        ctxt->currentTemplateRule = oldCurTemplRule;
    }
}

/**
 * xsltCallTemplate:
 * @ctxt:  a XSLT transformation context
 * @node:  the "current node" in the source tree
 * @inst:  the XSLT 'call-template' instruction
 * @castedComp:  the compiled information of the instruction
 *
 * Processes the XSLT call-template instruction on the source node.
 */
void
xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
                   xmlNodePtr inst, xsltStylePreCompPtr castedComp)
{
#ifdef XSLT_REFACTORED
    xsltStyleItemCallTemplatePtr comp =
        (xsltStyleItemCallTemplatePtr) castedComp;
#else
    xsltStylePreCompPtr comp = castedComp;
#endif
    xsltStackElemPtr withParams = NULL;

    if (ctxt->insert == NULL)
        return;
    if (comp == NULL) {
        xsltTransformError(ctxt, NULL, inst,
             "The XSLT 'call-template' instruction was not compiled.\n");
        return;
    }

    /*
     * The template must have been precomputed
     */
    if (comp->templ == NULL) {
        comp->templ = xsltFindTemplate(ctxt, comp->name, comp->ns);
        if (comp->templ == NULL) {
            if (comp->ns != NULL) {
                xsltTransformError(ctxt, NULL, inst,
                        "The called template '{%s}%s' was not found.\n",
                        comp->ns, comp->name);
            } else {
                xsltTransformError(ctxt, NULL, inst,
                        "The called template '%s' was not found.\n",
                        comp->name);
            }
            return;
        }
    }

#ifdef WITH_XSLT_DEBUG_PROCESS
    if ((comp != NULL) && (comp->name != NULL))
        XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
                         "call-template: name %s\n", comp->name));
#endif

    if (inst->children) {
        xmlNodePtr cur;
        xsltStackElemPtr param;

        cur = inst->children;
        while (cur != NULL) {
#ifdef WITH_DEBUGGER
            if (ctxt->debugStatus != XSLT_DEBUG_NONE)
                xslHandleDebugger(cur, node, comp->templ, ctxt);
#endif
            if (ctxt->state == XSLT_STATE_STOPPED) break;
            /*
            * TODO: The "with-param"s could be part of the "call-template"
            *   structure. Avoid to "search" for params dynamically
            *   in the XML tree every time.
            */
            if (IS_XSLT_ELEM(cur)) {
                if (IS_XSLT_NAME(cur, "with-param")) {
                    param = xsltParseStylesheetCallerParam(ctxt, cur);
                    if (param != NULL) {
                        param->next = withParams;
                        withParams = param;
                    }
                } else {
                    xsltGenericError(xsltGenericErrorContext,
                        "xsl:call-template: misplaced xsl:%s\n", cur->name);
                }
            } else {
                xsltGenericError(xsltGenericErrorContext,
                    "xsl:call-template: misplaced %s element\n", cur->name);
            }
            cur = cur->next;
        }
    }
    /*
     * Create a new frame using the params first
     */
    xsltApplyXSLTTemplate(ctxt, node, comp->templ->content, comp->templ,
        withParams);
    if (withParams != NULL)
        xsltFreeStackElemList(withParams);

#ifdef WITH_XSLT_DEBUG_PROCESS
    if ((comp != NULL) && (comp->name != NULL))
        XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
                         "call-template returned: name %s\n", comp->name));
#endif
}

/**
 * xsltApplyTemplates:
 * @ctxt:  a XSLT transformation context
 * @node:  the 'current node' in the source tree
 * @inst:  the element node of an XSLT 'apply-templates' instruction
 * @castedComp:  the compiled instruction
 *
 * Processes the XSLT 'apply-templates' instruction on the current node.
 */
void
xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node,
                   xmlNodePtr inst, xsltStylePreCompPtr castedComp)
{
#ifdef XSLT_REFACTORED
    xsltStyleItemApplyTemplatesPtr comp =
        (xsltStyleItemApplyTemplatesPtr) castedComp;
#else
    xsltStylePreCompPtr comp = castedComp;
#endif
    int i;
    xmlNodePtr cur, delNode = NULL, oldContextNode;
    xmlNodeSetPtr list = NULL, oldList;
    xsltStackElemPtr withParams = NULL;
    int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
    const xmlChar *oldMode, *oldModeURI;
    xmlDocPtr oldXPDoc;
    xsltDocumentPtr oldDocInfo;
    xmlXPathContextPtr xpctxt;
    xmlNsPtr *oldXPNamespaces;

    if (comp == NULL) {
        xsltTransformError(ctxt, NULL, inst,
             "xsl:apply-templates : compilation failed\n");
        return;
    }
    if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
        return;

#ifdef WITH_XSLT_DEBUG_PROCESS
    if ((node != NULL) && (node->name != NULL))
        XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
             "xsltApplyTemplates: node: '%s'\n", node->name));
#endif

    xpctxt = ctxt->xpathCtxt;
    /*
    * Save context states.
    */
    oldContextNode = ctxt->node;
    oldMode = ctxt->mode;
    oldModeURI = ctxt->modeURI;
    oldDocInfo = ctxt->document;
    oldList = ctxt->nodeList;

    /*
     * The xpath context size and proximity position, as
     * well as the xpath and context documents, may be changed
     * so we save their initial state and will restore on exit
     */
    oldXPContextSize = xpctxt->contextSize;
    oldXPProximityPosition = xpctxt->proximityPosition;
    oldXPDoc = xpctxt->doc;
    oldXPNsNr = xpctxt->nsNr;
    oldXPNamespaces = xpctxt->namespaces;

    /*
    * Set up contexts.
    */
    ctxt->mode = comp->mode;
    ctxt->modeURI = comp->modeURI;

    if (comp->select != NULL) {
        xmlXPathObjectPtr res = NULL;

        if (comp->comp == NULL) {
            xsltTransformError(ctxt, NULL, inst,
                 "xsl:apply-templates : compilation failed\n");
            goto error;
        }
#ifdef WITH_XSLT_DEBUG_PROCESS
        XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
             "xsltApplyTemplates: select %s\n", comp->select));
#endif

        /*
        * Set up XPath.
        */
        xpctxt->node = node; /* Set the "context node" */
#ifdef XSLT_REFACTORED
        if (comp->inScopeNs != NULL) {
            xpctxt->namespaces = comp->inScopeNs->list;
            xpctxt->nsNr = comp->inScopeNs->xpathNumber;
        } else {
            xpctxt->namespaces = NULL;
            xpctxt->nsNr = 0;
        }
#else
        xpctxt->namespaces = comp->nsList;
        xpctxt->nsNr = comp->nsNr;
#endif
        res = xmlXPathCompiledEval(comp->comp, xpctxt);

        xpctxt->contextSize = oldXPContextSize;
        xpctxt->proximityPosition = oldXPProximityPosition;
        if (res != NULL) {
            if (res->type == XPATH_NODESET) {
                list = res->nodesetval; /* consume the node set */
                res->nodesetval = NULL;
            } else {
                xsltTransformError(ctxt, NULL, inst,
                    "The 'select' expression did not evaluate to a "
                    "node set.\n");
                ctxt->state = XSLT_STATE_STOPPED;
                xmlXPathFreeObject(res);
                goto error;
            }
            xmlXPathFreeObject(res);
            /*
            * Note: An xsl:apply-templates with a 'select' attribute,
            * can change the current source doc.
            */
        } else {
            xsltTransformError(ctxt, NULL, inst,
                "Failed to evaluate the 'select' expression.\n");
            ctxt->state = XSLT_STATE_STOPPED;
            goto error;
        }
        if (list == NULL) {
#ifdef WITH_XSLT_DEBUG_PROCESS
            XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
                "xsltApplyTemplates: select didn't evaluate to a node list\n"));
#endif
            goto exit;
        }
        /*
        *
        * NOTE: Previously a document info (xsltDocument) was
        * created and attached to the Result Tree Fragment.
        * But such a document info is created on demand in
        * xsltKeyFunction() (functions.c), so we need to create
        * it here beforehand.
        * In order to take care of potential keys we need to
        * do some extra work for the case when a Result Tree Fragment
        * is converted into a nodeset (e.g. exslt:node-set()) :
        * We attach a "pseudo-doc" (xsltDocument) to _private.
        * This xsltDocument, together with the keyset, will be freed
        * when the Result Tree Fragment is freed.
        *
        */
#if 0
        if ((ctxt->nbKeys > 0) &&
            (list->nodeNr != 0) &&
            (list->nodeTab[0]->doc != NULL) &&
            XSLT_IS_RES_TREE_FRAG(list->nodeTab[0]->doc))
        {
            /*
            * NOTE that it's also OK if @effectiveDocInfo will be
            * set to NULL.
            */
            isRTF = 1;
            effectiveDocInfo = list->nodeTab[0]->doc->_private;
        }
#endif
    } else {
        /*
         * Build an XPath node set with the children
         */
        list = xmlXPathNodeSetCreate(NULL);
        if (list == NULL)
            goto error;
        cur = node->children;
        while (cur != NULL) {
            switch (cur->type) {
                case XML_TEXT_NODE:
                    if ((IS_BLANK_NODE(cur)) &&
                        (cur->parent != NULL) &&
                        (cur->parent->type == XML_ELEMENT_NODE) &&
                        (ctxt->style->stripSpaces != NULL)) {
                        const xmlChar *val;

                        if (cur->parent->ns != NULL) {
                            val = (const xmlChar *)
                                  xmlHashLookup2(ctxt->style->stripSpaces,
                                                 cur->parent->name,
                                                 cur->parent->ns->href);
                            if (val == NULL) {
                                val = (const xmlChar *)
                                  xmlHashLookup2(ctxt->style->stripSpaces,
                                                 BAD_CAST "*",
                                                 cur->parent->ns->href);
                            }
                        } else {
                            val = (const xmlChar *)
                                  xmlHashLookup2(ctxt->style->stripSpaces,
                                                 cur->parent->name, NULL);
                        }
                        if ((val != NULL) &&
                            (xmlStrEqual(val, (xmlChar *) "strip"))) {
                            delNode = cur;
                            break;
                        }
                    }
                    /* no break on purpose */
                case XML_ELEMENT_NODE:
                case XML_DOCUMENT_NODE:
                case XML_HTML_DOCUMENT_NODE:
                case XML_CDATA_SECTION_NODE:
                case XML_PI_NODE:
                case XML_COMMENT_NODE:
                    xmlXPathNodeSetAddUnique(list, cur);
                    break;
                case XML_DTD_NODE:
                    /* Unlink the DTD, it's still reachable
                     * using doc->intSubset */
                    if (cur->next != NULL)
                        cur->next->prev = cur->prev;
                    if (cur->prev != NULL)
                        cur->prev->next = cur->next;
                    break;
                default:
#ifdef WITH_XSLT_DEBUG_PROCESS
                    XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
                     "xsltApplyTemplates: skipping cur type %d\n",
                                     cur->type));
#endif
                    delNode = cur;
            }
            cur = cur->next;
            if (delNode != NULL) {
#ifdef WITH_XSLT_DEBUG_PROCESS
                XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
                     "xsltApplyTemplates: removing ignorable blank cur\n"));
#endif
                xmlUnlinkNode(delNode);
                xmlFreeNode(delNode);
                delNode = NULL;
            }
        }
    }

#ifdef WITH_XSLT_DEBUG_PROCESS
    if (list != NULL)
    XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
        "xsltApplyTemplates: list of %d nodes\n", list->nodeNr));
#endif

    if ((list == NULL) || (list->nodeNr == 0))
        goto exit;

    /*
    * Set the context's node set and size; this is also needed for
    * for xsltDoSortFunction().
    */
    ctxt->nodeList = list;
    /*
    * Process xsl:with-param and xsl:sort instructions.
    * (The code became so verbose just to avoid the
    *  xmlNodePtr sorts[XSLT_MAX_SORT] if there's no xsl:sort)
    * BUG TODO: We are not using namespaced potentially defined on the
    * xsl:sort or xsl:with-param elements; XPath expression might fail.
    */
    if (inst->children) {
        xsltStackElemPtr param;

        cur = inst->children;
        while (cur) {

#ifdef WITH_DEBUGGER
            if (ctxt->debugStatus != XSLT_DEBUG_NONE)
                xslHandleDebugger(cur, node, NULL, ctxt);
#endif
            if (ctxt->state == XSLT_STATE_STOPPED)
                break;
            if (cur->type == XML_TEXT_NODE) {
                cur = cur->next;
                continue;
            }
            if (! IS_XSLT_ELEM(cur))
                break;
            if (IS_XSLT_NAME(cur, "with-param")) {
                param = xsltParseStylesheetCallerParam(ctxt, cur);
                if (param != NULL) {
                    param->next = withParams;
                    withParams = param;
                }
            }
            if (IS_XSLT_NAME(cur, "sort")) {
                xsltTemplatePtr oldCurTempRule =
                    ctxt->currentTemplateRule;
                int nbsorts = 0;
                xmlNodePtr sorts[XSLT_MAX_SORT];

                sorts[nbsorts++] = cur;

                while (cur) {

#ifdef WITH_DEBUGGER
                    if (ctxt->debugStatus != XSLT_DEBUG_NONE)
                        xslHandleDebugger(cur, node, NULL, ctxt);
#endif
                    if (ctxt->state == XSLT_STATE_STOPPED)
                        break;

                    if (cur->type == XML_TEXT_NODE) {
                        cur = cur->next;
                        continue;
                    }

                    if (! IS_XSLT_ELEM(cur))
                        break;
                    if (IS_XSLT_NAME(cur, "with-param")) {
                        param = xsltParseStylesheetCallerParam(ctxt, cur);
                        if (param != NULL) {
                            param->next = withParams;
                            withParams = param;
                        }
                    }
                    if (IS_XSLT_NAME(cur, "sort")) {
                        if (nbsorts >= XSLT_MAX_SORT) {
                            xsltTransformError(ctxt, NULL, cur,
                                "The number (%d) of xsl:sort instructions exceeds the "
                                "maximum allowed by this processor's settings.\n",
                                nbsorts);
                            ctxt->state = XSLT_STATE_STOPPED;
                            break;
                        } else {
                            sorts[nbsorts++] = cur;
                        }
                    }
                    cur = cur->next;
                }
                /*
                * The "current template rule" is cleared for xsl:sort.
                */
                ctxt->currentTemplateRule = NULL;
                /*
                * Sort.
                */
                xsltDoSortFunction(ctxt, sorts, nbsorts);
                ctxt->currentTemplateRule = oldCurTempRule;
                break;
            }
            cur = cur->next;
        }
    }
    xpctxt->contextSize = list->nodeNr;
    /*
    * Apply templates for all selected source nodes.
    */
    for (i = 0; i < list->nodeNr; i++) {
        cur = list->nodeTab[i];
        /*
        * The node becomes the "current node".
        */
        ctxt->node = cur;
        /*
        * An xsl:apply-templates can change the current context doc.
        * OPTIMIZE TODO: Get rid of the need to set the context doc.
        */
        if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL))
            xpctxt->doc = cur->doc;

        xpctxt->proximityPosition = i + 1;
        /*
        * Find and apply a template for this node.
        */
        xsltProcessOneNode(ctxt, cur, withParams);
    }

exit:
error:
    /*
    * Free the parameter list.
    */
    if (withParams != NULL)
        xsltFreeStackElemList(withParams);
    if (list != NULL)
        xmlXPathFreeNodeSet(list);
    /*
    * Restore context states.
    */
    xpctxt->nsNr = oldXPNsNr;
    xpctxt->namespaces = oldXPNamespaces;
    xpctxt->doc = oldXPDoc;
    xpctxt->contextSize = oldXPContextSize;
    xpctxt->proximityPosition = oldXPProximityPosition;

    ctxt->document = oldDocInfo;
    ctxt->nodeList = oldList;
    ctxt->node = oldContextNode;
    ctxt->mode = oldMode;
    ctxt->modeURI = oldModeURI;
}


/**
 * xsltChoose:
 * @ctxt:  a XSLT process context
 * @contextNode:  the current node in the source tree
 * @inst:  the xsl:choose instruction
 * @comp:  compiled information of the instruction
 *
 * Processes the xsl:choose instruction on the source node.
 */
void
xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
           xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED)
{
    xmlNodePtr cur;

    if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL))
        return;

    /*
    * TODO: Content model checks should be done only at compilation
    * time.
    */
    cur = inst->children;
    if (cur == NULL) {
        xsltTransformError(ctxt, NULL, inst,
            "xsl:choose: The instruction has no content.\n");
        return;
    }

#ifdef XSLT_REFACTORED
    /*
    * We don't check the content model during transformation.
    */
#else
    if ((! IS_XSLT_ELEM(cur)) || (! IS_XSLT_NAME(cur, "when"))) {
        xsltTransformError(ctxt, NULL, inst,
             "xsl:choose: xsl:when expected first\n");
        return;
    }
#endif

    {
        int testRes = 0, res = 0;
        xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
        xmlDocPtr oldXPContextDoc = xpctxt->doc;
        int oldXPProximityPosition = xpctxt->proximityPosition;
        int oldXPContextSize = xpctxt->contextSize;
        xmlNsPtr *oldXPNamespaces = xpctxt->namespaces;
        int oldXPNsNr = xpctxt->nsNr;

#ifdef XSLT_REFACTORED
        xsltStyleItemWhenPtr wcomp = NULL;
#else
        xsltStylePreCompPtr wcomp = NULL;
#endif

        /*
        * Process xsl:when ---------------------------------------------------
        */
        while (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "when")) {
            wcomp = cur->psvi;

            if ((wcomp == NULL) || (wcomp->test == NULL) ||
                (wcomp->comp == NULL))
            {
                xsltTransformError(ctxt, NULL, cur,
                    "Internal error in xsltChoose(): "
                    "The XSLT 'when' instruction was not compiled.\n");
                goto error;
            }


#ifdef WITH_DEBUGGER
            if (xslDebugStatus != XSLT_DEBUG_NONE) {
                /*
                * TODO: Isn't comp->templ always NULL for xsl:choose?
                */
                xslHandleDebugger(cur, contextNode, NULL, ctxt);
            }
#endif
#ifdef WITH_XSLT_DEBUG_PROCESS
            XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
                "xsltChoose: test %s\n", wcomp->test));
#endif

            xpctxt->node = contextNode;
            xpctxt->doc = oldXPContextDoc;
            xpctxt->proximityPosition = oldXPProximityPosition;
            xpctxt->contextSize = oldXPContextSize;

#ifdef XSLT_REFACTORED
            if (wcomp->inScopeNs != NULL) {
                xpctxt->namespaces = wcomp->inScopeNs->list;
                xpctxt->nsNr = wcomp->inScopeNs->xpathNumber;
            } else {
                xpctxt->namespaces = NULL;
                xpctxt->nsNr = 0;
            }
#else
            xpctxt->namespaces = wcomp->nsList;
            xpctxt->nsNr = wcomp->nsNr;
#endif


#ifdef XSLT_FAST_IF
            res = xmlXPathCompiledEvalToBoolean(wcomp->comp, xpctxt);

            if (res == -1) {
                ctxt->state = XSLT_STATE_STOPPED;
                goto error;
            }
            testRes = (res == 1) ? 1 : 0;

#else /* XSLT_FAST_IF */

            res = xmlXPathCompiledEval(wcomp->comp, xpctxt);

            if (res != NULL) {
                if (res->type != XPATH_BOOLEAN)
                    res = xmlXPathConvertBoolean(res);
                if (res->type == XPATH_BOOLEAN)
                    testRes = res->boolval;
                else {
#ifdef WITH_XSLT_DEBUG_PROCESS
                    XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
                        "xsltChoose: test didn't evaluate to a boolean\n"));
#endif
                    goto error;
                }
                xmlXPathFreeObject(res);
                res = NULL;
            } else {
                ctxt->state = XSLT_STATE_STOPPED;
                goto error;
            }

#endif /* else of XSLT_FAST_IF */

#ifdef WITH_XSLT_DEBUG_PROCESS
            XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
                "xsltChoose: test evaluate to %d\n", testRes));
#endif
            if (testRes)
                goto test_is_true;

            cur = cur->next;
        }

        /*
        * Process xsl:otherwise ----------------------------------------------
        */
        if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "otherwise")) {

#ifdef WITH_DEBUGGER
            if (xslDebugStatus != XSLT_DEBUG_NONE)
                xslHandleDebugger(cur, contextNode, NULL, ctxt);
#endif

#ifdef WITH_XSLT_DEBUG_PROCESS
            XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
                "evaluating xsl:otherwise\n"));
#endif
            goto test_is_true;
        }
        xpctxt->node = contextNode;
        xpctxt->doc = oldXPContextDoc;
        xpctxt->proximityPosition = oldXPProximityPosition;
        xpctxt->contextSize = oldXPContextSize;
        xpctxt->namespaces = oldXPNamespaces;
        xpctxt->nsNr = oldXPNsNr;
        goto exit;

test_is_true:

        xpctxt->node = contextNode;
        xpctxt->doc = oldXPContextDoc;
        xpctxt->proximityPosition = oldXPProximityPosition;
        xpctxt->contextSize = oldXPContextSize;
        xpctxt->namespaces = oldXPNamespaces;
        xpctxt->nsNr = oldXPNsNr;
        goto process_sequence;
    }

process_sequence:

    /*
    * Instantiate the sequence constructor.
    */
    xsltApplySequenceConstructor(ctxt, ctxt->node, cur->children,
        NULL);

exit:
error:
    return;
}

/**
 * xsltIf:
 * @ctxt:  a XSLT process context
 * @contextNode:  the current node in the source tree
 * @inst:  the xsl:if instruction
 * @castedComp:  compiled information of the instruction
 *
 * Processes the xsl:if instruction on the source node.
 */
void
xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
                   xmlNodePtr inst, xsltStylePreCompPtr castedComp)
{
    int res = 0;

#ifdef XSLT_REFACTORED
    xsltStyleItemIfPtr comp = (xsltStyleItemIfPtr) castedComp;
#else
    xsltStylePreCompPtr comp = castedComp;
#endif

    if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL))
        return;
    if ((comp == NULL) || (comp->test == NULL) || (comp->comp == NULL)) {
        xsltTransformError(ctxt, NULL, inst,
            "Internal error in xsltIf(): "
            "The XSLT 'if' instruction was not compiled.\n");
        return;
    }

#ifdef WITH_XSLT_DEBUG_PROCESS
    XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext,
         "xsltIf: test %s\n", comp->test));
#endif

#ifdef XSLT_FAST_IF
    {
        xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
        xmlDocPtr oldXPContextDoc = xpctxt->doc;
        xmlNsPtr *oldXPNamespaces = xpctxt->namespaces;
        xmlNodePtr oldXPContextNode = xpctxt->node;
        int oldXPProximityPosition = xpctxt->proximityPosition;
        int oldXPContextSize = xpctxt->contextSize;
        int oldXPNsNr = xpctxt->nsNr;
        xmlDocPtr oldLocalFragmentTop = ctxt->localRVT;

        xpctxt->node = contextNode;
        if (comp != NULL) {

#ifdef XSLT_REFACTORED
            if (comp->inScopeNs != NULL) {
                xpctxt->namespaces = comp->inScopeNs->list;
                xpctxt->nsNr = comp->inScopeNs->xpathNumber;
            } else {
                xpctxt->namespaces = NULL;
                xpctxt->nsNr = 0;
            }
#else
            xpctxt->namespaces = comp->nsList;
            xpctxt->nsNr = comp->nsNr;
#endif
        } else {
            xpctxt->namespaces = NULL;
            xpctxt->nsNr = 0;
        }
        /*
        * This XPath function is optimized for boolean results.
        */
        res = xmlXPathCompiledEvalToBoolean(comp->comp, xpctxt);

        /*
        * Cleanup fragments created during evaluation of the
        * "select" expression.
        */
        if (oldLocalFragmentTop != ctxt->localRVT)
            xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);

        xpctxt->doc = oldXPContextDoc;
        xpctxt->node = oldXPContextNode;
        xpctxt->contextSize = oldXPContextSize;
        xpctxt->proximityPosition = oldXPProximityPosition;
        xpctxt->nsNr = oldXPNsNr;
        xpctxt->namespaces = oldXPNamespaces;
    }

#ifdef WITH_XSLT_DEBUG_PROCESS
    XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext,
        "xsltIf: test evaluate to %d\n", res));
#endif

    if (res == -1) {
        ctxt->state = XSLT_STATE_STOPPED;
        goto error;
    }
    if (res == 1) {
        /*
        * Instantiate the sequence constructor of xsl:if.
        */
        xsltApplySequenceConstructor(ctxt,
            contextNode, inst->children, NULL);
    }

#else /* XSLT_FAST_IF */
    {
        xmlXPathObjectPtr xpobj = NULL;
        /*
        * OLD CODE:
        */
        {
            xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
            xmlDocPtr oldXPContextDoc = xpctxt->doc;
            xmlNsPtr *oldXPNamespaces = xpctxt->namespaces;
            xmlNodePtr oldXPContextNode = xpctxt->node;
            int oldXPProximityPosition = xpctxt->proximityPosition;
            int oldXPContextSize = xpctxt->contextSize;
            int oldXPNsNr = xpctxt->nsNr;

            xpctxt->node = contextNode;
            if (comp != NULL) {

#ifdef XSLT_REFACTORED
                if (comp->inScopeNs != NULL) {
                    xpctxt->namespaces = comp->inScopeNs->list;
                    xpctxt->nsNr = comp->inScopeNs->xpathNumber;
                } else {
                    xpctxt->namespaces = NULL;
                    xpctxt->nsNr = 0;
                }
#else
                xpctxt->namespaces = comp->nsList;
                xpctxt->nsNr = comp->nsNr;
#endif
            } else {
                xpctxt->namespaces = NULL;
                xpctxt->nsNr = 0;
            }

            /*
            * This XPath function is optimized for boolean results.
            */
            xpobj = xmlXPathCompiledEval(comp->comp, xpctxt);

            xpctxt->doc = oldXPContextDoc;
            xpctxt->node = oldXPContextNode;
            xpctxt->contextSize = oldXPContextSize;
            xpctxt->proximityPosition = oldXPProximityPosition;
            xpctxt->nsNr = oldXPNsNr;
            xpctxt->namespaces = oldXPNamespaces;
        }
        if (xpobj != NULL) {
            if (xpobj->type != XPATH_BOOLEAN)
                xpobj = xmlXPathConvertBoolean(xpobj);
            if (xpobj->type == XPATH_BOOLEAN) {
                res = xpobj->boolval;

#ifdef WITH_XSLT_DEBUG_PROCESS
                XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext,
                    "xsltIf: test evaluate to %d\n", res));
#endif
                if (res) {
                    xsltApplySequenceConstructor(ctxt,
                        contextNode, inst->children, NULL);
                }
            } else {

#ifdef WITH_XSLT_DEBUG_PROCESS
                XSLT_TRACE(ctxt, XSLT_TRACE_IF,
                    xsltGenericDebug(xsltGenericDebugContext,
                    "xsltIf: test didn't evaluate to a boolean\n"));
#endif
                ctxt->state = XSLT_STATE_STOPPED;
            }
            xmlXPathFreeObject(xpobj);
        } else {
            ctxt->state = XSLT_STATE_STOPPED;
        }
    }
#endif /* else of XSLT_FAST_IF */

error:
    return;
}

/**
 * xsltForEach:
 * @ctxt:  an XSLT transformation context
 * @contextNode:  the "current node" in the source tree
 * @inst:  the element node of the xsl:for-each instruction
 * @castedComp:  the compiled information of the instruction
 *
 * Process the xslt for-each node on the source node
 */
void
xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
            xmlNodePtr inst, xsltStylePreCompPtr castedComp)
{
#ifdef XSLT_REFACTORED
    xsltStyleItemForEachPtr comp = (xsltStyleItemForEachPtr) castedComp;
#else
    xsltStylePreCompPtr comp = castedComp;
#endif
    int i;
    xmlXPathObjectPtr res = NULL;
    xmlNodePtr cur, curInst;
    xmlNodeSetPtr list = NULL;
    xmlNodeSetPtr oldList;
    int oldXPProximityPosition, oldXPContextSize;
    xmlNodePtr oldContextNode;
    xsltTemplatePtr oldCurTemplRule;
    xmlDocPtr oldXPDoc;
    xsltDocumentPtr oldDocInfo;
    xmlXPathContextPtr xpctxt;

    if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) {
        xsltGenericError(xsltGenericErrorContext,
            "xsltForEach(): Bad arguments.\n");
        return;
    }

    if (comp == NULL) {
        xsltTransformError(ctxt, NULL, inst,
            "Internal error in xsltForEach(): "
            "The XSLT 'for-each' instruction was not compiled.\n");
        return;
    }
    if ((comp->select == NULL) || (comp->comp == NULL)) {
        xsltTransformError(ctxt, NULL, inst,
            "Internal error in xsltForEach(): "
            "The selecting expression of the XSLT 'for-each' "
            "instruction was not compiled correctly.\n");
        return;
    }
    xpctxt = ctxt->xpathCtxt;

#ifdef WITH_XSLT_DEBUG_PROCESS
    XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext,
         "xsltForEach: select %s\n", comp->select));
#endif

    /*
    * Save context states.
    */
    oldDocInfo = ctxt->document;
    oldList = ctxt->nodeList;
    oldContextNode = ctxt->node;
    /*
    * The "current template rule" is cleared for the instantiation of
    * xsl:for-each.
    */
    oldCurTemplRule = ctxt->currentTemplateRule;
    ctxt->currentTemplateRule = NULL;

    oldXPDoc = xpctxt->doc;
    oldXPProximityPosition = xpctxt->proximityPosition;
    oldXPContextSize = xpctxt->contextSize;
    /*
    * Set up XPath.
    */
    xpctxt->node = contextNode;
#ifdef XSLT_REFACTORED
    if (comp->inScopeNs != NULL) {
        xpctxt->namespaces = comp->inScopeNs->list;
        xpctxt->nsNr = comp->inScopeNs->xpathNumber;
    } else {
        xpctxt->namespaces = NULL;
        xpctxt->nsNr = 0;
    }
#else
    xpctxt->namespaces = comp->nsList;
    xpctxt->nsNr = comp->nsNr;
#endif

    /*
    * Evaluate the 'select' expression.
    */
    res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);

    if (res != NULL) {
        if (res->type == XPATH_NODESET)
            list = res->nodesetval;
        else {
            xsltTransformError(ctxt, NULL, inst,
                "The 'select' expression does not evaluate to a node set.\n");

#ifdef WITH_XSLT_DEBUG_PROCESS
            XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext,
                "xsltForEach: select didn't evaluate to a node list\n"));
#endif
            goto error;
        }
    } else {
        xsltTransformError(ctxt, NULL, inst,
            "Failed to evaluate the 'select' expression.\n");
        ctxt->state = XSLT_STATE_STOPPED;
        goto error;
    }

    if ((list == NULL) || (list->nodeNr <= 0))
        goto exit;

#ifdef WITH_XSLT_DEBUG_PROCESS
    XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext,
        "xsltForEach: select evaluates to %d nodes\n", list->nodeNr));
#endif

    /*
    * Restore XPath states for the "current node".
    */
    xpctxt->contextSize = oldXPContextSize;
    xpctxt->proximityPosition = oldXPProximityPosition;
    xpctxt->node = contextNode;

    /*
    * Set the list; this has to be done already here for xsltDoSortFunction().
    */
    ctxt->nodeList = list;
    /*
    * Handle xsl:sort instructions and skip them for further processing.
    * BUG TODO: We are not using namespaced potentially defined on the
    * xsl:sort element; XPath expression might fail.
    */
    curInst = inst->children;
    if (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) {
        int nbsorts = 0;
        xmlNodePtr sorts[XSLT_MAX_SORT];

        sorts[nbsorts++] = curInst;

#ifdef WITH_DEBUGGER
        if (xslDebugStatus != XSLT_DEBUG_NONE)
            xslHandleDebugger(curInst, contextNode, NULL, ctxt);
#endif

        curInst = curInst->next;
        while (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) {
            if (nbsorts >= XSLT_MAX_SORT) {
                xsltTransformError(ctxt, NULL, curInst,
                    "The number of xsl:sort instructions exceeds the "
                    "maximum (%d) allowed by this processor.\n",
                    XSLT_MAX_SORT);
                goto error;
            } else {
                sorts[nbsorts++] = curInst;
            }

#ifdef WITH_DEBUGGER
            if (xslDebugStatus != XSLT_DEBUG_NONE)
                xslHandleDebugger(curInst, contextNode, NULL, ctxt);
#endif
            curInst = curInst->next;
        }
        xsltDoSortFunction(ctxt, sorts, nbsorts);
    }
    xpctxt->contextSize = list->nodeNr;
    /*
    * Instantiate the sequence constructor for each selected node.
    */
    for (i = 0; i < list->nodeNr; i++) {
        cur = list->nodeTab[i];
        /*
        * The selected node becomes the "current node".
        */
        ctxt->node = cur;
        /*
        * An xsl:for-each can change the current context doc.
        * OPTIMIZE TODO: Get rid of the need to set the context doc.
        */
        if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL))
            xpctxt->doc = cur->doc;

        xpctxt->proximityPosition = i + 1;

        xsltApplySequenceConstructor(ctxt, cur, curInst, NULL);
    }

exit:
error:
    if (res != NULL)
        xmlXPathFreeObject(res);
    /*
    * Restore old states.
    */
    ctxt->document = oldDocInfo;
    ctxt->nodeList = oldList;
    ctxt->node = oldContextNode;
    ctxt->currentTemplateRule = oldCurTemplRule;

    xpctxt->doc = oldXPDoc;
    xpctxt->contextSize = oldXPContextSize;
    xpctxt->proximityPosition = oldXPProximityPosition;
}

/************************************************************************
 *                                                                      *
 *                      Generic interface                               *
 *                                                                      *
 ************************************************************************/

#ifdef XSLT_GENERATE_HTML_DOCTYPE
typedef struct xsltHTMLVersion {
    const char *version;
    const char *public;
    const char *system;
} xsltHTMLVersion;

static xsltHTMLVersion xsltHTMLVersions[] = {
    { "4.01frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
      "http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd"},
    { "4.01strict", "-//W3C//DTD HTML 4.01//EN",
      "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"},
    { "4.01trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
      "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
    { "4.01", "-//W3C//DTD HTML 4.01 Transitional//EN",
      "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
    { "4.0strict", "-//W3C//DTD HTML 4.01//EN",
      "http://www.w3.org/TR/html4/strict.dtd"},
    { "4.0trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
      "http://www.w3.org/TR/html4/loose.dtd"},
    { "4.0frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
      "http://www.w3.org/TR/html4/frameset.dtd"},
    { "4.0", "-//W3C//DTD HTML 4.01 Transitional//EN",
      "http://www.w3.org/TR/html4/loose.dtd"},
    { "3.2", "-//W3C//DTD HTML 3.2//EN", NULL }
};

/**
 * xsltGetHTMLIDs:
 * @version:  the version string
 * @publicID:  used to return the public ID
 * @systemID:  used to return the system ID
 *
 * Returns -1 if not found, 0 otherwise and the system and public
 *         Identifier for this given verion of HTML
 */
static int
xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID,
                    const xmlChar **systemID) {
    unsigned int i;
    if (version == NULL)
        return(-1);
    for (i = 0;i < (sizeof(xsltHTMLVersions)/sizeof(xsltHTMLVersions[1]));
         i++) {
        if (!xmlStrcasecmp(version,
                           (const xmlChar *) xsltHTMLVersions[i].version)) {
            if (publicID != NULL)
                *publicID = (const xmlChar *) xsltHTMLVersions[i].public;
            if (systemID != NULL)
                *systemID = (const xmlChar *) xsltHTMLVersions[i].system;
            return(0);
        }
    }
    return(-1);
}
#endif

/**
 * xsltApplyStripSpaces:
 * @ctxt:  a XSLT process context
 * @node:  the root of the XML tree
 *
 * Strip the unwanted ignorable spaces from the input tree
 */
void
xsltApplyStripSpaces(xsltTransformContextPtr ctxt, xmlNodePtr node) {
    xmlNodePtr current;
#ifdef WITH_XSLT_DEBUG_PROCESS
    int nb = 0;
#endif


    current = node;
    while (current != NULL) {
        /*
         * Cleanup children empty nodes if asked for
         */
        if ((IS_XSLT_REAL_NODE(current)) &&
            (current->children != NULL) &&
            (xsltFindElemSpaceHandling(ctxt, current))) {
            xmlNodePtr delete = NULL, cur = current->children;

            while (cur != NULL) {
                if (IS_BLANK_NODE(cur))
                    delete = cur;

                cur = cur->next;
                if (delete != NULL) {
                    xmlUnlinkNode(delete);
                    xmlFreeNode(delete);
                    delete = NULL;
#ifdef WITH_XSLT_DEBUG_PROCESS
                    nb++;
#endif
                }
            }
        }

        /*
         * Skip to next node in document order.
         */
        if (node->type == XML_ENTITY_REF_NODE) {
            /* process deep in entities */
            xsltApplyStripSpaces(ctxt, node->children);
        }
        if ((current->children != NULL) &&
            (current->type != XML_ENTITY_REF_NODE)) {
            current = current->children;
        } else if (current->next != NULL) {
            current = current->next;
        } else {
            do {
                current = current->parent;
                if (current == NULL)
                    break;
                if (current == node)
                    goto done;
                if (current->next != NULL) {
                    current = current->next;
                    break;
                }
            } while (current != NULL);
        }
    }

done:
#ifdef WITH_XSLT_DEBUG_PROCESS
    XSLT_TRACE(ctxt,XSLT_TRACE_STRIP_SPACES,xsltGenericDebug(xsltGenericDebugContext,
             "xsltApplyStripSpaces: removed %d ignorable blank node\n", nb));
#endif
    return;
}

static int
xsltCountKeys(xsltTransformContextPtr ctxt)
{
    xsltStylesheetPtr style;
    xsltKeyDefPtr keyd;

    if (ctxt == NULL)
        return(-1);

    /*
    * Do we have those nastly templates with a key() in the match pattern?
    */
    ctxt->hasTemplKeyPatterns = 0;
    style = ctxt->style;
    while (style != NULL) {
        if (style->keyMatch != NULL) {
            ctxt->hasTemplKeyPatterns = 1;
            break;
        }
        style = xsltNextImport(style);
    }
    /*
    * Count number of key declarations.
    */
    ctxt->nbKeys = 0;
    style = ctxt->style;
    while (style != NULL) {
        keyd = style->keys;
        while (keyd) {
            ctxt->nbKeys++;
            keyd = keyd->next;
        }
        style = xsltNextImport(style);
    }
    return(ctxt->nbKeys);
}

/**
 * xsltApplyStylesheetInternal:
 * @style:  a parsed XSLT stylesheet
 * @doc:  a parsed XML document
 * @params:  a NULL terminated array of parameters names/values tuples
 * @output:  the targetted output
 * @profile:  profile FILE * output or NULL
 * @user:  user provided parameter
 *
 * Apply the stylesheet to the document
 * NOTE: This may lead to a non-wellformed output XML wise !
 *
 * Returns the result document or NULL in case of error
 */
static xmlDocPtr
xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc,
                            const char **params, const char *output,
                            FILE * profile, xsltTransformContextPtr userCtxt)
{
    xmlDocPtr res = NULL;
    xsltTransformContextPtr ctxt = NULL;
    xmlNodePtr root, node;
    const xmlChar *method;
    const xmlChar *doctypePublic;
    const xmlChar *doctypeSystem;
    const xmlChar *version;
    const xmlChar *encoding;
    xsltStackElemPtr variables;
    xsltStackElemPtr vptr;

    xsltInitGlobals();

    if ((style == NULL) || (doc == NULL))
        return (NULL);

    if (style->internalized == 0) {
#ifdef WITH_XSLT_DEBUG
        xsltGenericDebug(xsltGenericDebugContext,
                         "Stylesheet was not fully internalized !\n");
#endif
    }
    if (doc->intSubset != NULL) {
        /*
         * Avoid hitting the DTD when scanning nodes
         * but keep it linked as doc->intSubset
         */
        xmlNodePtr cur = (xmlNodePtr) doc->intSubset;
        if (cur->next != NULL)
            cur->next->prev = cur->prev;
        if (cur->prev != NULL)
            cur->prev->next = cur->next;
        if (doc->children == cur)
            doc->children = cur->next;
        if (doc->last == cur)
            doc->last = cur->prev;
        cur->prev = cur->next = NULL;
    }

    /*
     * Check for XPath document order availability
     */
    root = xmlDocGetRootElement(doc);
    if (root != NULL) {
        if (((long) root->content) >= 0 && (xslDebugStatus == XSLT_DEBUG_NONE))
            xmlXPathOrderDocElems(doc);
    }

    if (userCtxt != NULL)
        ctxt = userCtxt;
    else
        ctxt = xsltNewTransformContext(style, doc);

    if (ctxt == NULL)
        return (NULL);

    ctxt->initialContextDoc = doc;
    ctxt->initialContextNode = (xmlNodePtr) doc;

    if (profile != NULL)
        ctxt->profile = 1;

    if (output != NULL)
        ctxt->outputFile = output;
    else
        ctxt->outputFile = NULL;

    /*
     * internalize the modes if needed
     */
    if (ctxt->dict != NULL) {
        if (ctxt->mode != NULL)
            ctxt->mode = xmlDictLookup(ctxt->dict, ctxt->mode, -1);
        if (ctxt->modeURI != NULL)
            ctxt->modeURI = xmlDictLookup(ctxt->dict, ctxt->modeURI, -1);
    }

    XSLT_GET_IMPORT_PTR(method, style, method)
    XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
    XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
    XSLT_GET_IMPORT_PTR(version, style, version)
    XSLT_GET_IMPORT_PTR(encoding, style, encoding)

    if ((method != NULL) &&
        (!xmlStrEqual(method, (const xmlChar *) "xml")))
    {
        if (xmlStrEqual(method, (const xmlChar *) "html")) {
            ctxt->type = XSLT_OUTPUT_HTML;
            if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
                res = htmlNewDoc(doctypeSystem, doctypePublic);
            } else {
                if (version == NULL) {
                    xmlDtdPtr dtd;

                    res = htmlNewDoc(NULL, NULL);
                    /*
                    * Make sure no DTD node is generated in this case
                    */
                    if (res != NULL) {
                        dtd = xmlGetIntSubset(res);
                        if (dtd != NULL) {
                            xmlUnlinkNode((xmlNodePtr) dtd);
                            xmlFreeDtd(dtd);
                        }
                        res->intSubset = NULL;
                        res->extSubset = NULL;
                    }
                } else {

#ifdef XSLT_GENERATE_HTML_DOCTYPE
                    xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
#endif
                    res = htmlNewDoc(doctypeSystem, doctypePublic);
                }
            }
            if (res == NULL)
                goto error;
            res->dict = ctxt->dict;
            xmlDictReference(res->dict);

#ifdef WITH_XSLT_DEBUG
            xsltGenericDebug(xsltGenericDebugContext,
                "reusing transformation dict for output\n");
#endif
        } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
            xsltTransformError(ctxt, NULL, (xmlNodePtr) doc,
                "xsltApplyStylesheetInternal: unsupported method xhtml, using html\n",
                style->method);
            ctxt->type = XSLT_OUTPUT_HTML;
            res = htmlNewDoc(doctypeSystem, doctypePublic);
            if (res == NULL)
                goto error;
            res->dict = ctxt->dict;
            xmlDictReference(res->dict);

#ifdef WITH_XSLT_DEBUG
            xsltGenericDebug(xsltGenericDebugContext,
                "reusing transformation dict for output\n");
#endif
        } else if (xmlStrEqual(method, (const xmlChar *) "text")) {
            ctxt->type = XSLT_OUTPUT_TEXT;
            res = xmlNewDoc(style->version);
            if (res == NULL)
                goto error;
            res->dict = ctxt->dict;
            xmlDictReference(res->dict);

#ifdef WITH_XSLT_DEBUG
            xsltGenericDebug(xsltGenericDebugContext,
                "reusing transformation dict for output\n");
#endif
        } else {
            xsltTransformError(ctxt, NULL, (xmlNodePtr) doc,
                "xsltApplyStylesheetInternal: unsupported method %s\n",
                style->method);
            goto error;
        }
    } else {
        ctxt->type = XSLT_OUTPUT_XML;
        res = xmlNewDoc(style->version);
        if (res == NULL)
            goto error;
        res->dict = ctxt->dict;
        xmlDictReference(ctxt->dict);
#ifdef WITH_XSLT_DEBUG
        xsltGenericDebug(xsltGenericDebugContext,
                         "reusing transformation dict for output\n");
#endif
    }
    res->charset = XML_CHAR_ENCODING_UTF8;
    if (encoding != NULL)
        res->encoding = xmlStrdup(encoding);
    variables = style->variables;

    /*
     * Start the evaluation, evaluate the params, the stylesheets globals
     * and start by processing the top node.
     */
    if (xsltNeedElemSpaceHandling(ctxt))
        xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc));
    /*
    * Evaluate global params and user-provided params.
    */
    ctxt->node = (xmlNodePtr) doc;
    if (ctxt->globalVars == NULL)
        ctxt->globalVars = xmlHashCreate(20);
    if (params != NULL) {
        xsltEvalUserParams(ctxt, params);
    }

    /* need to be called before evaluating global variables */
    xsltCountKeys(ctxt);

    xsltEvalGlobalVariables(ctxt);

    ctxt->node = (xmlNodePtr) doc;
    ctxt->output = res;
    ctxt->insert = (xmlNodePtr) res;
    ctxt->varsBase = ctxt->varsNr - 1;

    ctxt->xpathCtxt->contextSize = 1;
    ctxt->xpathCtxt->proximityPosition = 1;
    ctxt->xpathCtxt->node = NULL; /* TODO: Set the context node here? */
    /*
    * Start processing the source tree -----------------------------------
    */
    xsltProcessOneNode(ctxt, ctxt->node, NULL);
    /*
    * Remove all remaining vars from the stack.
    */
    xsltLocalVariablePop(ctxt, 0, -2);
    xsltShutdownCtxtExts(ctxt);

    xsltCleanupTemplates(style); /* TODO: <- style should be read only */

    /*
     * Now cleanup our variables so stylesheet can be re-used
     *
     * TODO: this is not needed anymore global variables are copied
     *       and not evaluated directly anymore, keep this as a check
     */
    if (style->variables != variables) {
        vptr = style->variables;
        while (vptr->next != variables)
            vptr = vptr->next;
        vptr->next = NULL;
        xsltFreeStackElemList(style->variables);
        style->variables = variables;
    }
    vptr = style->variables;
    while (vptr != NULL) {
        if (vptr->computed) {
            if (vptr->value != NULL) {
                xmlXPathFreeObject(vptr->value);
                vptr->value = NULL;
                vptr->computed = 0;
            }
        }
        vptr = vptr->next;
    }
#if 0
    /*
     * code disabled by wmb; awaiting kb's review
     * problem is that global variable(s) may contain xpath objects
     * from doc associated with RVT, so can't be freed at this point.
     * xsltFreeTransformContext includes a call to xsltFreeRVTs, so
     * I assume this shouldn't be required at this point.
     */
    /*
    * Free all remaining tree fragments.
    */
    xsltFreeRVTs(ctxt);
#endif
    /*
     * Do some post processing work depending on the generated output
     */
    root = xmlDocGetRootElement(res);
    if (root != NULL) {
        const xmlChar *doctype = NULL;

        if ((root->ns != NULL) && (root->ns->prefix != NULL))
            doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name);
        if (doctype == NULL)
            doctype = root->name;

        /*
         * Apply the default selection of the method
         */
        if ((method == NULL) &&
            (root->ns == NULL) &&
            (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) {
            xmlNodePtr tmp;

            tmp = res->children;
            while ((tmp != NULL) && (tmp != root)) {
                if (tmp->type == XML_ELEMENT_NODE)
                    break;
                if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp)))
                    break;
                tmp = tmp->next;
            }
            if (tmp == root) {
                ctxt->type = XSLT_OUTPUT_HTML;
                /*
                * REVISIT TODO: XML_HTML_DOCUMENT_NODE is set after the
                *  transformation on the doc, but functions like
                */
                res->type = XML_HTML_DOCUMENT_NODE;
                if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
                    res->intSubset = xmlCreateIntSubset(res, doctype,
                                                        doctypePublic,
                                                        doctypeSystem);
#ifdef XSLT_GENERATE_HTML_DOCTYPE
                } else if (version != NULL) {
                    xsltGetHTMLIDs(version, &doctypePublic,
                                   &doctypeSystem);
                    if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
                        res->intSubset =
                            xmlCreateIntSubset(res, doctype,
                                               doctypePublic,
                                               doctypeSystem);
#endif
                }
            }

        }
        if (ctxt->type == XSLT_OUTPUT_XML) {
            XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
            XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
            if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
                xmlNodePtr last;
                /* Need a small "hack" here to assure DTD comes before
                   possible comment nodes */
                node = res->children;
                last = res->last;
                res->children = NULL;
                res->last = NULL;
                res->intSubset = xmlCreateIntSubset(res, doctype,
                                                    doctypePublic,
                                                    doctypeSystem);
                if (res->children != NULL) {
                    res->children->next = node;
                    node->prev = res->children;
                    res->last = last;
                } else {
                    res->children = node;
                    res->last = last;
                }
            }
        }
    }
    xmlXPathFreeNodeSet(ctxt->nodeList);
    if (profile != NULL) {
        xsltSaveProfiling(ctxt, profile);
    }

    /*
     * Be pedantic.
     */
    if ((ctxt != NULL) && (ctxt->state == XSLT_STATE_ERROR)) {
        xmlFreeDoc(res);
        res = NULL;
    }
    if ((res != NULL) && (ctxt != NULL) && (output != NULL)) {
        int ret;

        ret = xsltCheckWrite(ctxt->sec, ctxt, (const xmlChar *) output);
        if (ret == 0) {
            xsltTransformError(ctxt, NULL, NULL,
                     "xsltApplyStylesheet: forbidden to save to %s\n",
                               output);
        } else if (ret < 0) {
            xsltTransformError(ctxt, NULL, NULL,
                     "xsltApplyStylesheet: saving to %s may not be possible\n",
                               output);
        }
    }

#ifdef XSLT_DEBUG_PROFILE_CACHE
    printf("# Cache:\n");
    printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs);
    printf("# Reused variables     : %d\n", ctxt->cache->dbgReusedVars);
#endif

    if ((ctxt != NULL) && (userCtxt == NULL))
        xsltFreeTransformContext(ctxt);

    return (res);

error:
    if (res != NULL)
        xmlFreeDoc(res);

#ifdef XSLT_DEBUG_PROFILE_CACHE
    printf("# Cache:\n");
    printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs);
    printf("# Reused variables     : %d\n", ctxt->cache->dbgReusedVars);
#endif

    if ((ctxt != NULL) && (userCtxt == NULL))
        xsltFreeTransformContext(ctxt);
    return (NULL);
}

/**
 * xsltApplyStylesheet:
 * @style:  a parsed XSLT stylesheet
 * @doc:  a parsed XML document
 * @params:  a NULL terminated arry of parameters names/values tuples
 *
 * Apply the stylesheet to the document
 * NOTE: This may lead to a non-wellformed output XML wise !
 *
 * Returns the result document or NULL in case of error
 */
xmlDocPtr
xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
                    const char **params)
{
    return (xsltApplyStylesheetInternal(style, doc, params, NULL, NULL, NULL));
}

/**
 * xsltProfileStylesheet:
 * @style:  a parsed XSLT stylesheet
 * @doc:  a parsed XML document
 * @params:  a NULL terminated arry of parameters names/values tuples
 * @output:  a FILE * for the profiling output
 *
 * Apply the stylesheet to the document and dump the profiling to
 * the given output.
 *
 * Returns the result document or NULL in case of error
 */
xmlDocPtr
xsltProfileStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
                      const char **params, FILE * output)
{
    xmlDocPtr res;

    res = xsltApplyStylesheetInternal(style, doc, params, NULL, output, NULL);
    return (res);
}

/**
 * xsltApplyStylesheetUser:
 * @style:  a parsed XSLT stylesheet
 * @doc:  a parsed XML document
 * @params:  a NULL terminated array of parameters names/values tuples
 * @output:  the targetted output
 * @profile:  profile FILE * output or NULL
 * @userCtxt:  user provided transform context
 *
 * Apply the stylesheet to the document and allow the user to provide
 * its own transformation context.
 *
 * Returns the result document or NULL in case of error
 */
xmlDocPtr
xsltApplyStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc,
                            const char **params, const char *output,
                            FILE * profile, xsltTransformContextPtr userCtxt)
{
    xmlDocPtr res;

    res = xsltApplyStylesheetInternal(style, doc, params, output,
                                      profile, userCtxt);
    return (res);
}

/**
 * xsltRunStylesheetUser:
 * @style:  a parsed XSLT stylesheet
 * @doc:  a parsed XML document
 * @params:  a NULL terminated array of parameters names/values tuples
 * @output:  the URL/filename ot the generated resource if available
 * @SAX:  a SAX handler for progressive callback output (not implemented yet)
 * @IObuf:  an output buffer for progressive output (not implemented yet)
 * @profile:  profile FILE * output or NULL
 * @userCtxt:  user provided transform context
 *
 * Apply the stylesheet to the document and generate the output according
 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf.
 *
 * NOTE: This may lead to a non-wellformed output XML wise !
 * NOTE: This may also result in multiple files being generated
 * NOTE: using IObuf, the result encoding used will be the one used for
 *       creating the output buffer, use the following macro to read it
 *       from the stylesheet
 *       XSLT_GET_IMPORT_PTR(encoding, style, encoding)
 * NOTE: using SAX, any encoding specified in the stylesheet will be lost
 *       since the interface uses only UTF8
 *
 * Returns the number of by written to the main resource or -1 in case of
 *         error.
 */
int
xsltRunStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc,
                  const char **params, const char *output,
                  xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf,
                  FILE * profile, xsltTransformContextPtr userCtxt)
{
    xmlDocPtr tmp;
    int ret;

    if ((output == NULL) && (SAX == NULL) && (IObuf == NULL))
        return (-1);
    if ((SAX != NULL) && (IObuf != NULL))
        return (-1);

    /* unsupported yet */
    if (SAX != NULL) {
        XSLT_TODO   /* xsltRunStylesheet xmlSAXHandlerPtr SAX */
        return (-1);
    }

    tmp = xsltApplyStylesheetInternal(style, doc, params, output, profile,
                                      userCtxt);
    if (tmp == NULL) {
        xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
                         "xsltRunStylesheet : run failed\n");
        return (-1);
    }
    if (IObuf != NULL) {
        /* TODO: incomplete, IObuf output not progressive */
        ret = xsltSaveResultTo(IObuf, tmp, style);
    } else {
        ret = xsltSaveResultToFilename(output, tmp, style, 0);
    }
    xmlFreeDoc(tmp);
    return (ret);
}

/**
 * xsltRunStylesheet:
 * @style:  a parsed XSLT stylesheet
 * @doc:  a parsed XML document
 * @params:  a NULL terminated array of parameters names/values tuples
 * @output:  the URL/filename ot the generated resource if available
 * @SAX:  a SAX handler for progressive callback output (not implemented yet)
 * @IObuf:  an output buffer for progressive output (not implemented yet)
 *
 * Apply the stylesheet to the document and generate the output according
 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf.
 *
 * NOTE: This may lead to a non-wellformed output XML wise !
 * NOTE: This may also result in multiple files being generated
 * NOTE: using IObuf, the result encoding used will be the one used for
 *       creating the output buffer, use the following macro to read it
 *       from the stylesheet
 *       XSLT_GET_IMPORT_PTR(encoding, style, encoding)
 * NOTE: using SAX, any encoding specified in the stylesheet will be lost
 *       since the interface uses only UTF8
 *
 * Returns the number of bytes written to the main resource or -1 in case of
 *         error.
 */
int
xsltRunStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
                  const char **params, const char *output,
                  xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf)
{
    return(xsltRunStylesheetUser(style, doc, params, output, SAX, IObuf,
                                 NULL, NULL));
}

/**
 * xsltRegisterAllElement:
 * @ctxt:  the XPath context
 *
 * Registers all default XSLT elements in this context
 */
void
xsltRegisterAllElement(xsltTransformContextPtr ctxt)
{
    xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-templates",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltApplyTemplates);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-imports",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltApplyImports);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "call-template",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltCallTemplate);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "element",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltElement);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "attribute",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltAttribute);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "text",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltText);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "processing-instruction",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltProcessingInstruction);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "comment",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltComment);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "copy",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltCopy);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "value-of",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltValueOf);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "number",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltNumber);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "for-each",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltForEach);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "if",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltIf);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "choose",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltChoose);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "sort",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltSort);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "copy-of",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltCopyOf);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "message",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltMessage);

    /*
     * Those don't have callable entry points but are registered anyway
     */
    xsltRegisterExtElement(ctxt, (const xmlChar *) "variable",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltDebug);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "param",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltDebug);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "with-param",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltDebug);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "decimal-format",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltDebug);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "when",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltDebug);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "otherwise",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltDebug);
    xsltRegisterExtElement(ctxt, (const xmlChar *) "fallback",
                           XSLT_NAMESPACE,
                           (xsltTransformFunction) xsltDebug);

}

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