xslt: Import upstream release 1.1.38.
[wine.git] / libs / xslt / libxslt / transform.c
blob0fc0400c1f9bea1d64667b06ffddd9e97de9c566
1 /*
2 * transform.c: Implementation of the XSL Transformation 1.0 engine
3 * transform part, i.e. applying a Stylesheet to a document
5 * References:
6 * http://www.w3.org/TR/1999/REC-xslt-19991116
8 * Michael Kay "XSLT Programmer's Reference" pp 637-643
9 * Writing Multiple Output Files
11 * XSLT-1.1 Working Draft
12 * http://www.w3.org/TR/xslt11#multiple-output
14 * See Copyright for the status of this software.
16 * daniel@veillard.com
19 #define IN_LIBXSLT
20 #include "libxslt.h"
22 #include <limits.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <stddef.h>
27 #include <libxml/xmlmemory.h>
28 #include <libxml/parser.h>
29 #include <libxml/tree.h>
30 #include <libxml/valid.h>
31 #include <libxml/hash.h>
32 #include <libxml/encoding.h>
33 #include <libxml/xmlerror.h>
34 #include <libxml/xpath.h>
35 #include <libxml/parserInternals.h>
36 #include <libxml/xpathInternals.h>
37 #include <libxml/HTMLtree.h>
38 #include <libxml/debugXML.h>
39 #include <libxml/uri.h>
40 #include "xslt.h"
41 #include "xsltInternals.h"
42 #include "xsltutils.h"
43 #include "xsltlocale.h"
44 #include "pattern.h"
45 #include "transform.h"
46 #include "variables.h"
47 #include "numbersInternals.h"
48 #include "namespaces.h"
49 #include "attributes.h"
50 #include "templates.h"
51 #include "imports.h"
52 #include "keys.h"
53 #include "documents.h"
54 #include "extensions.h"
55 #include "extra.h"
56 #include "preproc.h"
57 #include "security.h"
59 #ifdef WITH_XSLT_DEBUG
60 #define WITH_XSLT_DEBUG_EXTRA
61 #define WITH_XSLT_DEBUG_PROCESS
62 #define WITH_XSLT_DEBUG_VARIABLE
63 #endif
65 #define XSLT_GENERATE_HTML_DOCTYPE
66 #ifdef XSLT_GENERATE_HTML_DOCTYPE
67 static int xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID,
68 const xmlChar **systemID);
69 #endif
71 int xsltMaxDepth = 3000;
72 int xsltMaxVars = 15000;
75 * Useful macros
78 #ifndef FALSE
79 # define FALSE (0 == 1)
80 # define TRUE (!FALSE)
81 #endif
83 #define IS_BLANK_NODE(n) \
84 (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
88 * Forward declarations
91 static xmlNsPtr
92 xsltCopyNamespaceListInternal(xmlNodePtr node, xmlNsPtr cur);
94 static xmlNodePtr
95 xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
96 xmlNodePtr node, xmlNodePtr insert, int isLRE,
97 int topElemVisited);
99 static void
100 xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
101 xmlNodePtr contextNode, xmlNodePtr list,
102 xsltTemplatePtr templ);
104 static void
105 xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt,
106 xmlNodePtr contextNode,
107 xmlNodePtr list,
108 xsltTemplatePtr templ,
109 xsltStackElemPtr withParams);
112 * templPush:
113 * @ctxt: the transformation context
114 * @value: the template to push on the stack
116 * Push a template on the stack
118 * Returns the new index in the stack or 0 in case of error
120 static int
121 templPush(xsltTransformContextPtr ctxt, xsltTemplatePtr value)
123 if (ctxt->templNr >= ctxt->templMax) {
124 xsltTemplatePtr *tmp;
125 int newMax = ctxt->templMax == 0 ? 4 : ctxt->templMax * 2;
127 tmp = (xsltTemplatePtr *) xmlRealloc(ctxt->templTab,
128 newMax * sizeof(*tmp));
129 if (tmp == NULL) {
130 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
131 return (0);
133 ctxt->templTab = tmp;
134 ctxt->templMax = newMax;
136 ctxt->templTab[ctxt->templNr] = value;
137 ctxt->templ = value;
138 return (ctxt->templNr++);
141 * templPop:
142 * @ctxt: the transformation context
144 * Pop a template value from the stack
146 * Returns the stored template value
148 static xsltTemplatePtr
149 templPop(xsltTransformContextPtr ctxt)
151 xsltTemplatePtr ret;
153 if (ctxt->templNr <= 0)
154 return (0);
155 ctxt->templNr--;
156 if (ctxt->templNr > 0)
157 ctxt->templ = ctxt->templTab[ctxt->templNr - 1];
158 else
159 ctxt->templ = (xsltTemplatePtr) 0;
160 ret = ctxt->templTab[ctxt->templNr];
161 ctxt->templTab[ctxt->templNr] = 0;
162 return (ret);
166 * xsltLocalVariablePop:
167 * @ctxt: the transformation context
168 * @limitNr: number of variables which should remain
169 * @level: the depth in the xsl:template's tree
171 * Pops all variable values at the given @depth from the stack.
173 * Returns the stored variable value
174 * **NOTE:**
175 * This is an internal routine and should not be called by users!
177 void
178 xsltLocalVariablePop(xsltTransformContextPtr ctxt, int limitNr, int level)
180 xsltStackElemPtr variable;
182 if (ctxt->varsNr <= 0)
183 return;
185 do {
186 if (ctxt->varsNr <= limitNr)
187 break;
188 variable = ctxt->varsTab[ctxt->varsNr - 1];
189 if (variable->level <= level)
190 break;
191 if (variable->level >= 0)
192 xsltFreeStackElemList(variable);
193 ctxt->varsNr--;
194 } while (ctxt->varsNr != 0);
195 if (ctxt->varsNr > 0)
196 ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1];
197 else
198 ctxt->vars = NULL;
202 * xsltTemplateParamsCleanup:
204 * Removes xsl:param and xsl:with-param items from the
205 * variable-stack. Only xsl:with-param items are not freed.
207 static void
208 xsltTemplateParamsCleanup(xsltTransformContextPtr ctxt)
210 xsltStackElemPtr param;
212 for (; ctxt->varsNr > ctxt->varsBase; ctxt->varsNr--) {
213 param = ctxt->varsTab[ctxt->varsNr -1];
215 * Free xsl:param items.
216 * xsl:with-param items will have a level of -1 or -2.
218 if (param->level >= 0) {
219 xsltFreeStackElemList(param);
222 if (ctxt->varsNr > 0)
223 ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1];
224 else
225 ctxt->vars = NULL;
228 #ifdef WITH_PROFILER
231 * profPush:
232 * @ctxt: the transformation context
233 * @value: the profiling value to push on the stack
235 * Push a profiling value on the stack
237 * Returns the new index in the stack or 0 in case of error
239 static int
240 profPush(xsltTransformContextPtr ctxt, long value)
242 if (ctxt->profMax == 0) {
243 ctxt->profMax = 4;
244 ctxt->profTab =
245 (long *) xmlMalloc(ctxt->profMax * sizeof(ctxt->profTab[0]));
246 if (ctxt->profTab == NULL) {
247 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
248 return (0);
251 else if (ctxt->profNr >= ctxt->profMax) {
252 ctxt->profMax *= 2;
253 ctxt->profTab =
254 (long *) xmlRealloc(ctxt->profTab,
255 ctxt->profMax * sizeof(ctxt->profTab[0]));
256 if (ctxt->profTab == NULL) {
257 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
258 return (0);
261 ctxt->profTab[ctxt->profNr] = value;
262 ctxt->prof = value;
263 return (ctxt->profNr++);
266 * profPop:
267 * @ctxt: the transformation context
269 * Pop a profiling value from the stack
271 * Returns the stored profiling value
273 static long
274 profPop(xsltTransformContextPtr ctxt)
276 long ret;
278 if (ctxt->profNr <= 0)
279 return (0);
280 ctxt->profNr--;
281 if (ctxt->profNr > 0)
282 ctxt->prof = ctxt->profTab[ctxt->profNr - 1];
283 else
284 ctxt->prof = (long) 0;
285 ret = ctxt->profTab[ctxt->profNr];
286 ctxt->profTab[ctxt->profNr] = 0;
287 return (ret);
290 static void
291 profCallgraphAdd(xsltTemplatePtr templ, xsltTemplatePtr parent)
293 int i;
295 if (templ->templMax == 0) {
296 templ->templMax = 4;
297 templ->templCalledTab =
298 (xsltTemplatePtr *) xmlMalloc(templ->templMax *
299 sizeof(templ->templCalledTab[0]));
300 templ->templCountTab =
301 (int *) xmlMalloc(templ->templMax *
302 sizeof(templ->templCountTab[0]));
303 if (templ->templCalledTab == NULL || templ->templCountTab == NULL) {
304 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
305 return;
308 else if (templ->templNr >= templ->templMax) {
309 templ->templMax *= 2;
310 templ->templCalledTab =
311 (xsltTemplatePtr *) xmlRealloc(templ->templCalledTab,
312 templ->templMax *
313 sizeof(templ->templCalledTab[0]));
314 templ->templCountTab =
315 (int *) xmlRealloc(templ->templCountTab,
316 templ->templMax *
317 sizeof(templ->templCountTab[0]));
318 if (templ->templCalledTab == NULL || templ->templCountTab == NULL) {
319 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
320 return;
324 for (i = 0; i < templ->templNr; i++) {
325 if (templ->templCalledTab[i] == parent) {
326 templ->templCountTab[i]++;
327 break;
330 if (i == templ->templNr) {
331 /* not found, add new one */
332 templ->templCalledTab[templ->templNr] = parent;
333 templ->templCountTab[templ->templNr] = 1;
334 templ->templNr++;
338 #endif /* WITH_PROFILER */
341 * xsltPreCompEval:
342 * @ctxt: transform context
343 * @node: context node
344 * @comp: precompiled expression
346 * Evaluate a precompiled XPath expression.
348 static xmlXPathObjectPtr
349 xsltPreCompEval(xsltTransformContextPtr ctxt, xmlNodePtr node,
350 xsltStylePreCompPtr comp) {
351 xmlXPathObjectPtr res;
352 xmlXPathContextPtr xpctxt;
353 xmlNodePtr oldXPContextNode;
354 xmlNsPtr *oldXPNamespaces;
355 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
357 xpctxt = ctxt->xpathCtxt;
358 oldXPContextNode = xpctxt->node;
359 oldXPProximityPosition = xpctxt->proximityPosition;
360 oldXPContextSize = xpctxt->contextSize;
361 oldXPNsNr = xpctxt->nsNr;
362 oldXPNamespaces = xpctxt->namespaces;
364 xpctxt->node = node;
365 #ifdef XSLT_REFACTORED
366 if (comp->inScopeNs != NULL) {
367 xpctxt->namespaces = comp->inScopeNs->list;
368 xpctxt->nsNr = comp->inScopeNs->xpathNumber;
369 } else {
370 xpctxt->namespaces = NULL;
371 xpctxt->nsNr = 0;
373 #else
374 xpctxt->namespaces = comp->nsList;
375 xpctxt->nsNr = comp->nsNr;
376 #endif
378 res = xmlXPathCompiledEval(comp->comp, xpctxt);
380 xpctxt->node = oldXPContextNode;
381 xpctxt->proximityPosition = oldXPProximityPosition;
382 xpctxt->contextSize = oldXPContextSize;
383 xpctxt->nsNr = oldXPNsNr;
384 xpctxt->namespaces = oldXPNamespaces;
386 return(res);
390 * xsltPreCompEvalToBoolean:
391 * @ctxt: transform context
392 * @node: context node
393 * @comp: precompiled expression
395 * Evaluate a precompiled XPath expression as boolean.
397 static int
398 xsltPreCompEvalToBoolean(xsltTransformContextPtr ctxt, xmlNodePtr node,
399 xsltStylePreCompPtr comp) {
400 int res;
401 xmlXPathContextPtr xpctxt;
402 xmlNodePtr oldXPContextNode;
403 xmlNsPtr *oldXPNamespaces;
404 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
406 xpctxt = ctxt->xpathCtxt;
407 oldXPContextNode = xpctxt->node;
408 oldXPProximityPosition = xpctxt->proximityPosition;
409 oldXPContextSize = xpctxt->contextSize;
410 oldXPNsNr = xpctxt->nsNr;
411 oldXPNamespaces = xpctxt->namespaces;
413 xpctxt->node = node;
414 #ifdef XSLT_REFACTORED
415 if (comp->inScopeNs != NULL) {
416 xpctxt->namespaces = comp->inScopeNs->list;
417 xpctxt->nsNr = comp->inScopeNs->xpathNumber;
418 } else {
419 xpctxt->namespaces = NULL;
420 xpctxt->nsNr = 0;
422 #else
423 xpctxt->namespaces = comp->nsList;
424 xpctxt->nsNr = comp->nsNr;
425 #endif
427 res = xmlXPathCompiledEvalToBoolean(comp->comp, xpctxt);
429 xpctxt->node = oldXPContextNode;
430 xpctxt->proximityPosition = oldXPProximityPosition;
431 xpctxt->contextSize = oldXPContextSize;
432 xpctxt->nsNr = oldXPNsNr;
433 xpctxt->namespaces = oldXPNamespaces;
435 return(res);
438 /************************************************************************
440 * XInclude default settings *
442 ************************************************************************/
444 static int xsltDoXIncludeDefault = 0;
447 * xsltSetXIncludeDefault:
448 * @xinclude: whether to do XInclude processing
450 * Set whether XInclude should be processed on document being loaded by default
452 void
453 xsltSetXIncludeDefault(int xinclude) {
454 xsltDoXIncludeDefault = (xinclude != 0);
458 * xsltGetXIncludeDefault:
460 * Provides the default state for XInclude processing
462 * Returns 0 if there is no processing 1 otherwise
465 xsltGetXIncludeDefault(void) {
466 return(xsltDoXIncludeDefault);
469 static unsigned long xsltDefaultTrace = (unsigned long) XSLT_TRACE_ALL;
472 * xsltDebugSetDefaultTrace:
473 * @val: tracing level mask
475 * Set the default debug tracing level mask
477 void xsltDebugSetDefaultTrace(xsltDebugTraceCodes val) {
478 xsltDefaultTrace = val;
482 * xsltDebugGetDefaultTrace:
484 * Get the current default debug tracing level mask
486 * Returns the current default debug tracing level mask
488 xsltDebugTraceCodes xsltDebugGetDefaultTrace(void) {
489 return xsltDefaultTrace;
492 /************************************************************************
494 * Handling of Transformation Contexts *
496 ************************************************************************/
498 static xsltTransformCachePtr
499 xsltTransformCacheCreate(void)
501 xsltTransformCachePtr ret;
503 ret = (xsltTransformCachePtr) xmlMalloc(sizeof(xsltTransformCache));
504 if (ret == NULL) {
505 xsltTransformError(NULL, NULL, NULL,
506 "xsltTransformCacheCreate : malloc failed\n");
507 return(NULL);
509 memset(ret, 0, sizeof(xsltTransformCache));
510 return(ret);
513 static void
514 xsltTransformCacheFree(xsltTransformCachePtr cache)
516 if (cache == NULL)
517 return;
519 * Free tree fragments.
521 if (cache->RVT) {
522 xmlDocPtr tmp, cur = cache->RVT;
523 while (cur) {
524 tmp = cur;
525 cur = (xmlDocPtr) cur->next;
526 if (tmp->_private != NULL) {
528 * Tree the document info.
530 xsltFreeDocumentKeys((xsltDocumentPtr) tmp->_private);
531 xmlFree(tmp->_private);
533 xmlFreeDoc(tmp);
537 * Free vars/params.
539 if (cache->stackItems) {
540 xsltStackElemPtr tmp, cur = cache->stackItems;
541 while (cur) {
542 tmp = cur;
543 cur = cur->next;
545 * REVISIT TODO: Should be call a destruction-function
546 * instead?
548 xmlFree(tmp);
551 xmlFree(cache);
555 * xsltNewTransformContext:
556 * @style: a parsed XSLT stylesheet
557 * @doc: the input document
559 * Create a new XSLT TransformContext
561 * Returns the newly allocated xsltTransformContextPtr or NULL in case of error
563 xsltTransformContextPtr
564 xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) {
565 xsltTransformContextPtr cur;
566 xsltDocumentPtr docu;
567 int i;
569 xsltInitGlobals();
571 cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext));
572 if (cur == NULL) {
573 xsltTransformError(NULL, NULL, (xmlNodePtr)doc,
574 "xsltNewTransformContext : malloc failed\n");
575 return(NULL);
577 memset(cur, 0, sizeof(xsltTransformContext));
579 cur->cache = xsltTransformCacheCreate();
580 if (cur->cache == NULL)
581 goto internal_err;
583 * setup of the dictionary must be done early as some of the
584 * processing later like key handling may need it.
586 cur->dict = xmlDictCreateSub(style->dict);
587 cur->internalized = ((style->internalized) && (cur->dict != NULL));
588 #ifdef WITH_XSLT_DEBUG
589 xsltGenericDebug(xsltGenericDebugContext,
590 "Creating sub-dictionary from stylesheet for transformation\n");
591 #endif
594 * initialize the template stack
596 cur->templTab = (xsltTemplatePtr *)
597 xmlMalloc(10 * sizeof(xsltTemplatePtr));
598 if (cur->templTab == NULL) {
599 xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
600 "xsltNewTransformContext: out of memory\n");
601 goto internal_err;
603 cur->templNr = 0;
604 cur->templMax = 5;
605 cur->templ = NULL;
606 cur->maxTemplateDepth = xsltMaxDepth;
609 * initialize the variables stack
611 cur->varsTab = (xsltStackElemPtr *)
612 xmlMalloc(10 * sizeof(xsltStackElemPtr));
613 if (cur->varsTab == NULL) {
614 xmlGenericError(xmlGenericErrorContext,
615 "xsltNewTransformContext: out of memory\n");
616 goto internal_err;
618 cur->varsNr = 0;
619 cur->varsMax = 10;
620 cur->vars = NULL;
621 cur->varsBase = 0;
622 cur->maxTemplateVars = xsltMaxVars;
625 * the profiling stack is not initialized by default
627 cur->profTab = NULL;
628 cur->profNr = 0;
629 cur->profMax = 0;
630 cur->prof = 0;
632 cur->style = style;
633 cur->xpathCtxt = xmlXPathNewContext(doc);
634 if (cur->xpathCtxt == NULL) {
635 xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
636 "xsltNewTransformContext : xmlXPathNewContext failed\n");
637 goto internal_err;
640 * Create an XPath cache.
642 if (xmlXPathContextSetCache(cur->xpathCtxt, 1, -1, 0) == -1)
643 goto internal_err;
645 * Initialize the extras array
647 if (style->extrasNr != 0) {
648 cur->extrasMax = style->extrasNr + 20;
649 cur->extras = (xsltRuntimeExtraPtr)
650 xmlMalloc(cur->extrasMax * sizeof(xsltRuntimeExtra));
651 if (cur->extras == NULL) {
652 xmlGenericError(xmlGenericErrorContext,
653 "xsltNewTransformContext: out of memory\n");
654 goto internal_err;
656 cur->extrasNr = style->extrasNr;
657 for (i = 0;i < cur->extrasMax;i++) {
658 cur->extras[i].info = NULL;
659 cur->extras[i].deallocate = NULL;
660 cur->extras[i].val.ptr = NULL;
662 } else {
663 cur->extras = NULL;
664 cur->extrasNr = 0;
665 cur->extrasMax = 0;
668 XSLT_REGISTER_VARIABLE_LOOKUP(cur);
669 XSLT_REGISTER_FUNCTION_LOOKUP(cur);
670 cur->xpathCtxt->nsHash = style->nsHash;
672 * Initialize the registered external modules
674 xsltInitCtxtExts(cur);
676 * Setup document element ordering for later efficiencies
677 * (bug 133289)
679 if (xslDebugStatus == XSLT_DEBUG_NONE)
680 xmlXPathOrderDocElems(doc);
682 * Must set parserOptions before calling xsltNewDocument
683 * (bug 164530)
685 cur->parserOptions = XSLT_PARSE_OPTIONS;
686 docu = xsltNewDocument(cur, doc);
687 if (docu == NULL) {
688 xsltTransformError(cur, NULL, (xmlNodePtr)doc,
689 "xsltNewTransformContext : xsltNewDocument failed\n");
690 goto internal_err;
692 docu->main = 1;
693 cur->document = docu;
694 cur->inst = NULL;
695 cur->outputFile = NULL;
696 cur->sec = xsltGetDefaultSecurityPrefs();
697 cur->debugStatus = xslDebugStatus;
698 cur->traceCode = (unsigned long*) &xsltDefaultTrace;
699 cur->xinclude = xsltGetXIncludeDefault();
700 cur->keyInitLevel = 0;
702 cur->newLocale = xsltNewLocale;
703 cur->freeLocale = xsltFreeLocale;
704 cur->genSortKey = xsltStrxfrm;
706 return(cur);
708 internal_err:
709 if (cur != NULL)
710 xsltFreeTransformContext(cur);
711 return(NULL);
715 * xsltFreeTransformContext:
716 * @ctxt: an XSLT transform context
718 * Free up the memory allocated by @ctxt
720 void
721 xsltFreeTransformContext(xsltTransformContextPtr ctxt) {
722 if (ctxt == NULL)
723 return;
726 * Shutdown the extension modules associated to the stylesheet
727 * used if needed.
729 xsltShutdownCtxtExts(ctxt);
731 if (ctxt->xpathCtxt != NULL) {
732 ctxt->xpathCtxt->nsHash = NULL;
733 xmlXPathFreeContext(ctxt->xpathCtxt);
735 if (ctxt->templTab != NULL)
736 xmlFree(ctxt->templTab);
737 if (ctxt->varsTab != NULL)
738 xmlFree(ctxt->varsTab);
739 if (ctxt->profTab != NULL)
740 xmlFree(ctxt->profTab);
741 if ((ctxt->extrasNr > 0) && (ctxt->extras != NULL)) {
742 int i;
744 for (i = 0;i < ctxt->extrasNr;i++) {
745 if ((ctxt->extras[i].deallocate != NULL) &&
746 (ctxt->extras[i].info != NULL))
747 ctxt->extras[i].deallocate(ctxt->extras[i].info);
749 xmlFree(ctxt->extras);
751 xsltFreeGlobalVariables(ctxt);
752 xsltFreeDocuments(ctxt);
753 xsltFreeCtxtExts(ctxt);
754 xsltFreeRVTs(ctxt);
755 xsltTransformCacheFree(ctxt->cache);
756 xmlDictFree(ctxt->dict);
757 #ifdef WITH_XSLT_DEBUG
758 xsltGenericDebug(xsltGenericDebugContext,
759 "freeing transformation dictionary\n");
760 #endif
761 memset(ctxt, -1, sizeof(xsltTransformContext));
762 xmlFree(ctxt);
765 /************************************************************************
767 * Copy of Nodes in an XSLT fashion *
769 ************************************************************************/
772 * xsltAddChild:
773 * @parent: the parent node
774 * @cur: the child node
776 * Wrapper version of xmlAddChild with a more consistent behaviour on
777 * error. One expect the use to be child = xsltAddChild(parent, child);
778 * and the routine will take care of not leaking on errors or node merge
780 * Returns the child is successfully attached or NULL if merged or freed
782 static xmlNodePtr
783 xsltAddChild(xmlNodePtr parent, xmlNodePtr cur) {
784 xmlNodePtr ret;
786 if (cur == NULL)
787 return(NULL);
788 if (parent == NULL) {
789 xmlFreeNode(cur);
790 return(NULL);
792 ret = xmlAddChild(parent, cur);
794 return(ret);
798 * xsltAddTextString:
799 * @ctxt: a XSLT process context
800 * @target: the text node where the text will be attached
801 * @string: the text string
802 * @len: the string length in byte
804 * Extend the current text node with the new string, it handles coalescing
806 * Returns: the text node
808 static xmlNodePtr
809 xsltAddTextString(xsltTransformContextPtr ctxt, xmlNodePtr target,
810 const xmlChar *string, int len) {
812 * optimization
814 if ((len <= 0) || (string == NULL) || (target == NULL))
815 return(target);
817 if (ctxt->lasttext == target->content) {
818 int minSize;
820 /* Check for integer overflow accounting for NUL terminator. */
821 if (len >= INT_MAX - ctxt->lasttuse) {
822 xsltTransformError(ctxt, NULL, target,
823 "xsltCopyText: text allocation failed\n");
824 return(NULL);
826 minSize = ctxt->lasttuse + len + 1;
828 if (ctxt->lasttsize < minSize) {
829 xmlChar *newbuf;
830 int size;
831 int extra;
833 /* Double buffer size but increase by at least 100 bytes. */
834 extra = minSize < 100 ? 100 : minSize;
836 /* Check for integer overflow. */
837 if (extra > INT_MAX - ctxt->lasttsize) {
838 size = INT_MAX;
840 else {
841 size = ctxt->lasttsize + extra;
844 newbuf = (xmlChar *) xmlRealloc(target->content,size);
845 if (newbuf == NULL) {
846 xsltTransformError(ctxt, NULL, target,
847 "xsltCopyText: text allocation failed\n");
848 return(NULL);
850 ctxt->lasttsize = size;
851 ctxt->lasttext = newbuf;
852 target->content = newbuf;
854 memcpy(&(target->content[ctxt->lasttuse]), string, len);
855 ctxt->lasttuse += len;
856 target->content[ctxt->lasttuse] = 0;
857 } else {
858 xmlNodeAddContent(target, string);
859 ctxt->lasttext = target->content;
860 len = xmlStrlen(target->content);
861 ctxt->lasttsize = len;
862 ctxt->lasttuse = len;
864 return(target);
868 * xsltCopyTextString:
869 * @ctxt: a XSLT process context
870 * @target: the element where the text will be attached
871 * @string: the text string
872 * @noescape: should disable-escaping be activated for this text node.
874 * Adds @string to a newly created or an existent text node child of
875 * @target.
877 * Returns: the text node, where the text content of @cur is copied to.
878 * NULL in case of API or internal errors.
880 xmlNodePtr
881 xsltCopyTextString(xsltTransformContextPtr ctxt, xmlNodePtr target,
882 const xmlChar *string, int noescape)
884 xmlNodePtr copy;
885 int len;
887 if (string == NULL)
888 return(NULL);
890 #ifdef WITH_XSLT_DEBUG_PROCESS
891 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
892 "xsltCopyTextString: copy text %s\n",
893 string));
894 #endif
897 * Play safe and reset the merging mechanism for every new
898 * target node.
900 if ((target == NULL) || (target->children == NULL)) {
901 ctxt->lasttext = NULL;
904 /* handle coalescing of text nodes here */
905 len = xmlStrlen(string);
906 if ((ctxt->type == XSLT_OUTPUT_XML) &&
907 (ctxt->style->cdataSection != NULL) &&
908 (target != NULL) &&
909 (target->type == XML_ELEMENT_NODE) &&
910 (((target->ns == NULL) &&
911 (xmlHashLookup2(ctxt->style->cdataSection,
912 target->name, NULL) != NULL)) ||
913 ((target->ns != NULL) &&
914 (xmlHashLookup2(ctxt->style->cdataSection,
915 target->name, target->ns->href) != NULL))))
918 * Process "cdata-section-elements".
920 if ((target->last != NULL) &&
921 (target->last->type == XML_CDATA_SECTION_NODE))
923 return(xsltAddTextString(ctxt, target->last, string, len));
925 copy = xmlNewCDataBlock(ctxt->output, string, len);
926 } else if (noescape) {
928 * Process "disable-output-escaping".
930 if ((target != NULL) && (target->last != NULL) &&
931 (target->last->type == XML_TEXT_NODE) &&
932 (target->last->name == xmlStringTextNoenc))
934 return(xsltAddTextString(ctxt, target->last, string, len));
936 copy = xmlNewTextLen(string, len);
937 if (copy != NULL)
938 copy->name = xmlStringTextNoenc;
939 } else {
941 * Default processing.
943 if ((target != NULL) && (target->last != NULL) &&
944 (target->last->type == XML_TEXT_NODE) &&
945 (target->last->name == xmlStringText)) {
946 return(xsltAddTextString(ctxt, target->last, string, len));
948 copy = xmlNewTextLen(string, len);
950 if (copy != NULL && target != NULL)
951 copy = xsltAddChild(target, copy);
952 if (copy != NULL) {
953 ctxt->lasttext = copy->content;
954 ctxt->lasttsize = len;
955 ctxt->lasttuse = len;
956 } else {
957 xsltTransformError(ctxt, NULL, target,
958 "xsltCopyTextString: text copy failed\n");
959 ctxt->lasttext = NULL;
961 return(copy);
965 * xsltCopyText:
966 * @ctxt: a XSLT process context
967 * @target: the element where the text will be attached
968 * @cur: the text or CDATA node
969 * @interned: the string is in the target doc dictionary
971 * Copy the text content of @cur and append it to @target's children.
973 * Returns: the text node, where the text content of @cur is copied to.
974 * NULL in case of API or internal errors.
976 static xmlNodePtr
977 xsltCopyText(xsltTransformContextPtr ctxt, xmlNodePtr target,
978 xmlNodePtr cur, int interned)
980 xmlNodePtr copy;
982 if ((cur->type != XML_TEXT_NODE) &&
983 (cur->type != XML_CDATA_SECTION_NODE))
984 return(NULL);
985 if (cur->content == NULL)
986 return(NULL);
988 #ifdef WITH_XSLT_DEBUG_PROCESS
989 if (cur->type == XML_CDATA_SECTION_NODE) {
990 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
991 "xsltCopyText: copy CDATA text %s\n",
992 cur->content));
993 } else if (cur->name == xmlStringTextNoenc) {
994 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
995 "xsltCopyText: copy unescaped text %s\n",
996 cur->content));
997 } else {
998 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
999 "xsltCopyText: copy text %s\n",
1000 cur->content));
1002 #endif
1005 * Play save and reset the merging mechanism for every new
1006 * target node.
1008 if ((target == NULL) || (target->children == NULL)) {
1009 ctxt->lasttext = NULL;
1012 if ((ctxt->style->cdataSection != NULL) &&
1013 (ctxt->type == XSLT_OUTPUT_XML) &&
1014 (target != NULL) &&
1015 (target->type == XML_ELEMENT_NODE) &&
1016 (((target->ns == NULL) &&
1017 (xmlHashLookup2(ctxt->style->cdataSection,
1018 target->name, NULL) != NULL)) ||
1019 ((target->ns != NULL) &&
1020 (xmlHashLookup2(ctxt->style->cdataSection,
1021 target->name, target->ns->href) != NULL))))
1024 * Process "cdata-section-elements".
1027 * OPTIMIZE TODO: xsltCopyText() is also used for attribute content.
1030 * TODO: Since this doesn't merge adjacent CDATA-section nodes,
1031 * we'll get: <![CDATA[x]]><!CDATA[y]]>.
1032 * TODO: Reported in #321505.
1034 if ((target->last != NULL) &&
1035 (target->last->type == XML_CDATA_SECTION_NODE))
1038 * Append to existing CDATA-section node.
1040 copy = xsltAddTextString(ctxt, target->last, cur->content,
1041 xmlStrlen(cur->content));
1042 goto exit;
1043 } else {
1044 unsigned int len;
1046 len = xmlStrlen(cur->content);
1047 copy = xmlNewCDataBlock(ctxt->output, cur->content, len);
1048 if (copy == NULL)
1049 goto exit;
1050 ctxt->lasttext = copy->content;
1051 ctxt->lasttsize = len;
1052 ctxt->lasttuse = len;
1054 } else if ((target != NULL) &&
1055 (target->last != NULL) &&
1056 /* both escaped or both non-escaped text-nodes */
1057 (((target->last->type == XML_TEXT_NODE) &&
1058 (target->last->name == cur->name)) ||
1059 /* non-escaped text nodes and CDATA-section nodes */
1060 (((target->last->type == XML_CDATA_SECTION_NODE) &&
1061 (cur->name == xmlStringTextNoenc)))))
1064 * we are appending to an existing text node
1066 copy = xsltAddTextString(ctxt, target->last, cur->content,
1067 xmlStrlen(cur->content));
1068 goto exit;
1069 } else if ((interned) && (target != NULL) &&
1070 (target->doc != NULL) &&
1071 (target->doc->dict == ctxt->dict))
1074 * TODO: DO we want to use this also for "text" output?
1076 copy = xmlNewTextLen(NULL, 0);
1077 if (copy == NULL)
1078 goto exit;
1079 if (cur->name == xmlStringTextNoenc)
1080 copy->name = xmlStringTextNoenc;
1083 * Must confirm that content is in dict (bug 302821)
1084 * TODO: This check should be not needed for text coming
1085 * from the stylesheets
1087 if (xmlDictOwns(ctxt->dict, cur->content))
1088 copy->content = cur->content;
1089 else {
1090 if ((copy->content = xmlStrdup(cur->content)) == NULL) {
1091 xmlFreeNode(copy);
1092 return NULL;
1096 ctxt->lasttext = NULL;
1097 } else {
1099 * normal processing. keep counters to extend the text node
1100 * in xsltAddTextString if needed.
1102 unsigned int len;
1104 len = xmlStrlen(cur->content);
1105 copy = xmlNewTextLen(cur->content, len);
1106 if (copy == NULL)
1107 goto exit;
1108 if (cur->name == xmlStringTextNoenc)
1109 copy->name = xmlStringTextNoenc;
1110 ctxt->lasttext = copy->content;
1111 ctxt->lasttsize = len;
1112 ctxt->lasttuse = len;
1114 if (copy != NULL) {
1115 if (target != NULL) {
1116 copy->doc = target->doc;
1118 * MAYBE TODO: Maybe we should reset the ctxt->lasttext here
1119 * to ensure that the optimized text-merging mechanism
1120 * won't interfere with normal node-merging in any case.
1122 copy = xsltAddChild(target, copy);
1124 } else {
1125 xsltTransformError(ctxt, NULL, target,
1126 "xsltCopyText: text copy failed\n");
1129 exit:
1130 if ((copy == NULL) || (copy->content == NULL)) {
1131 xsltTransformError(ctxt, NULL, target,
1132 "Internal error in xsltCopyText(): "
1133 "Failed to copy the string.\n");
1134 ctxt->state = XSLT_STATE_STOPPED;
1136 return(copy);
1140 * xsltShallowCopyAttr:
1141 * @ctxt: a XSLT process context
1142 * @invocNode: responsible node in the stylesheet; used for error reports
1143 * @target: the element where the attribute will be grafted
1144 * @attr: the attribute to be copied
1146 * Do a copy of an attribute.
1147 * Called by:
1148 * - xsltCopyTree()
1149 * - xsltCopyOf()
1150 * - xsltCopy()
1152 * Returns: a new xmlAttrPtr, or NULL in case of error.
1154 static xmlAttrPtr
1155 xsltShallowCopyAttr(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
1156 xmlNodePtr target, xmlAttrPtr attr)
1158 xmlAttrPtr copy;
1159 xmlChar *value;
1161 if (attr == NULL)
1162 return(NULL);
1164 if (target->type != XML_ELEMENT_NODE) {
1165 xsltTransformError(ctxt, NULL, invocNode,
1166 "Cannot add an attribute node to a non-element node.\n");
1167 return(NULL);
1170 if (target->children != NULL) {
1171 xsltTransformError(ctxt, NULL, invocNode,
1172 "Attribute nodes must be added before "
1173 "any child nodes to an element.\n");
1174 return(NULL);
1177 value = xmlNodeListGetString(attr->doc, attr->children, 1);
1178 if (attr->ns != NULL) {
1179 xmlNsPtr ns;
1181 ns = xsltGetSpecialNamespace(ctxt, invocNode,
1182 attr->ns->href, attr->ns->prefix, target);
1183 if (ns == NULL) {
1184 xsltTransformError(ctxt, NULL, invocNode,
1185 "Namespace fixup error: Failed to acquire an in-scope "
1186 "namespace binding of the copied attribute '{%s}%s'.\n",
1187 attr->ns->href, attr->name);
1189 * TODO: Should we just stop here?
1193 * Note that xmlSetNsProp() will take care of duplicates
1194 * and assigns the new namespace even to a duplicate.
1196 copy = xmlSetNsProp(target, ns, attr->name, value);
1197 } else {
1198 copy = xmlSetNsProp(target, NULL, attr->name, value);
1200 if (value != NULL)
1201 xmlFree(value);
1203 if (copy == NULL)
1204 return(NULL);
1206 #if 0
1208 * NOTE: This was optimized according to bug #342695.
1209 * TODO: Can this further be optimized, if source and target
1210 * share the same dict and attr->children is just 1 text node
1211 * which is in the dict? How probable is such a case?
1214 * TODO: Do we need to create an empty text node if the value
1215 * is the empty string?
1217 value = xmlNodeListGetString(attr->doc, attr->children, 1);
1218 if (value != NULL) {
1219 txtNode = xmlNewDocText(target->doc, NULL);
1220 if (txtNode == NULL)
1221 return(NULL);
1222 if ((target->doc != NULL) &&
1223 (target->doc->dict != NULL))
1225 txtNode->content =
1226 (xmlChar *) xmlDictLookup(target->doc->dict,
1227 BAD_CAST value, -1);
1228 xmlFree(value);
1229 } else
1230 txtNode->content = value;
1231 copy->children = txtNode;
1233 #endif
1235 return(copy);
1239 * xsltCopyAttrListNoOverwrite:
1240 * @ctxt: a XSLT process context
1241 * @invocNode: responsible node in the stylesheet; used for error reports
1242 * @target: the element where the new attributes will be grafted
1243 * @attr: the first attribute in the list to be copied
1245 * Copies a list of attribute nodes, starting with @attr, over to the
1246 * @target element node.
1248 * Called by:
1249 * - xsltCopyTree()
1251 * Returns 0 on success and -1 on errors and internal errors.
1253 static int
1254 xsltCopyAttrListNoOverwrite(xsltTransformContextPtr ctxt,
1255 xmlNodePtr invocNode,
1256 xmlNodePtr target, xmlAttrPtr attr)
1258 xmlAttrPtr copy;
1259 xmlNsPtr origNs = NULL, copyNs = NULL;
1260 xmlChar *value;
1263 * Don't use xmlCopyProp() here, since it will try to
1264 * reconciliate namespaces.
1266 while (attr != NULL) {
1268 * Find a namespace node in the tree of @target.
1269 * Avoid searching for the same ns.
1271 if (attr->ns != origNs) {
1272 origNs = attr->ns;
1273 if (attr->ns != NULL) {
1274 copyNs = xsltGetSpecialNamespace(ctxt, invocNode,
1275 attr->ns->href, attr->ns->prefix, target);
1276 if (copyNs == NULL)
1277 return(-1);
1278 } else
1279 copyNs = NULL;
1282 * If attribute has a value, we need to copy it (watching out
1283 * for possible entities)
1285 if ((attr->children) && (attr->children->type == XML_TEXT_NODE) &&
1286 (attr->children->next == NULL)) {
1287 copy = xmlNewNsProp(target, copyNs, attr->name,
1288 attr->children->content);
1289 } else if (attr->children != NULL) {
1290 value = xmlNodeListGetString(attr->doc, attr->children, 1);
1291 copy = xmlNewNsProp(target, copyNs, attr->name, BAD_CAST value);
1292 xmlFree(value);
1293 } else {
1294 copy = xmlNewNsProp(target, copyNs, attr->name, NULL);
1297 if (copy == NULL)
1298 return(-1);
1300 attr = attr->next;
1302 return(0);
1306 * xsltShallowCopyElem:
1307 * @ctxt: the XSLT process context
1308 * @node: the element node in the source tree
1309 * or the Literal Result Element
1310 * @insert: the parent in the result tree
1311 * @isLRE: if @node is a Literal Result Element
1313 * Make a copy of the element node @node
1314 * and insert it as last child of @insert.
1316 * URGENT TODO: The problem with this one (for the non-refactored code)
1317 * is that it is used for both, Literal Result Elements *and*
1318 * copying input nodes.
1320 * BIG NOTE: This is only called for XML_ELEMENT_NODEs.
1322 * Called from:
1323 * xsltApplySequenceConstructor()
1324 * (for Literal Result Elements - which is a problem)
1325 * xsltCopy() (for shallow-copying elements via xsl:copy)
1327 * Returns a pointer to the new node, or NULL in case of error
1329 static xmlNodePtr
1330 xsltShallowCopyElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
1331 xmlNodePtr insert, int isLRE)
1333 xmlNodePtr copy;
1335 if ((node->type == XML_DTD_NODE) || (insert == NULL))
1336 return(NULL);
1337 if ((node->type == XML_TEXT_NODE) ||
1338 (node->type == XML_CDATA_SECTION_NODE))
1339 return(xsltCopyText(ctxt, insert, node, 0));
1341 copy = xmlDocCopyNode(node, insert->doc, 0);
1342 if (copy != NULL) {
1343 copy->doc = ctxt->output;
1344 copy = xsltAddChild(insert, copy);
1345 if (copy == NULL) {
1346 xsltTransformError(ctxt, NULL, node,
1347 "xsltShallowCopyElem: copy failed\n");
1348 return (copy);
1351 if (node->type == XML_ELEMENT_NODE) {
1353 * Add namespaces as they are needed
1355 if (node->nsDef != NULL) {
1357 * TODO: Remove the LRE case in the refactored code
1358 * gets enabled.
1360 if (isLRE)
1361 xsltCopyNamespaceList(ctxt, copy, node->nsDef);
1362 else
1363 xsltCopyNamespaceListInternal(copy, node->nsDef);
1367 * URGENT TODO: The problem with this is that it does not
1368 * copy over all namespace nodes in scope.
1369 * The damn thing about this is, that we would need to
1370 * use the xmlGetNsList(), for every single node; this is
1371 * also done in xsltCopyTree(), but only for the top node.
1373 if (node->ns != NULL) {
1374 if (isLRE) {
1376 * REVISIT TODO: Since the non-refactored code still does
1377 * ns-aliasing, we need to call xsltGetNamespace() here.
1378 * Remove this when ready.
1380 copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy);
1381 } else {
1382 copy->ns = xsltGetSpecialNamespace(ctxt,
1383 node, node->ns->href, node->ns->prefix, copy);
1386 } else if ((insert->type == XML_ELEMENT_NODE) &&
1387 (insert->ns != NULL))
1390 * "Undeclare" the default namespace.
1392 xsltGetSpecialNamespace(ctxt, node, NULL, NULL, copy);
1395 } else {
1396 xsltTransformError(ctxt, NULL, node,
1397 "xsltShallowCopyElem: copy %s failed\n", node->name);
1399 return(copy);
1403 * xsltCopyTreeList:
1404 * @ctxt: a XSLT process context
1405 * @invocNode: responsible node in the stylesheet; used for error reports
1406 * @list: the list of element nodes in the source tree.
1407 * @insert: the parent in the result tree.
1408 * @isLRE: is this a literal result element list
1409 * @topElemVisited: indicates if a top-most element was already processed
1411 * Make a copy of the full list of tree @list
1412 * and insert it as last children of @insert
1414 * NOTE: Not to be used for Literal Result Elements.
1416 * Used by:
1417 * - xsltCopyOf()
1419 * Returns a pointer to the new list, or NULL in case of error
1421 static xmlNodePtr
1422 xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
1423 xmlNodePtr list,
1424 xmlNodePtr insert, int isLRE, int topElemVisited)
1426 xmlNodePtr copy, ret = NULL;
1428 while (list != NULL) {
1429 copy = xsltCopyTree(ctxt, invocNode,
1430 list, insert, isLRE, topElemVisited);
1431 if (copy != NULL) {
1432 if (ret == NULL) {
1433 ret = copy;
1436 list = list->next;
1438 return(ret);
1442 * xsltCopyNamespaceListInternal:
1443 * @node: the target node
1444 * @cur: the first namespace
1446 * Do a copy of a namespace list. If @node is non-NULL the
1447 * new namespaces are added automatically.
1448 * Called by:
1449 * xsltCopyTree()
1451 * QUESTION: What is the exact difference between this function
1452 * and xsltCopyNamespaceList() in "namespaces.c"?
1453 * ANSWER: xsltCopyNamespaceList() tries to apply ns-aliases.
1455 * Returns: a new xmlNsPtr, or NULL in case of error.
1457 static xmlNsPtr
1458 xsltCopyNamespaceListInternal(xmlNodePtr elem, xmlNsPtr ns) {
1459 xmlNsPtr ret = NULL;
1460 xmlNsPtr p = NULL, q, luNs;
1462 if (ns == NULL)
1463 return(NULL);
1465 * One can add namespaces only on element nodes
1467 if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE))
1468 elem = NULL;
1470 do {
1471 if (ns->type != XML_NAMESPACE_DECL)
1472 break;
1474 * Avoid duplicating namespace declarations on the tree.
1476 if (elem != NULL) {
1477 if ((elem->ns != NULL) &&
1478 xmlStrEqual(elem->ns->prefix, ns->prefix) &&
1479 xmlStrEqual(elem->ns->href, ns->href))
1481 ns = ns->next;
1482 continue;
1484 luNs = xmlSearchNs(elem->doc, elem, ns->prefix);
1485 if ((luNs != NULL) && (xmlStrEqual(luNs->href, ns->href)))
1487 ns = ns->next;
1488 continue;
1491 q = xmlNewNs(elem, ns->href, ns->prefix);
1492 if (p == NULL) {
1493 ret = p = q;
1494 } else if (q != NULL) {
1495 p->next = q;
1496 p = q;
1498 ns = ns->next;
1499 } while (ns != NULL);
1500 return(ret);
1504 * xsltShallowCopyNsNode:
1505 * @ctxt: the XSLT transformation context
1506 * @invocNode: responsible node in the stylesheet; used for error reports
1507 * @insert: the target element node in the result tree
1508 * @ns: the namespace node
1510 * This is used for copying ns-nodes with xsl:copy-of and xsl:copy.
1512 * Returns a new/existing ns-node, or NULL.
1514 static xmlNsPtr
1515 xsltShallowCopyNsNode(xsltTransformContextPtr ctxt,
1516 xmlNodePtr invocNode,
1517 xmlNodePtr insert,
1518 xmlNsPtr ns)
1521 * TODO: Contrary to header comments, this is declared as int.
1522 * be modified to return a node pointer, or NULL if any error
1524 xmlNsPtr tmpns;
1526 if ((insert == NULL) || (insert->type != XML_ELEMENT_NODE))
1527 return(NULL);
1529 if (insert->children != NULL) {
1530 xsltTransformError(ctxt, NULL, invocNode,
1531 "Namespace nodes must be added before "
1532 "any child nodes are added to an element.\n");
1533 return(NULL);
1536 * BIG NOTE: Xalan-J simply overwrites any ns-decls with
1537 * an equal prefix. We definitively won't do that.
1539 * MSXML 4.0 and the .NET ignores ns-decls for which an
1540 * equal prefix is already in use.
1542 * Saxon raises an error like:
1543 * "net.sf.saxon.xpath.DynamicError: Cannot create two namespace
1544 * nodes with the same name".
1546 * NOTE: We'll currently follow MSXML here.
1547 * REVISIT TODO: Check if it's better to follow Saxon here.
1549 if (ns->prefix == NULL) {
1551 * If we are adding ns-nodes to an element using e.g.
1552 * <xsl:copy-of select="/foo/namespace::*">, then we need
1553 * to ensure that we don't incorrectly declare a default
1554 * namespace on an element in no namespace, which otherwise
1555 * would move the element incorrectly into a namespace, if
1556 * the node tree is serialized.
1558 if (insert->ns == NULL)
1559 goto occupied;
1560 } else if ((ns->prefix[0] == 'x') &&
1561 xmlStrEqual(ns->prefix, BAD_CAST "xml"))
1564 * The XML namespace is built in.
1566 return(NULL);
1569 if (insert->nsDef != NULL) {
1570 tmpns = insert->nsDef;
1571 do {
1572 if ((tmpns->prefix == NULL) == (ns->prefix == NULL)) {
1573 if ((tmpns->prefix == ns->prefix) ||
1574 xmlStrEqual(tmpns->prefix, ns->prefix))
1577 * Same prefix.
1579 if (xmlStrEqual(tmpns->href, ns->href))
1580 return(NULL);
1581 goto occupied;
1584 tmpns = tmpns->next;
1585 } while (tmpns != NULL);
1587 tmpns = xmlSearchNs(insert->doc, insert, ns->prefix);
1588 if ((tmpns != NULL) && xmlStrEqual(tmpns->href, ns->href))
1589 return(NULL);
1591 * Declare a new namespace.
1592 * TODO: The problem (wrt efficiency) with this xmlNewNs() is
1593 * that it will again search the already declared namespaces
1594 * for a duplicate :-/
1596 return(xmlNewNs(insert, ns->href, ns->prefix));
1598 occupied:
1600 * TODO: We could as well raise an error here (like Saxon does),
1601 * or at least generate a warning.
1603 return(NULL);
1607 * xsltCopyTree:
1608 * @ctxt: the XSLT transformation context
1609 * @invocNode: responsible node in the stylesheet; used for error reports
1610 * @node: the element node in the source tree
1611 * @insert: the parent in the result tree
1612 * @isLRE: indicates if @node is a Literal Result Element
1613 * @topElemVisited: indicates if a top-most element was already processed
1615 * Make a copy of the full tree under the element node @node
1616 * and insert it as last child of @insert
1618 * NOTE: Not to be used for Literal Result Elements.
1620 * Used by:
1621 * - xsltCopyOf()
1623 * Returns a pointer to the new tree, or NULL in case of error
1625 static xmlNodePtr
1626 xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
1627 xmlNodePtr node, xmlNodePtr insert, int isLRE,
1628 int topElemVisited)
1630 xmlNodePtr copy;
1632 if (node == NULL)
1633 return(NULL);
1634 switch (node->type) {
1635 case XML_ELEMENT_NODE:
1636 case XML_ENTITY_REF_NODE:
1637 case XML_ENTITY_NODE:
1638 case XML_PI_NODE:
1639 case XML_COMMENT_NODE:
1640 case XML_DOCUMENT_NODE:
1641 case XML_HTML_DOCUMENT_NODE:
1642 #ifdef LIBXML_DOCB_ENABLED
1643 case XML_DOCB_DOCUMENT_NODE:
1644 #endif
1645 break;
1646 case XML_TEXT_NODE: {
1647 int noenc = (node->name == xmlStringTextNoenc);
1648 return(xsltCopyTextString(ctxt, insert, node->content, noenc));
1650 case XML_CDATA_SECTION_NODE:
1651 return(xsltCopyTextString(ctxt, insert, node->content, 0));
1652 case XML_ATTRIBUTE_NODE:
1653 return((xmlNodePtr)
1654 xsltShallowCopyAttr(ctxt, invocNode, insert, (xmlAttrPtr) node));
1655 case XML_NAMESPACE_DECL:
1656 return((xmlNodePtr) xsltShallowCopyNsNode(ctxt, invocNode,
1657 insert, (xmlNsPtr) node));
1659 case XML_DOCUMENT_TYPE_NODE:
1660 case XML_DOCUMENT_FRAG_NODE:
1661 case XML_NOTATION_NODE:
1662 case XML_DTD_NODE:
1663 case XML_ELEMENT_DECL:
1664 case XML_ATTRIBUTE_DECL:
1665 case XML_ENTITY_DECL:
1666 case XML_XINCLUDE_START:
1667 case XML_XINCLUDE_END:
1668 return(NULL);
1670 if (XSLT_IS_RES_TREE_FRAG(node)) {
1671 if (node->children != NULL)
1672 copy = xsltCopyTreeList(ctxt, invocNode,
1673 node->children, insert, 0, 0);
1674 else
1675 copy = NULL;
1676 return(copy);
1678 copy = xmlDocCopyNode(node, insert->doc, 0);
1679 if (copy != NULL) {
1680 copy->doc = ctxt->output;
1681 copy = xsltAddChild(insert, copy);
1682 if (copy == NULL) {
1683 xsltTransformError(ctxt, NULL, invocNode,
1684 "xsltCopyTree: Copying of '%s' failed.\n", node->name);
1685 return (copy);
1688 * The node may have been coalesced into another text node.
1690 if (insert->last != copy)
1691 return(insert->last);
1692 copy->next = NULL;
1694 if (node->type == XML_ELEMENT_NODE) {
1696 * Copy in-scope namespace nodes.
1698 * REVISIT: Since we try to reuse existing in-scope ns-decls by
1699 * using xmlSearchNsByHref(), this will eventually change
1700 * the prefix of an original ns-binding; thus it might
1701 * break QNames in element/attribute content.
1702 * OPTIMIZE TODO: If we had a xmlNsPtr * on the transformation
1703 * context, plus a ns-lookup function, which writes directly
1704 * to a given list, then we wouldn't need to create/free the
1705 * nsList every time.
1707 if ((topElemVisited == 0) &&
1708 (node->parent != NULL) &&
1709 (node->parent->type != XML_DOCUMENT_NODE) &&
1710 (node->parent->type != XML_HTML_DOCUMENT_NODE))
1712 xmlNsPtr *nsList, *curns, ns;
1715 * If this is a top-most element in a tree to be
1716 * copied, then we need to ensure that all in-scope
1717 * namespaces are copied over. For nodes deeper in the
1718 * tree, it is sufficient to reconcile only the ns-decls
1719 * (node->nsDef entries).
1722 nsList = xmlGetNsList(node->doc, node);
1723 if (nsList != NULL) {
1724 curns = nsList;
1725 do {
1727 * Search by prefix first in order to break as less
1728 * QNames in element/attribute content as possible.
1730 ns = xmlSearchNs(insert->doc, insert,
1731 (*curns)->prefix);
1733 if ((ns == NULL) ||
1734 (! xmlStrEqual(ns->href, (*curns)->href)))
1736 ns = NULL;
1738 * Search by namespace name.
1739 * REVISIT TODO: Currently disabled.
1741 #if 0
1742 ns = xmlSearchNsByHref(insert->doc,
1743 insert, (*curns)->href);
1744 #endif
1746 if (ns == NULL) {
1748 * Declare a new namespace on the copied element.
1750 ns = xmlNewNs(copy, (*curns)->href,
1751 (*curns)->prefix);
1752 /* TODO: Handle errors */
1754 if (node->ns == *curns) {
1756 * If this was the original's namespace then set
1757 * the generated counterpart on the copy.
1759 copy->ns = ns;
1761 curns++;
1762 } while (*curns != NULL);
1763 xmlFree(nsList);
1765 } else if (node->nsDef != NULL) {
1767 * Copy over all namespace declaration attributes.
1769 if (node->nsDef != NULL) {
1770 if (isLRE)
1771 xsltCopyNamespaceList(ctxt, copy, node->nsDef);
1772 else
1773 xsltCopyNamespaceListInternal(copy, node->nsDef);
1777 * Set the namespace.
1779 if (node->ns != NULL) {
1780 if (copy->ns == NULL) {
1782 * This will map copy->ns to one of the newly created
1783 * in-scope ns-decls, OR create a new ns-decl on @copy.
1785 copy->ns = xsltGetSpecialNamespace(ctxt, invocNode,
1786 node->ns->href, node->ns->prefix, copy);
1788 } else if ((insert->type == XML_ELEMENT_NODE) &&
1789 (insert->ns != NULL))
1792 * "Undeclare" the default namespace on @copy with xmlns="".
1794 xsltGetSpecialNamespace(ctxt, invocNode, NULL, NULL, copy);
1797 * Copy attribute nodes.
1799 if (node->properties != NULL) {
1800 xsltCopyAttrListNoOverwrite(ctxt, invocNode,
1801 copy, node->properties);
1803 if (topElemVisited == 0)
1804 topElemVisited = 1;
1807 * Copy the subtree.
1809 if (node->children != NULL) {
1810 xsltCopyTreeList(ctxt, invocNode,
1811 node->children, copy, isLRE, topElemVisited);
1813 } else {
1814 xsltTransformError(ctxt, NULL, invocNode,
1815 "xsltCopyTree: Copying of '%s' failed.\n", node->name);
1817 return(copy);
1820 /************************************************************************
1822 * Error/fallback processing *
1824 ************************************************************************/
1827 * xsltApplyFallbacks:
1828 * @ctxt: a XSLT process context
1829 * @node: the node in the source tree.
1830 * @inst: the node generating the error
1832 * Process possible xsl:fallback nodes present under @inst
1834 * Returns the number of xsl:fallback element found and processed
1836 static int
1837 xsltApplyFallbacks(xsltTransformContextPtr ctxt, xmlNodePtr node,
1838 xmlNodePtr inst) {
1840 xmlNodePtr child;
1841 int ret = 0;
1843 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) ||
1844 (inst->children == NULL))
1845 return(0);
1847 child = inst->children;
1848 while (child != NULL) {
1849 if ((IS_XSLT_ELEM(child)) &&
1850 (xmlStrEqual(child->name, BAD_CAST "fallback"))) {
1851 #ifdef WITH_XSLT_DEBUG_PARSING
1852 xsltGenericDebug(xsltGenericDebugContext,
1853 "applying xsl:fallback\n");
1854 #endif
1855 ret++;
1856 xsltApplySequenceConstructor(ctxt, node, child->children,
1857 NULL);
1859 child = child->next;
1861 return(ret);
1864 /************************************************************************
1866 * Default processing *
1868 ************************************************************************/
1871 * xsltDefaultProcessOneNode:
1872 * @ctxt: a XSLT process context
1873 * @node: the node in the source tree.
1874 * @params: extra parameters passed to the template if any
1876 * Process the source node with the default built-in template rule:
1877 * <xsl:template match="*|/">
1878 * <xsl:apply-templates/>
1879 * </xsl:template>
1881 * and
1883 * <xsl:template match="text()|@*">
1884 * <xsl:value-of select="."/>
1885 * </xsl:template>
1887 * Note also that namespace declarations are copied directly:
1889 * the built-in template rule is the only template rule that is applied
1890 * for namespace nodes.
1892 static void
1893 xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
1894 xsltStackElemPtr params) {
1895 xmlNodePtr copy;
1896 xmlNodePtr cur;
1897 int nbchild = 0, oldSize;
1898 int childno = 0, oldPos;
1899 xsltTemplatePtr template;
1901 CHECK_STOPPED;
1903 * Handling of leaves
1905 switch (node->type) {
1906 case XML_DOCUMENT_NODE:
1907 case XML_HTML_DOCUMENT_NODE:
1908 case XML_ELEMENT_NODE:
1909 break;
1910 case XML_CDATA_SECTION_NODE:
1911 #ifdef WITH_XSLT_DEBUG_PROCESS
1912 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1913 "xsltDefaultProcessOneNode: copy CDATA %s\n",
1914 node->content));
1915 #endif
1916 copy = xsltCopyText(ctxt, ctxt->insert, node, 0);
1917 if (copy == NULL) {
1918 xsltTransformError(ctxt, NULL, node,
1919 "xsltDefaultProcessOneNode: cdata copy failed\n");
1921 return;
1922 case XML_TEXT_NODE:
1923 #ifdef WITH_XSLT_DEBUG_PROCESS
1924 if (node->content == NULL) {
1925 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1926 "xsltDefaultProcessOneNode: copy empty text\n"));
1927 return;
1928 } else {
1929 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1930 "xsltDefaultProcessOneNode: copy text %s\n",
1931 node->content));
1933 #endif
1934 copy = xsltCopyText(ctxt, ctxt->insert, node, 0);
1935 if (copy == NULL) {
1936 xsltTransformError(ctxt, NULL, node,
1937 "xsltDefaultProcessOneNode: text copy failed\n");
1939 return;
1940 case XML_ATTRIBUTE_NODE:
1941 cur = node->children;
1942 while ((cur != NULL) && (cur->type != XML_TEXT_NODE))
1943 cur = cur->next;
1944 if (cur == NULL) {
1945 xsltTransformError(ctxt, NULL, node,
1946 "xsltDefaultProcessOneNode: no text for attribute\n");
1947 } else {
1948 #ifdef WITH_XSLT_DEBUG_PROCESS
1949 if (cur->content == NULL) {
1950 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1951 "xsltDefaultProcessOneNode: copy empty text\n"));
1952 } else {
1953 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1954 "xsltDefaultProcessOneNode: copy text %s\n",
1955 cur->content));
1957 #endif
1958 copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
1959 if (copy == NULL) {
1960 xsltTransformError(ctxt, NULL, node,
1961 "xsltDefaultProcessOneNode: text copy failed\n");
1964 return;
1965 default:
1966 return;
1969 * Handling of Elements: first pass, counting
1971 cur = node->children;
1972 while (cur != NULL) {
1973 if (IS_XSLT_REAL_NODE(cur))
1974 nbchild++;
1975 cur = cur->next;
1979 * Handling of Elements: second pass, actual processing
1981 * Note that params are passed to the next template. This matches
1982 * XSLT 2.0 behavior but doesn't conform to XSLT 1.0.
1984 oldSize = ctxt->xpathCtxt->contextSize;
1985 oldPos = ctxt->xpathCtxt->proximityPosition;
1986 cur = node->children;
1987 while (cur != NULL) {
1988 childno++;
1989 switch (cur->type) {
1990 case XML_DOCUMENT_NODE:
1991 case XML_HTML_DOCUMENT_NODE:
1992 case XML_ELEMENT_NODE:
1993 ctxt->xpathCtxt->contextSize = nbchild;
1994 ctxt->xpathCtxt->proximityPosition = childno;
1995 xsltProcessOneNode(ctxt, cur, params);
1996 break;
1997 case XML_CDATA_SECTION_NODE:
1998 template = xsltGetTemplate(ctxt, cur, NULL);
1999 if (template) {
2000 #ifdef WITH_XSLT_DEBUG_PROCESS
2001 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2002 "xsltDefaultProcessOneNode: applying template for CDATA %s\n",
2003 cur->content));
2004 #endif
2006 * Instantiate the xsl:template.
2008 xsltApplyXSLTTemplate(ctxt, cur, template->content,
2009 template, params);
2010 } else /* if (ctxt->mode == NULL) */ {
2011 #ifdef WITH_XSLT_DEBUG_PROCESS
2012 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2013 "xsltDefaultProcessOneNode: copy CDATA %s\n",
2014 cur->content));
2015 #endif
2016 copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
2017 if (copy == NULL) {
2018 xsltTransformError(ctxt, NULL, cur,
2019 "xsltDefaultProcessOneNode: cdata copy failed\n");
2022 break;
2023 case XML_TEXT_NODE:
2024 template = xsltGetTemplate(ctxt, cur, NULL);
2025 if (template) {
2026 #ifdef WITH_XSLT_DEBUG_PROCESS
2027 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2028 "xsltDefaultProcessOneNode: applying template for text %s\n",
2029 cur->content));
2030 #endif
2031 ctxt->xpathCtxt->contextSize = nbchild;
2032 ctxt->xpathCtxt->proximityPosition = childno;
2034 * Instantiate the xsl:template.
2036 xsltApplyXSLTTemplate(ctxt, cur, template->content,
2037 template, params);
2038 } else /* if (ctxt->mode == NULL) */ {
2039 #ifdef WITH_XSLT_DEBUG_PROCESS
2040 if (cur->content == NULL) {
2041 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2042 "xsltDefaultProcessOneNode: copy empty text\n"));
2043 } else {
2044 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2045 "xsltDefaultProcessOneNode: copy text %s\n",
2046 cur->content));
2048 #endif
2049 copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
2050 if (copy == NULL) {
2051 xsltTransformError(ctxt, NULL, cur,
2052 "xsltDefaultProcessOneNode: text copy failed\n");
2055 break;
2056 case XML_PI_NODE:
2057 case XML_COMMENT_NODE:
2058 template = xsltGetTemplate(ctxt, cur, NULL);
2059 if (template) {
2060 #ifdef WITH_XSLT_DEBUG_PROCESS
2061 if (cur->type == XML_PI_NODE) {
2062 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2063 "xsltDefaultProcessOneNode: template found for PI %s\n",
2064 cur->name));
2065 } else if (cur->type == XML_COMMENT_NODE) {
2066 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2067 "xsltDefaultProcessOneNode: template found for comment\n"));
2069 #endif
2070 ctxt->xpathCtxt->contextSize = nbchild;
2071 ctxt->xpathCtxt->proximityPosition = childno;
2073 * Instantiate the xsl:template.
2075 xsltApplyXSLTTemplate(ctxt, cur, template->content,
2076 template, params);
2078 break;
2079 default:
2080 break;
2082 cur = cur->next;
2084 ctxt->xpathCtxt->contextSize = oldSize;
2085 ctxt->xpathCtxt->proximityPosition = oldPos;
2089 * xsltProcessOneNode:
2090 * @ctxt: a XSLT process context
2091 * @contextNode: the "current node" in the source tree
2092 * @withParams: extra parameters (e.g. xsl:with-param) passed to the
2093 * template if any
2095 * Process the source node.
2097 void
2098 xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
2099 xsltStackElemPtr withParams)
2101 xsltTemplatePtr templ;
2102 xmlNodePtr oldNode;
2104 templ = xsltGetTemplate(ctxt, contextNode, NULL);
2106 * If no template is found, apply the default rule.
2108 if (templ == NULL) {
2109 #ifdef WITH_XSLT_DEBUG_PROCESS
2110 if (contextNode->type == XML_DOCUMENT_NODE) {
2111 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2112 "xsltProcessOneNode: no template found for /\n"));
2113 } else if (contextNode->type == XML_CDATA_SECTION_NODE) {
2114 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2115 "xsltProcessOneNode: no template found for CDATA\n"));
2116 } else if (contextNode->type == XML_ATTRIBUTE_NODE) {
2117 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2118 "xsltProcessOneNode: no template found for attribute %s\n",
2119 ((xmlAttrPtr) contextNode)->name));
2120 } else {
2121 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2122 "xsltProcessOneNode: no template found for %s\n", contextNode->name));
2124 #endif
2125 oldNode = ctxt->node;
2126 ctxt->node = contextNode;
2127 xsltDefaultProcessOneNode(ctxt, contextNode, withParams);
2128 ctxt->node = oldNode;
2129 return;
2132 if (contextNode->type == XML_ATTRIBUTE_NODE) {
2133 xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule;
2135 * Set the "current template rule".
2137 ctxt->currentTemplateRule = templ;
2139 #ifdef WITH_XSLT_DEBUG_PROCESS
2140 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2141 "xsltProcessOneNode: applying template '%s' for attribute %s\n",
2142 templ->match, contextNode->name));
2143 #endif
2144 xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams);
2146 ctxt->currentTemplateRule = oldCurTempRule;
2147 } else {
2148 xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule;
2150 * Set the "current template rule".
2152 ctxt->currentTemplateRule = templ;
2154 #ifdef WITH_XSLT_DEBUG_PROCESS
2155 if (contextNode->type == XML_DOCUMENT_NODE) {
2156 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2157 "xsltProcessOneNode: applying template '%s' for /\n",
2158 templ->match));
2159 } else {
2160 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2161 "xsltProcessOneNode: applying template '%s' for %s\n",
2162 templ->match, contextNode->name));
2164 #endif
2165 xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams);
2167 ctxt->currentTemplateRule = oldCurTempRule;
2171 #ifdef WITH_DEBUGGER
2172 static xmlNodePtr
2173 xsltDebuggerStartSequenceConstructor(xsltTransformContextPtr ctxt,
2174 xmlNodePtr contextNode,
2175 xmlNodePtr list,
2176 xsltTemplatePtr templ,
2177 int *addCallResult)
2179 xmlNodePtr debugedNode = NULL;
2181 if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
2182 if (templ) {
2183 *addCallResult = xslAddCall(templ, templ->elem);
2184 } else {
2185 *addCallResult = xslAddCall(NULL, list);
2187 switch (ctxt->debugStatus) {
2188 case XSLT_DEBUG_RUN_RESTART:
2189 case XSLT_DEBUG_QUIT:
2190 if (*addCallResult)
2191 xslDropCall();
2192 return(NULL);
2194 if (templ) {
2195 xslHandleDebugger(templ->elem, contextNode, templ, ctxt);
2196 debugedNode = templ->elem;
2197 } else if (list) {
2198 xslHandleDebugger(list, contextNode, templ, ctxt);
2199 debugedNode = list;
2200 } else if (ctxt->inst) {
2201 xslHandleDebugger(ctxt->inst, contextNode, templ, ctxt);
2202 debugedNode = ctxt->inst;
2205 return(debugedNode);
2207 #endif /* WITH_DEBUGGER */
2210 * xsltLocalVariablePush:
2211 * @ctxt: the transformation context
2212 * @variable: variable to be pushed to the variable stack
2213 * @level: new value for variable's level
2215 * Places the variable onto the local variable stack
2217 * Returns: 0 for success, -1 for any error
2218 * **NOTE:**
2219 * This is an internal routine and should not be called by users!
2222 xsltLocalVariablePush(xsltTransformContextPtr ctxt,
2223 xsltStackElemPtr variable,
2224 int level)
2226 if (ctxt->varsNr >= ctxt->varsMax) {
2227 xsltStackElemPtr *tmp;
2228 int newMax = ctxt->varsMax == 0 ? 10 : 2 * ctxt->varsMax;
2230 tmp = (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab,
2231 newMax * sizeof(*tmp));
2232 if (tmp == NULL) {
2233 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
2234 return (-1);
2236 ctxt->varsTab = tmp;
2237 ctxt->varsMax = newMax;
2239 ctxt->varsTab[ctxt->varsNr++] = variable;
2240 ctxt->vars = variable;
2241 variable->level = level;
2242 return(0);
2246 * xsltReleaseLocalRVTs:
2248 * Fragments which are results of extension instructions
2249 * are preserved; all other fragments are freed/cached.
2251 static void
2252 xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt, xmlDocPtr base)
2254 xmlDocPtr cur = ctxt->localRVT, tmp;
2256 if (cur == base)
2257 return;
2258 if (cur->prev != NULL)
2259 xsltTransformError(ctxt, NULL, NULL, "localRVT not head of list\n");
2261 /* Reset localRVT early because some RVTs might be registered again. */
2262 ctxt->localRVT = base;
2263 if (base != NULL)
2264 base->prev = NULL;
2266 do {
2267 tmp = cur;
2268 cur = (xmlDocPtr) cur->next;
2269 if (tmp->compression == XSLT_RVT_LOCAL) {
2270 xsltReleaseRVT(ctxt, tmp);
2271 } else if (tmp->compression == XSLT_RVT_GLOBAL) {
2272 xsltRegisterPersistRVT(ctxt, tmp);
2273 } else if (tmp->compression == XSLT_RVT_FUNC_RESULT) {
2275 * This will either register the RVT again or move it to the
2276 * context variable.
2278 xsltRegisterLocalRVT(ctxt, tmp);
2279 tmp->compression = XSLT_RVT_FUNC_RESULT;
2280 } else {
2281 xmlGenericError(xmlGenericErrorContext,
2282 "xsltReleaseLocalRVTs: Unexpected RVT flag %p\n",
2283 tmp->psvi);
2285 } while (cur != base);
2289 * xsltApplySequenceConstructor:
2290 * @ctxt: a XSLT process context
2291 * @contextNode: the "current node" in the source tree
2292 * @list: the nodes of a sequence constructor;
2293 * (plus leading xsl:param elements)
2294 * @templ: the compiled xsl:template (optional)
2296 * Processes a sequence constructor.
2298 * NOTE: ctxt->currentTemplateRule was introduced to reflect the
2299 * semantics of "current template rule". I.e. the field ctxt->templ
2300 * is not intended to reflect this, thus always pushed onto the
2301 * template stack.
2303 static void
2304 xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
2305 xmlNodePtr contextNode, xmlNodePtr list,
2306 xsltTemplatePtr templ)
2308 xmlNodePtr oldInsert, oldInst, oldCurInst, oldContextNode;
2309 xmlNodePtr cur, insert, copy = NULL;
2310 int level = 0, oldVarsNr;
2311 xmlDocPtr oldLocalFragmentTop;
2313 #ifdef XSLT_REFACTORED
2314 xsltStylePreCompPtr info;
2315 #endif
2317 #ifdef WITH_DEBUGGER
2318 int addCallResult = 0;
2319 xmlNodePtr debuggedNode = NULL;
2320 #endif
2322 if (ctxt == NULL)
2323 return;
2325 #ifdef WITH_DEBUGGER
2326 if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
2327 debuggedNode =
2328 xsltDebuggerStartSequenceConstructor(ctxt, contextNode,
2329 list, templ, &addCallResult);
2330 if (debuggedNode == NULL)
2331 return;
2333 #endif
2335 if (list == NULL)
2336 return;
2337 CHECK_STOPPED;
2340 * Check for infinite recursion: stop if the maximum of nested templates
2341 * is excceeded. Adjust xsltMaxDepth if you need more.
2343 if (ctxt->depth >= ctxt->maxTemplateDepth) {
2344 xsltTransformError(ctxt, NULL, list,
2345 "xsltApplySequenceConstructor: A potential infinite template "
2346 "recursion was detected.\n"
2347 "You can adjust xsltMaxDepth (--maxdepth) in order to "
2348 "raise the maximum number of nested template calls and "
2349 "variables/params (currently set to %d).\n",
2350 ctxt->maxTemplateDepth);
2351 xsltDebug(ctxt, contextNode, list, NULL);
2352 ctxt->state = XSLT_STATE_STOPPED;
2353 return;
2355 ctxt->depth++;
2357 oldLocalFragmentTop = ctxt->localRVT;
2358 oldInsert = insert = ctxt->insert;
2359 oldInst = oldCurInst = ctxt->inst;
2360 oldContextNode = ctxt->node;
2362 * Save current number of variables on the stack; new vars are popped when
2363 * exiting.
2365 oldVarsNr = ctxt->varsNr;
2367 * Process the sequence constructor.
2369 cur = list;
2370 while (cur != NULL) {
2371 if (ctxt->opLimit != 0) {
2372 if (ctxt->opCount >= ctxt->opLimit) {
2373 xsltTransformError(ctxt, NULL, cur,
2374 "xsltApplySequenceConstructor: "
2375 "Operation limit exceeded\n");
2376 ctxt->state = XSLT_STATE_STOPPED;
2377 goto error;
2379 ctxt->opCount += 1;
2382 ctxt->inst = cur;
2384 #ifdef WITH_DEBUGGER
2385 switch (ctxt->debugStatus) {
2386 case XSLT_DEBUG_RUN_RESTART:
2387 case XSLT_DEBUG_QUIT:
2388 break;
2391 #endif
2393 * Test; we must have a valid insertion point.
2395 if (insert == NULL) {
2397 #ifdef WITH_XSLT_DEBUG_PROCESS
2398 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2399 "xsltApplySequenceConstructor: insert == NULL !\n"));
2400 #endif
2401 goto error;
2404 #ifdef WITH_DEBUGGER
2405 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (debuggedNode != cur))
2406 xslHandleDebugger(cur, contextNode, templ, ctxt);
2407 #endif
2409 #ifdef XSLT_REFACTORED
2410 if (cur->type == XML_ELEMENT_NODE) {
2411 info = (xsltStylePreCompPtr) cur->psvi;
2413 * We expect a compiled representation on:
2414 * 1) XSLT instructions of this XSLT version (1.0)
2415 * (with a few exceptions)
2416 * 2) Literal result elements
2417 * 3) Extension instructions
2418 * 4) XSLT instructions of future XSLT versions
2419 * (forwards-compatible mode).
2421 if (info == NULL) {
2423 * Handle the rare cases where we don't expect a compiled
2424 * representation on an XSLT element.
2426 if (IS_XSLT_ELEM_FAST(cur) && IS_XSLT_NAME(cur, "message")) {
2427 xsltMessage(ctxt, contextNode, cur);
2428 goto skip_children;
2431 * Something really went wrong:
2433 xsltTransformError(ctxt, NULL, cur,
2434 "Internal error in xsltApplySequenceConstructor(): "
2435 "The element '%s' in the stylesheet has no compiled "
2436 "representation.\n",
2437 cur->name);
2438 goto skip_children;
2441 if (info->type == XSLT_FUNC_LITERAL_RESULT_ELEMENT) {
2442 xsltStyleItemLRElementInfoPtr lrInfo =
2443 (xsltStyleItemLRElementInfoPtr) info;
2445 * Literal result elements
2446 * --------------------------------------------------------
2448 #ifdef WITH_XSLT_DEBUG_PROCESS
2449 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
2450 xsltGenericDebug(xsltGenericDebugContext,
2451 "xsltApplySequenceConstructor: copy literal result "
2452 "element '%s'\n", cur->name));
2453 #endif
2455 * Copy the raw element-node.
2456 * OLD: if ((copy = xsltShallowCopyElem(ctxt, cur, insert))
2457 * == NULL)
2458 * goto error;
2460 copy = xmlDocCopyNode(cur, insert->doc, 0);
2461 if (copy == NULL) {
2462 xsltTransformError(ctxt, NULL, cur,
2463 "Internal error in xsltApplySequenceConstructor(): "
2464 "Failed to copy literal result element '%s'.\n",
2465 cur->name);
2466 goto error;
2467 } else {
2469 * Add the element-node to the result tree.
2471 copy->doc = ctxt->output;
2472 copy = xsltAddChild(insert, copy);
2474 * Create effective namespaces declarations.
2475 * OLD: xsltCopyNamespaceList(ctxt, copy, cur->nsDef);
2477 if (lrInfo->effectiveNs != NULL) {
2478 xsltEffectiveNsPtr effNs = lrInfo->effectiveNs;
2479 xmlNsPtr ns, lastns = NULL;
2481 while (effNs != NULL) {
2483 * Avoid generating redundant namespace
2484 * declarations; thus lookup if there is already
2485 * such a ns-decl in the result.
2487 ns = xmlSearchNs(copy->doc, copy, effNs->prefix);
2488 if ((ns != NULL) &&
2489 (xmlStrEqual(ns->href, effNs->nsName)))
2491 effNs = effNs->next;
2492 continue;
2494 ns = xmlNewNs(copy, effNs->nsName, effNs->prefix);
2495 if (ns == NULL) {
2496 xsltTransformError(ctxt, NULL, cur,
2497 "Internal error in "
2498 "xsltApplySequenceConstructor(): "
2499 "Failed to copy a namespace "
2500 "declaration.\n");
2501 goto error;
2504 if (lastns == NULL)
2505 copy->nsDef = ns;
2506 else
2507 lastns->next =ns;
2508 lastns = ns;
2510 effNs = effNs->next;
2515 * NOTE that we don't need to apply ns-alising: this was
2516 * already done at compile-time.
2518 if (cur->ns != NULL) {
2520 * If there's no such ns-decl in the result tree,
2521 * then xsltGetSpecialNamespace() will
2522 * create a ns-decl on the copied node.
2524 copy->ns = xsltGetSpecialNamespace(ctxt, cur,
2525 cur->ns->href, cur->ns->prefix, copy);
2526 } else {
2528 * Undeclare the default namespace if needed.
2529 * This can be skipped, if the result element has
2530 * no ns-decls, in which case the result element
2531 * obviously does not declare a default namespace;
2532 * AND there's either no parent, or the parent
2533 * element is in no namespace; this means there's no
2534 * default namespace is scope to care about.
2536 * REVISIT: This might result in massive
2537 * generation of ns-decls if nodes in a default
2538 * namespaces are mixed with nodes in no namespace.
2541 if (copy->nsDef ||
2542 ((insert != NULL) &&
2543 (insert->type == XML_ELEMENT_NODE) &&
2544 (insert->ns != NULL)))
2546 xsltGetSpecialNamespace(ctxt, cur,
2547 NULL, NULL, copy);
2552 * SPEC XSLT 2.0 "Each attribute of the literal result
2553 * element, other than an attribute in the XSLT namespace,
2554 * is processed to produce an attribute for the element in
2555 * the result tree."
2556 * NOTE: See bug #341325.
2558 if (cur->properties != NULL) {
2559 xsltAttrListTemplateProcess(ctxt, copy, cur->properties);
2561 } else if (IS_XSLT_ELEM_FAST(cur)) {
2563 * XSLT instructions
2564 * --------------------------------------------------------
2566 if (info->type == XSLT_FUNC_UNKOWN_FORWARDS_COMPAT) {
2568 * We hit an unknown XSLT element.
2569 * Try to apply one of the fallback cases.
2571 ctxt->insert = insert;
2572 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
2573 xsltTransformError(ctxt, NULL, cur,
2574 "The is no fallback behaviour defined for "
2575 "the unknown XSLT element '%s'.\n",
2576 cur->name);
2578 ctxt->insert = oldInsert;
2579 } else if (info->func != NULL) {
2581 * Execute the XSLT instruction.
2583 ctxt->insert = insert;
2585 info->func(ctxt, contextNode, cur,
2586 (xsltElemPreCompPtr) info);
2589 * Cleanup temporary tree fragments.
2591 if (oldLocalFragmentTop != ctxt->localRVT)
2592 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
2594 ctxt->insert = oldInsert;
2595 } else if (info->type == XSLT_FUNC_VARIABLE) {
2596 xsltStackElemPtr tmpvar = ctxt->vars;
2598 xsltParseStylesheetVariable(ctxt, cur);
2600 if (tmpvar != ctxt->vars) {
2602 * TODO: Using a @tmpvar is an annoying workaround, but
2603 * the current mechanisms do not provide any other way
2604 * of knowing if the var was really pushed onto the
2605 * stack.
2607 ctxt->vars->level = level;
2609 } else if (info->type == XSLT_FUNC_MESSAGE) {
2611 * TODO: Won't be hit, since we don't compile xsl:message.
2613 xsltMessage(ctxt, contextNode, cur);
2614 } else {
2615 xsltTransformError(ctxt, NULL, cur,
2616 "Unexpected XSLT element '%s'.\n", cur->name);
2618 goto skip_children;
2620 } else {
2621 xsltTransformFunction func;
2623 * Extension intructions (elements)
2624 * --------------------------------------------------------
2626 if (cur->psvi == xsltExtMarker) {
2628 * The xsltExtMarker was set during the compilation
2629 * of extension instructions if there was no registered
2630 * handler for this specific extension function at
2631 * compile-time.
2632 * Libxslt will now lookup if a handler is
2633 * registered in the context of this transformation.
2635 func = xsltExtElementLookup(ctxt, cur->name,
2636 cur->ns->href);
2637 } else
2638 func = ((xsltElemPreCompPtr) cur->psvi)->func;
2640 if (func == NULL) {
2642 * No handler available.
2643 * Try to execute fallback behaviour via xsl:fallback.
2645 #ifdef WITH_XSLT_DEBUG_PROCESS
2646 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
2647 xsltGenericDebug(xsltGenericDebugContext,
2648 "xsltApplySequenceConstructor: unknown extension %s\n",
2649 cur->name));
2650 #endif
2651 ctxt->insert = insert;
2652 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
2653 xsltTransformError(ctxt, NULL, cur,
2654 "Unknown extension instruction '{%s}%s'.\n",
2655 cur->ns->href, cur->name);
2657 ctxt->insert = oldInsert;
2658 } else {
2660 * Execute the handler-callback.
2662 #ifdef WITH_XSLT_DEBUG_PROCESS
2663 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2664 "xsltApplySequenceConstructor: extension construct %s\n",
2665 cur->name));
2666 #endif
2668 * Disable the xsltCopyTextString optimization for
2669 * extension elements. Extensions could append text using
2670 * xmlAddChild which will free the buffer pointed to by
2671 * 'lasttext'. This buffer could later be reallocated with
2672 * a different size than recorded in 'lasttsize'. See bug
2673 * #777432.
2675 if (cur->psvi == xsltExtMarker) {
2676 ctxt->lasttext = NULL;
2679 ctxt->insert = insert;
2681 func(ctxt, contextNode, cur, cur->psvi);
2684 * Cleanup temporary tree fragments.
2686 if (oldLocalFragmentTop != ctxt->localRVT)
2687 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
2689 ctxt->insert = oldInsert;
2691 goto skip_children;
2694 } else if (XSLT_IS_TEXT_NODE(cur)) {
2696 * Text
2697 * ------------------------------------------------------------
2699 #ifdef WITH_XSLT_DEBUG_PROCESS
2700 if (cur->name == xmlStringTextNoenc) {
2701 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
2702 xsltGenericDebug(xsltGenericDebugContext,
2703 "xsltApplySequenceConstructor: copy unescaped text '%s'\n",
2704 cur->content));
2705 } else {
2706 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
2707 xsltGenericDebug(xsltGenericDebugContext,
2708 "xsltApplySequenceConstructor: copy text '%s'\n",
2709 cur->content));
2711 #endif
2712 if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL)
2713 goto error;
2716 #else /* XSLT_REFACTORED */
2718 if (IS_XSLT_ELEM(cur)) {
2720 * This is an XSLT node
2722 xsltStylePreCompPtr info = (xsltStylePreCompPtr) cur->psvi;
2724 if (info == NULL) {
2725 if (IS_XSLT_NAME(cur, "message")) {
2726 xsltMessage(ctxt, contextNode, cur);
2727 } else {
2729 * That's an error try to apply one of the fallback cases
2731 ctxt->insert = insert;
2732 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
2733 xsltGenericError(xsltGenericErrorContext,
2734 "xsltApplySequenceConstructor: %s was not compiled\n",
2735 cur->name);
2737 ctxt->insert = oldInsert;
2739 goto skip_children;
2742 if (info->func != NULL) {
2743 oldCurInst = ctxt->inst;
2744 ctxt->inst = cur;
2745 ctxt->insert = insert;
2747 info->func(ctxt, contextNode, cur, (xsltElemPreCompPtr) info);
2750 * Cleanup temporary tree fragments.
2752 if (oldLocalFragmentTop != ctxt->localRVT)
2753 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
2755 ctxt->insert = oldInsert;
2756 ctxt->inst = oldCurInst;
2757 goto skip_children;
2760 if (IS_XSLT_NAME(cur, "variable")) {
2761 xsltStackElemPtr tmpvar = ctxt->vars;
2763 oldCurInst = ctxt->inst;
2764 ctxt->inst = cur;
2766 xsltParseStylesheetVariable(ctxt, cur);
2768 ctxt->inst = oldCurInst;
2770 if (tmpvar != ctxt->vars) {
2772 * TODO: Using a @tmpvar is an annoying workaround, but
2773 * the current mechanisms do not provide any other way
2774 * of knowing if the var was really pushed onto the
2775 * stack.
2777 ctxt->vars->level = level;
2779 } else if (IS_XSLT_NAME(cur, "message")) {
2780 xsltMessage(ctxt, contextNode, cur);
2781 } else {
2782 xsltTransformError(ctxt, NULL, cur,
2783 "Unexpected XSLT element '%s'.\n", cur->name);
2785 goto skip_children;
2786 } else if ((cur->type == XML_TEXT_NODE) ||
2787 (cur->type == XML_CDATA_SECTION_NODE)) {
2790 * This text comes from the stylesheet
2791 * For stylesheets, the set of whitespace-preserving
2792 * element names consists of just xsl:text.
2794 #ifdef WITH_XSLT_DEBUG_PROCESS
2795 if (cur->type == XML_CDATA_SECTION_NODE) {
2796 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2797 "xsltApplySequenceConstructor: copy CDATA text %s\n",
2798 cur->content));
2799 } else if (cur->name == xmlStringTextNoenc) {
2800 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2801 "xsltApplySequenceConstructor: copy unescaped text %s\n",
2802 cur->content));
2803 } else {
2804 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2805 "xsltApplySequenceConstructor: copy text %s\n",
2806 cur->content));
2808 #endif
2809 if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL)
2810 goto error;
2811 } else if ((cur->type == XML_ELEMENT_NODE) &&
2812 (cur->ns != NULL) && (cur->psvi != NULL)) {
2813 xsltTransformFunction function;
2815 oldCurInst = ctxt->inst;
2816 ctxt->inst = cur;
2818 * Flagged as an extension element
2820 if (cur->psvi == xsltExtMarker)
2821 function = xsltExtElementLookup(ctxt, cur->name,
2822 cur->ns->href);
2823 else
2824 function = ((xsltElemPreCompPtr) cur->psvi)->func;
2826 if (function == NULL) {
2827 xmlNodePtr child;
2828 int found = 0;
2830 #ifdef WITH_XSLT_DEBUG_PROCESS
2831 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2832 "xsltApplySequenceConstructor: unknown extension %s\n",
2833 cur->name));
2834 #endif
2836 * Search if there are fallbacks
2838 ctxt->insert = insert;
2839 child = cur->children;
2840 while (child != NULL) {
2841 if ((IS_XSLT_ELEM(child)) &&
2842 (IS_XSLT_NAME(child, "fallback")))
2844 found = 1;
2845 xsltApplySequenceConstructor(ctxt, contextNode,
2846 child->children, NULL);
2848 child = child->next;
2850 ctxt->insert = oldInsert;
2852 if (!found) {
2853 xsltTransformError(ctxt, NULL, cur,
2854 "xsltApplySequenceConstructor: failed to find extension %s\n",
2855 cur->name);
2857 } else {
2858 #ifdef WITH_XSLT_DEBUG_PROCESS
2859 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2860 "xsltApplySequenceConstructor: extension construct %s\n",
2861 cur->name));
2862 #endif
2865 * Disable the xsltCopyTextString optimization for
2866 * extension elements. Extensions could append text using
2867 * xmlAddChild which will free the buffer pointed to by
2868 * 'lasttext'. This buffer could later be reallocated with
2869 * a different size than recorded in 'lasttsize'. See bug
2870 * #777432.
2872 if (cur->psvi == xsltExtMarker) {
2873 ctxt->lasttext = NULL;
2876 ctxt->insert = insert;
2878 function(ctxt, contextNode, cur, cur->psvi);
2880 * Cleanup temporary tree fragments.
2882 if (oldLocalFragmentTop != ctxt->localRVT)
2883 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
2885 ctxt->insert = oldInsert;
2888 ctxt->inst = oldCurInst;
2889 goto skip_children;
2890 } else if (cur->type == XML_ELEMENT_NODE) {
2891 #ifdef WITH_XSLT_DEBUG_PROCESS
2892 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2893 "xsltApplySequenceConstructor: copy node %s\n",
2894 cur->name));
2895 #endif
2896 oldCurInst = ctxt->inst;
2897 ctxt->inst = cur;
2899 if ((copy = xsltShallowCopyElem(ctxt, cur, insert, 1)) == NULL)
2900 goto error;
2902 * Add extra namespaces inherited from the current template
2903 * if we are in the first level children and this is a
2904 * "real" template.
2906 if ((templ != NULL) && (oldInsert == insert) &&
2907 (ctxt->templ != NULL) && (ctxt->templ->inheritedNs != NULL)) {
2908 int i;
2909 xmlNsPtr ns, ret;
2911 for (i = 0; i < ctxt->templ->inheritedNsNr; i++) {
2912 const xmlChar *URI = NULL;
2913 xsltStylesheetPtr style;
2914 ns = ctxt->templ->inheritedNs[i];
2916 /* Note that the XSLT namespace was already excluded
2917 * in xsltGetInheritedNsList().
2919 #if 0
2920 if (xmlStrEqual(ns->href, XSLT_NAMESPACE))
2921 continue;
2922 #endif
2923 style = ctxt->style;
2924 while (style != NULL) {
2925 if (style->nsAliases != NULL)
2926 URI = (const xmlChar *)
2927 xmlHashLookup(style->nsAliases, ns->href);
2928 if (URI != NULL)
2929 break;
2931 style = xsltNextImport(style);
2933 if (URI == UNDEFINED_DEFAULT_NS)
2934 continue;
2935 if (URI == NULL)
2936 URI = ns->href;
2938 * TODO: The following will still be buggy for the
2939 * non-refactored code.
2941 ret = xmlSearchNs(copy->doc, copy, ns->prefix);
2942 if ((ret == NULL) || (!xmlStrEqual(ret->href, URI)))
2944 xmlNewNs(copy, URI, ns->prefix);
2947 if (copy->ns != NULL) {
2949 * Fix the node namespace if needed
2951 copy->ns = xsltGetNamespace(ctxt, cur, copy->ns, copy);
2955 * all the attributes are directly inherited
2957 if (cur->properties != NULL) {
2958 xsltAttrListTemplateProcess(ctxt, copy, cur->properties);
2960 ctxt->inst = oldCurInst;
2962 #endif /* else of XSLT_REFACTORED */
2965 * Descend into content in document order.
2967 if (cur->children != NULL) {
2968 if (cur->children->type != XML_ENTITY_DECL) {
2969 cur = cur->children;
2970 level++;
2971 if (copy != NULL)
2972 insert = copy;
2973 continue;
2977 skip_children:
2979 * If xslt:message was just processed, we might have hit a
2980 * terminate='yes'; if so, then break the loop and clean up.
2981 * TODO: Do we need to check this also before trying to descend
2982 * into the content?
2984 if (ctxt->state == XSLT_STATE_STOPPED)
2985 break;
2986 if (cur->next != NULL) {
2987 cur = cur->next;
2988 continue;
2991 do {
2992 cur = cur->parent;
2993 level--;
2995 * Pop variables/params (xsl:variable and xsl:param).
2997 if ((ctxt->varsNr > oldVarsNr) && (ctxt->vars->level > level)) {
2998 xsltLocalVariablePop(ctxt, oldVarsNr, level);
3001 insert = insert->parent;
3002 if (cur == NULL)
3003 break;
3004 if (cur == list->parent) {
3005 cur = NULL;
3006 break;
3008 if (cur->next != NULL) {
3009 cur = cur->next;
3010 break;
3012 } while (cur != NULL);
3015 error:
3017 * In case of errors: pop remaining variables.
3019 if (ctxt->varsNr > oldVarsNr)
3020 xsltLocalVariablePop(ctxt, oldVarsNr, -1);
3022 ctxt->node = oldContextNode;
3023 ctxt->inst = oldInst;
3024 ctxt->insert = oldInsert;
3026 ctxt->depth--;
3028 #ifdef WITH_DEBUGGER
3029 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) {
3030 xslDropCall();
3032 #endif
3036 * xsltApplyXSLTTemplate:
3037 * @ctxt: a XSLT transformation context
3038 * @contextNode: the node in the source tree.
3039 * @list: the nodes of a sequence constructor;
3040 * (plus leading xsl:param elements)
3041 * @templ: the compiled xsl:template declaration;
3042 * NULL if a sequence constructor
3043 * @withParams: a set of caller-parameters (xsl:with-param) or NULL
3045 * Called by:
3046 * - xsltApplyImports()
3047 * - xsltCallTemplate()
3048 * - xsltDefaultProcessOneNode()
3049 * - xsltProcessOneNode()
3051 static void
3052 xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt,
3053 xmlNodePtr contextNode,
3054 xmlNodePtr list,
3055 xsltTemplatePtr templ,
3056 xsltStackElemPtr withParams)
3058 int oldVarsBase = 0;
3059 xmlNodePtr cur;
3060 xsltStackElemPtr tmpParam = NULL;
3061 xmlDocPtr oldUserFragmentTop;
3062 #ifdef WITH_PROFILER
3063 long start = 0;
3064 #endif
3066 #ifdef XSLT_REFACTORED
3067 xsltStyleItemParamPtr iparam;
3068 #else
3069 xsltStylePreCompPtr iparam;
3070 #endif
3072 #ifdef WITH_DEBUGGER
3073 int addCallResult = 0;
3074 #endif
3076 if (ctxt == NULL)
3077 return;
3078 if (templ == NULL) {
3079 xsltTransformError(ctxt, NULL, list,
3080 "xsltApplyXSLTTemplate: Bad arguments; @templ is mandatory.\n");
3081 return;
3084 #ifdef WITH_DEBUGGER
3085 if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
3086 if (xsltDebuggerStartSequenceConstructor(ctxt, contextNode,
3087 list, templ, &addCallResult) == NULL)
3088 return;
3090 #endif
3092 if (list == NULL)
3093 return;
3094 CHECK_STOPPED;
3096 if (ctxt->varsNr >= ctxt->maxTemplateVars)
3098 xsltTransformError(ctxt, NULL, list,
3099 "xsltApplyXSLTTemplate: A potential infinite template recursion "
3100 "was detected.\n"
3101 "You can adjust maxTemplateVars (--maxvars) in order to "
3102 "raise the maximum number of variables/params (currently set to %d).\n",
3103 ctxt->maxTemplateVars);
3104 xsltDebug(ctxt, contextNode, list, NULL);
3105 ctxt->state = XSLT_STATE_STOPPED;
3106 return;
3109 oldUserFragmentTop = ctxt->tmpRVT;
3110 ctxt->tmpRVT = NULL;
3113 * Initiate a distinct scope of local params/variables.
3115 oldVarsBase = ctxt->varsBase;
3116 ctxt->varsBase = ctxt->varsNr;
3118 ctxt->node = contextNode;
3120 #ifdef WITH_PROFILER
3121 if (ctxt->profile) {
3122 templ->nbCalls++;
3123 start = xsltTimestamp();
3124 profPush(ctxt, 0);
3125 profCallgraphAdd(templ, ctxt->templ);
3127 #endif
3130 * Push the xsl:template declaration onto the stack.
3132 templPush(ctxt, templ);
3134 #ifdef WITH_XSLT_DEBUG_PROCESS
3135 if (templ->name != NULL)
3136 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
3137 "applying xsl:template '%s'\n", templ->name));
3138 #endif
3140 * Process xsl:param instructions and skip those elements for
3141 * further processing.
3143 cur = list;
3144 do {
3145 if (cur->type == XML_TEXT_NODE) {
3146 cur = cur->next;
3147 continue;
3149 if ((cur->type != XML_ELEMENT_NODE) ||
3150 (cur->name[0] != 'p') ||
3151 (cur->psvi == NULL) ||
3152 (! xmlStrEqual(cur->name, BAD_CAST "param")) ||
3153 (! IS_XSLT_ELEM(cur)))
3155 break;
3158 list = cur->next;
3160 #ifdef XSLT_REFACTORED
3161 iparam = (xsltStyleItemParamPtr) cur->psvi;
3162 #else
3163 iparam = (xsltStylePreCompPtr) cur->psvi;
3164 #endif
3167 * Substitute xsl:param for a given xsl:with-param.
3168 * Since the XPath expression will reference the params/vars
3169 * by index, we need to slot the xsl:with-params in the
3170 * order of encountered xsl:params to keep the sequence of
3171 * params/variables in the stack exactly as it was at
3172 * compile time,
3174 tmpParam = NULL;
3175 if (withParams) {
3176 tmpParam = withParams;
3177 do {
3178 if ((tmpParam->name == (iparam->name)) &&
3179 (tmpParam->nameURI == (iparam->ns)))
3182 * Push the caller-parameter.
3184 xsltLocalVariablePush(ctxt, tmpParam, -1);
3185 break;
3187 tmpParam = tmpParam->next;
3188 } while (tmpParam != NULL);
3191 * Push the xsl:param.
3193 if (tmpParam == NULL) {
3195 * Note that we must assume that the added parameter
3196 * has a @depth of 0.
3198 xsltParseStylesheetParam(ctxt, cur);
3200 cur = cur->next;
3201 } while (cur != NULL);
3203 * Process the sequence constructor.
3205 xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
3208 * Remove remaining xsl:param and xsl:with-param items from
3209 * the stack. Don't free xsl:with-param items.
3211 if (ctxt->varsNr > ctxt->varsBase)
3212 xsltTemplateParamsCleanup(ctxt);
3213 ctxt->varsBase = oldVarsBase;
3216 * Release user-created fragments stored in the scope
3217 * of xsl:template. Note that this mechanism is deprecated:
3218 * user code should now use xsltRegisterLocalRVT() instead
3219 * of the obsolete xsltRegisterTmpRVT().
3221 if (ctxt->tmpRVT) {
3222 xmlDocPtr curdoc = ctxt->tmpRVT, tmp;
3224 while (curdoc != NULL) {
3225 tmp = curdoc;
3226 curdoc = (xmlDocPtr) curdoc->next;
3227 xsltReleaseRVT(ctxt, tmp);
3230 ctxt->tmpRVT = oldUserFragmentTop;
3233 * Pop the xsl:template declaration from the stack.
3235 templPop(ctxt);
3237 #ifdef WITH_PROFILER
3238 if (ctxt->profile) {
3239 long spent, child, total, end;
3241 end = xsltTimestamp();
3242 child = profPop(ctxt);
3243 total = end - start;
3244 spent = total - child;
3245 if (spent <= 0) {
3247 * Not possible unless the original calibration failed
3248 * we can try to correct it on the fly.
3250 xsltCalibrateAdjust(spent);
3251 spent = 0;
3254 templ->time += spent;
3255 if (ctxt->profNr > 0)
3256 ctxt->profTab[ctxt->profNr - 1] += total;
3258 #endif
3260 #ifdef WITH_DEBUGGER
3261 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) {
3262 xslDropCall();
3264 #endif
3269 * xsltApplyOneTemplate:
3270 * @ctxt: a XSLT process context
3271 * @contextNode: the node in the source tree.
3272 * @list: the nodes of a sequence constructor
3273 * @templ: not used
3274 * @params: a set of parameters (xsl:param) or NULL
3276 * Processes a sequence constructor on the current node in the source tree.
3278 * @params are the already computed variable stack items; this function
3279 * pushes them on the variable stack, and pops them before exiting; it's
3280 * left to the caller to free or reuse @params afterwards. The initial
3281 * states of the variable stack will always be restored before this
3282 * function exits.
3283 * NOTE that this does *not* initiate a new distinct variable scope; i.e.
3284 * variables already on the stack are visible to the process. The caller's
3285 * side needs to start a new variable scope if needed (e.g. in exsl:function).
3287 * @templ is obsolete and not used anymore (e.g. <exslt:function> does not
3288 * provide a @templ); a non-NULL @templ might raise an error in the future.
3290 * BIG NOTE: This function is not intended to process the content of an
3291 * xsl:template; it does not expect xsl:param instructions in @list and
3292 * will report errors if found.
3294 * Called by:
3295 * - xsltEvalVariable() (variables.c)
3296 * - exsltFuncFunctionFunction() (libexsl/functions.c)
3298 void
3299 xsltApplyOneTemplate(xsltTransformContextPtr ctxt,
3300 xmlNodePtr contextNode,
3301 xmlNodePtr list,
3302 xsltTemplatePtr templ ATTRIBUTE_UNUSED,
3303 xsltStackElemPtr params)
3305 if ((ctxt == NULL) || (list == NULL))
3306 return;
3307 CHECK_STOPPED;
3309 if (params) {
3311 * This code should be obsolete - was previously used
3312 * by libexslt/functions.c, but due to bug 381319 the
3313 * logic there was changed.
3315 int oldVarsNr = ctxt->varsNr;
3318 * Push the given xsl:param(s) onto the variable stack.
3320 while (params != NULL) {
3321 xsltLocalVariablePush(ctxt, params, -1);
3322 params = params->next;
3324 xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
3326 * Pop the given xsl:param(s) from the stack but don't free them.
3328 xsltLocalVariablePop(ctxt, oldVarsNr, -2);
3329 } else
3330 xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
3333 /************************************************************************
3335 * XSLT-1.1 extensions *
3337 ************************************************************************/
3340 * xsltDocumentElem:
3341 * @ctxt: an XSLT processing context
3342 * @node: The current node
3343 * @inst: the instruction in the stylesheet
3344 * @castedComp: precomputed information
3346 * Process an EXSLT/XSLT-1.1 document element
3348 void
3349 xsltDocumentElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
3350 xmlNodePtr inst, xsltElemPreCompPtr castedComp)
3352 #ifdef XSLT_REFACTORED
3353 xsltStyleItemDocumentPtr comp = (xsltStyleItemDocumentPtr) castedComp;
3354 #else
3355 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
3356 #endif
3357 xsltStylesheetPtr style = NULL;
3358 int ret;
3359 xmlChar *filename = NULL, *prop, *elements;
3360 xmlChar *element, *end;
3361 xmlDocPtr res = NULL;
3362 xmlDocPtr oldOutput;
3363 xmlNodePtr oldInsert, root;
3364 const char *oldOutputFile;
3365 xsltOutputType oldType;
3366 xmlChar *URL = NULL;
3367 const xmlChar *method;
3368 const xmlChar *doctypePublic;
3369 const xmlChar *doctypeSystem;
3370 const xmlChar *version;
3371 const xmlChar *encoding;
3372 int redirect_write_append = 0;
3374 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
3375 return;
3377 if (comp->filename == NULL) {
3379 if (xmlStrEqual(inst->name, (const xmlChar *) "output")) {
3381 * The element "output" is in the namespace XSLT_SAXON_NAMESPACE
3382 * (http://icl.com/saxon)
3383 * The @file is in no namespace.
3385 #ifdef WITH_XSLT_DEBUG_EXTRA
3386 xsltGenericDebug(xsltGenericDebugContext,
3387 "Found saxon:output extension\n");
3388 #endif
3389 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3390 (const xmlChar *) "file",
3391 XSLT_SAXON_NAMESPACE);
3393 if (URL == NULL)
3394 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3395 (const xmlChar *) "href",
3396 XSLT_SAXON_NAMESPACE);
3397 } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) {
3398 #ifdef WITH_XSLT_DEBUG_EXTRA
3399 xsltGenericDebug(xsltGenericDebugContext,
3400 "Found xalan:write extension\n");
3401 #endif
3402 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3403 (const xmlChar *)
3404 "select",
3405 XSLT_XALAN_NAMESPACE);
3406 if (URL != NULL) {
3407 xmlXPathCompExprPtr cmp;
3408 xmlChar *val;
3411 * Trying to handle bug #59212
3412 * The value of the "select" attribute is an
3413 * XPath expression.
3414 * (see http://xml.apache.org/xalan-j/extensionslib.html#redirect)
3416 cmp = xmlXPathCtxtCompile(ctxt->xpathCtxt, URL);
3417 val = xsltEvalXPathString(ctxt, cmp);
3418 xmlXPathFreeCompExpr(cmp);
3419 xmlFree(URL);
3420 URL = val;
3422 if (URL == NULL)
3423 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3424 (const xmlChar *)
3425 "file",
3426 XSLT_XALAN_NAMESPACE);
3427 if (URL == NULL)
3428 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3429 (const xmlChar *)
3430 "href",
3431 XSLT_XALAN_NAMESPACE);
3432 } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) {
3433 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3434 (const xmlChar *) "href",
3435 NULL);
3438 } else {
3439 URL = xmlStrdup(comp->filename);
3442 if (URL == NULL) {
3443 xsltTransformError(ctxt, NULL, inst,
3444 "xsltDocumentElem: href/URI-Reference not found\n");
3445 return;
3449 * If the computation failed, it's likely that the URL wasn't escaped
3451 filename = xmlBuildURI(URL, (const xmlChar *) ctxt->outputFile);
3452 if (filename == NULL) {
3453 xmlChar *escURL;
3455 escURL=xmlURIEscapeStr(URL, BAD_CAST ":/.?,");
3456 if (escURL != NULL) {
3457 filename = xmlBuildURI(escURL, (const xmlChar *) ctxt->outputFile);
3458 xmlFree(escURL);
3462 if (filename == NULL) {
3463 xsltTransformError(ctxt, NULL, inst,
3464 "xsltDocumentElem: URL computation failed for %s\n",
3465 URL);
3466 xmlFree(URL);
3467 return;
3471 * Security checking: can we write to this resource
3473 if (ctxt->sec != NULL) {
3474 ret = xsltCheckWrite(ctxt->sec, ctxt, filename);
3475 if (ret <= 0) {
3476 if (ret == 0)
3477 xsltTransformError(ctxt, NULL, inst,
3478 "xsltDocumentElem: write rights for %s denied\n",
3479 filename);
3480 xmlFree(URL);
3481 xmlFree(filename);
3482 return;
3486 oldOutputFile = ctxt->outputFile;
3487 oldOutput = ctxt->output;
3488 oldInsert = ctxt->insert;
3489 oldType = ctxt->type;
3490 ctxt->outputFile = (const char *) filename;
3492 style = xsltNewStylesheet();
3493 if (style == NULL) {
3494 xsltTransformError(ctxt, NULL, inst,
3495 "xsltDocumentElem: out of memory\n");
3496 goto error;
3500 * Version described in 1.1 draft allows full parameterization
3501 * of the output.
3503 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3504 (const xmlChar *) "version",
3505 NULL);
3506 if (prop != NULL) {
3507 if (style->version != NULL)
3508 xmlFree(style->version);
3509 style->version = prop;
3511 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3512 (const xmlChar *) "encoding",
3513 NULL);
3514 if (prop != NULL) {
3515 if (style->encoding != NULL)
3516 xmlFree(style->encoding);
3517 style->encoding = prop;
3519 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3520 (const xmlChar *) "method",
3521 NULL);
3522 if (prop != NULL) {
3523 const xmlChar *URI;
3525 if (style->method != NULL)
3526 xmlFree(style->method);
3527 style->method = NULL;
3528 if (style->methodURI != NULL)
3529 xmlFree(style->methodURI);
3530 style->methodURI = NULL;
3532 URI = xsltGetQNameURI(inst, &prop);
3533 if (prop == NULL) {
3534 if (style != NULL) style->errors++;
3535 } else if (URI == NULL) {
3536 if ((xmlStrEqual(prop, (const xmlChar *) "xml")) ||
3537 (xmlStrEqual(prop, (const xmlChar *) "html")) ||
3538 (xmlStrEqual(prop, (const xmlChar *) "text"))) {
3539 style->method = prop;
3540 } else {
3541 xsltTransformError(ctxt, NULL, inst,
3542 "invalid value for method: %s\n", prop);
3543 if (style != NULL) style->warnings++;
3545 } else {
3546 style->method = prop;
3547 style->methodURI = xmlStrdup(URI);
3550 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3551 (const xmlChar *)
3552 "doctype-system", NULL);
3553 if (prop != NULL) {
3554 if (style->doctypeSystem != NULL)
3555 xmlFree(style->doctypeSystem);
3556 style->doctypeSystem = prop;
3558 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3559 (const xmlChar *)
3560 "doctype-public", NULL);
3561 if (prop != NULL) {
3562 if (style->doctypePublic != NULL)
3563 xmlFree(style->doctypePublic);
3564 style->doctypePublic = prop;
3566 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3567 (const xmlChar *) "standalone",
3568 NULL);
3569 if (prop != NULL) {
3570 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
3571 style->standalone = 1;
3572 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
3573 style->standalone = 0;
3574 } else {
3575 xsltTransformError(ctxt, NULL, inst,
3576 "invalid value for standalone: %s\n",
3577 prop);
3578 if (style != NULL) style->warnings++;
3580 xmlFree(prop);
3583 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3584 (const xmlChar *) "indent",
3585 NULL);
3586 if (prop != NULL) {
3587 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
3588 style->indent = 1;
3589 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
3590 style->indent = 0;
3591 } else {
3592 xsltTransformError(ctxt, NULL, inst,
3593 "invalid value for indent: %s\n", prop);
3594 if (style != NULL) style->warnings++;
3596 xmlFree(prop);
3599 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3600 (const xmlChar *)
3601 "omit-xml-declaration",
3602 NULL);
3603 if (prop != NULL) {
3604 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
3605 style->omitXmlDeclaration = 1;
3606 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
3607 style->omitXmlDeclaration = 0;
3608 } else {
3609 xsltTransformError(ctxt, NULL, inst,
3610 "invalid value for omit-xml-declaration: %s\n",
3611 prop);
3612 if (style != NULL) style->warnings++;
3614 xmlFree(prop);
3617 elements = xsltEvalAttrValueTemplate(ctxt, inst,
3618 (const xmlChar *)
3619 "cdata-section-elements",
3620 NULL);
3621 if (elements != NULL) {
3622 if (style->stripSpaces == NULL)
3623 style->stripSpaces = xmlHashCreate(10);
3624 if (style->stripSpaces == NULL) {
3625 xmlFree(elements);
3626 return;
3629 element = elements;
3630 while (*element != 0) {
3631 while (xmlIsBlank_ch(*element))
3632 element++;
3633 if (*element == 0)
3634 break;
3635 end = element;
3636 while ((*end != 0) && (!xmlIsBlank_ch(*end)))
3637 end++;
3638 element = xmlStrndup(element, end - element);
3639 if (element) {
3640 const xmlChar *URI;
3642 #ifdef WITH_XSLT_DEBUG_PARSING
3643 xsltGenericDebug(xsltGenericDebugContext,
3644 "add cdata section output element %s\n",
3645 element);
3646 #endif
3647 URI = xsltGetQNameURI(inst, &element);
3649 xmlHashAddEntry2(style->stripSpaces, element, URI,
3650 (xmlChar *) "cdata");
3651 xmlFree(element);
3653 element = end;
3655 xmlFree(elements);
3659 * Create a new document tree and process the element template
3661 XSLT_GET_IMPORT_PTR(method, style, method)
3662 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
3663 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
3664 XSLT_GET_IMPORT_PTR(version, style, version)
3665 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
3667 if ((method != NULL) &&
3668 (!xmlStrEqual(method, (const xmlChar *) "xml"))) {
3669 if (xmlStrEqual(method, (const xmlChar *) "html")) {
3670 ctxt->type = XSLT_OUTPUT_HTML;
3671 if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
3672 res = htmlNewDoc(doctypeSystem, doctypePublic);
3673 else {
3674 if (version != NULL) {
3675 #ifdef XSLT_GENERATE_HTML_DOCTYPE
3676 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
3677 #endif
3679 res = htmlNewDocNoDtD(doctypeSystem, doctypePublic);
3681 if (res == NULL)
3682 goto error;
3683 res->dict = ctxt->dict;
3684 xmlDictReference(res->dict);
3685 } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
3686 xsltTransformError(ctxt, NULL, inst,
3687 "xsltDocumentElem: unsupported method xhtml\n");
3688 ctxt->type = XSLT_OUTPUT_HTML;
3689 res = htmlNewDocNoDtD(doctypeSystem, doctypePublic);
3690 if (res == NULL)
3691 goto error;
3692 res->dict = ctxt->dict;
3693 xmlDictReference(res->dict);
3694 } else if (xmlStrEqual(method, (const xmlChar *) "text")) {
3695 ctxt->type = XSLT_OUTPUT_TEXT;
3696 res = xmlNewDoc(style->version);
3697 if (res == NULL)
3698 goto error;
3699 res->dict = ctxt->dict;
3700 xmlDictReference(res->dict);
3701 #ifdef WITH_XSLT_DEBUG
3702 xsltGenericDebug(xsltGenericDebugContext,
3703 "reusing transformation dict for output\n");
3704 #endif
3705 } else {
3706 xsltTransformError(ctxt, NULL, inst,
3707 "xsltDocumentElem: unsupported method (%s)\n",
3708 method);
3709 goto error;
3711 } else {
3712 ctxt->type = XSLT_OUTPUT_XML;
3713 res = xmlNewDoc(style->version);
3714 if (res == NULL)
3715 goto error;
3716 res->dict = ctxt->dict;
3717 xmlDictReference(res->dict);
3718 #ifdef WITH_XSLT_DEBUG
3719 xsltGenericDebug(xsltGenericDebugContext,
3720 "reusing transformation dict for output\n");
3721 #endif
3723 res->charset = XML_CHAR_ENCODING_UTF8;
3724 if (encoding != NULL)
3725 res->encoding = xmlStrdup(encoding);
3726 ctxt->output = res;
3727 ctxt->insert = (xmlNodePtr) res;
3728 xsltApplySequenceConstructor(ctxt, node, inst->children, NULL);
3731 * Do some post processing work depending on the generated output
3733 root = xmlDocGetRootElement(res);
3734 if (root != NULL) {
3735 const xmlChar *doctype = NULL;
3737 if ((root->ns != NULL) && (root->ns->prefix != NULL))
3738 doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name);
3739 if (doctype == NULL)
3740 doctype = root->name;
3743 * Apply the default selection of the method
3745 if ((method == NULL) &&
3746 (root->ns == NULL) &&
3747 (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) {
3748 xmlNodePtr tmp;
3750 tmp = res->children;
3751 while ((tmp != NULL) && (tmp != root)) {
3752 if (tmp->type == XML_ELEMENT_NODE)
3753 break;
3754 if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp)))
3755 break;
3756 tmp = tmp->next;
3758 if (tmp == root) {
3759 ctxt->type = XSLT_OUTPUT_HTML;
3760 res->type = XML_HTML_DOCUMENT_NODE;
3761 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
3762 res->intSubset = xmlCreateIntSubset(res, doctype,
3763 doctypePublic,
3764 doctypeSystem);
3765 #ifdef XSLT_GENERATE_HTML_DOCTYPE
3766 } else if (version != NULL) {
3767 xsltGetHTMLIDs(version, &doctypePublic,
3768 &doctypeSystem);
3769 if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
3770 res->intSubset =
3771 xmlCreateIntSubset(res, doctype,
3772 doctypePublic,
3773 doctypeSystem);
3774 #endif
3779 if (ctxt->type == XSLT_OUTPUT_XML) {
3780 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
3781 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
3782 if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
3783 res->intSubset = xmlCreateIntSubset(res, doctype,
3784 doctypePublic,
3785 doctypeSystem);
3790 * Calls to redirect:write also take an optional attribute append.
3791 * Attribute append="true|yes" which will attempt to simply append
3792 * to an existing file instead of always opening a new file. The
3793 * default behavior of always overwriting the file still happens
3794 * if we do not specify append.
3795 * Note that append use will forbid use of remote URI target.
3797 prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"append",
3798 NULL);
3799 if (prop != NULL) {
3800 if (xmlStrEqual(prop, (const xmlChar *) "true") ||
3801 xmlStrEqual(prop, (const xmlChar *) "yes")) {
3802 style->omitXmlDeclaration = 1;
3803 redirect_write_append = 1;
3804 } else
3805 style->omitXmlDeclaration = 0;
3806 xmlFree(prop);
3809 if (redirect_write_append) {
3810 FILE *f;
3812 f = fopen((const char *) filename, "ab");
3813 if (f == NULL) {
3814 ret = -1;
3815 } else {
3816 ret = xsltSaveResultToFile(f, res, style);
3817 fclose(f);
3819 } else {
3820 ret = xsltSaveResultToFilename((const char *) filename, res, style, 0);
3822 if (ret < 0) {
3823 xsltTransformError(ctxt, NULL, inst,
3824 "xsltDocumentElem: unable to save to %s\n",
3825 filename);
3826 #ifdef WITH_XSLT_DEBUG_EXTRA
3827 } else {
3828 xsltGenericDebug(xsltGenericDebugContext,
3829 "Wrote %d bytes to %s\n", ret, filename);
3830 #endif
3833 error:
3834 ctxt->output = oldOutput;
3835 ctxt->insert = oldInsert;
3836 ctxt->type = oldType;
3837 ctxt->outputFile = oldOutputFile;
3838 if (URL != NULL)
3839 xmlFree(URL);
3840 if (filename != NULL)
3841 xmlFree(filename);
3842 if (style != NULL)
3843 xsltFreeStylesheet(style);
3844 if (res != NULL)
3845 xmlFreeDoc(res);
3848 /************************************************************************
3850 * Most of the XSLT-1.0 transformations *
3852 ************************************************************************/
3855 * xsltSort:
3856 * @ctxt: a XSLT process context
3857 * @node: the node in the source tree.
3858 * @inst: the xslt sort node
3859 * @comp: precomputed information
3861 * function attached to xsl:sort nodes, but this should not be
3862 * called directly
3864 void
3865 xsltSort(xsltTransformContextPtr ctxt,
3866 xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst,
3867 xsltElemPreCompPtr comp) {
3868 if (comp == NULL) {
3869 xsltTransformError(ctxt, NULL, inst,
3870 "xsl:sort : compilation failed\n");
3871 return;
3873 xsltTransformError(ctxt, NULL, inst,
3874 "xsl:sort : improper use this should not be reached\n");
3878 * xsltCopy:
3879 * @ctxt: an XSLT process context
3880 * @node: the node in the source tree
3881 * @inst: the element node of the XSLT-copy instruction
3882 * @castedComp: computed information of the XSLT-copy instruction
3884 * Execute the XSLT-copy instruction on the source node.
3886 void
3887 xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node,
3888 xmlNodePtr inst, xsltElemPreCompPtr castedComp)
3890 #ifdef XSLT_REFACTORED
3891 xsltStyleItemCopyPtr comp = (xsltStyleItemCopyPtr) castedComp;
3892 #else
3893 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
3894 #endif
3895 xmlNodePtr copy, oldInsert;
3897 oldInsert = ctxt->insert;
3898 if (ctxt->insert != NULL) {
3899 switch (node->type) {
3900 case XML_TEXT_NODE:
3901 case XML_CDATA_SECTION_NODE:
3903 * This text comes from the stylesheet
3904 * For stylesheets, the set of whitespace-preserving
3905 * element names consists of just xsl:text.
3907 #ifdef WITH_XSLT_DEBUG_PROCESS
3908 if (node->type == XML_CDATA_SECTION_NODE) {
3909 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
3910 "xsltCopy: CDATA text %s\n", node->content));
3911 } else {
3912 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
3913 "xsltCopy: text %s\n", node->content));
3915 #endif
3916 xsltCopyText(ctxt, ctxt->insert, node, 0);
3917 break;
3918 case XML_DOCUMENT_NODE:
3919 case XML_HTML_DOCUMENT_NODE:
3920 break;
3921 case XML_ELEMENT_NODE:
3923 * REVISIT NOTE: The "fake" is a doc-node, not an element node.
3924 * REMOVED:
3925 * if (xmlStrEqual(node->name, BAD_CAST " fake node libxslt"))
3926 * return;
3929 #ifdef WITH_XSLT_DEBUG_PROCESS
3930 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
3931 "xsltCopy: node %s\n", node->name));
3932 #endif
3933 copy = xsltShallowCopyElem(ctxt, node, ctxt->insert, 0);
3934 ctxt->insert = copy;
3935 if (comp->use != NULL) {
3936 xsltApplyAttributeSet(ctxt, node, inst, comp->use);
3938 break;
3939 case XML_ATTRIBUTE_NODE: {
3940 #ifdef WITH_XSLT_DEBUG_PROCESS
3941 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
3942 "xsltCopy: attribute %s\n", node->name));
3943 #endif
3945 * REVISIT: We could also raise an error if the parent is not
3946 * an element node.
3947 * OPTIMIZE TODO: Can we set the value/children of the
3948 * attribute without an intermediate copy of the string value?
3950 xsltShallowCopyAttr(ctxt, inst, ctxt->insert, (xmlAttrPtr) node);
3951 break;
3953 case XML_PI_NODE:
3954 #ifdef WITH_XSLT_DEBUG_PROCESS
3955 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
3956 "xsltCopy: PI %s\n", node->name));
3957 #endif
3958 copy = xmlNewDocPI(ctxt->insert->doc, node->name,
3959 node->content);
3960 copy = xsltAddChild(ctxt->insert, copy);
3961 break;
3962 case XML_COMMENT_NODE:
3963 #ifdef WITH_XSLT_DEBUG_PROCESS
3964 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
3965 "xsltCopy: comment\n"));
3966 #endif
3967 copy = xmlNewComment(node->content);
3968 copy = xsltAddChild(ctxt->insert, copy);
3969 break;
3970 case XML_NAMESPACE_DECL:
3971 #ifdef WITH_XSLT_DEBUG_PROCESS
3972 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
3973 "xsltCopy: namespace declaration\n"));
3974 #endif
3975 xsltShallowCopyNsNode(ctxt, inst, ctxt->insert, (xmlNsPtr)node);
3976 break;
3977 default:
3978 break;
3983 switch (node->type) {
3984 case XML_DOCUMENT_NODE:
3985 case XML_HTML_DOCUMENT_NODE:
3986 case XML_ELEMENT_NODE:
3987 xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children,
3988 NULL);
3989 break;
3990 default:
3991 break;
3993 ctxt->insert = oldInsert;
3997 * xsltText:
3998 * @ctxt: a XSLT process context
3999 * @node: the node in the source tree.
4000 * @inst: the xslt text node
4001 * @comp: precomputed information
4003 * Process the xslt text node on the source node
4005 void
4006 xsltText(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED,
4007 xmlNodePtr inst, xsltElemPreCompPtr comp ATTRIBUTE_UNUSED) {
4008 if ((inst->children != NULL) && (comp != NULL)) {
4009 xmlNodePtr text = inst->children;
4010 xmlNodePtr copy;
4012 while (text != NULL) {
4013 if ((text->type != XML_TEXT_NODE) &&
4014 (text->type != XML_CDATA_SECTION_NODE)) {
4015 xsltTransformError(ctxt, NULL, inst,
4016 "xsl:text content problem\n");
4017 break;
4019 copy = xmlNewDocText(ctxt->output, text->content);
4020 if (text->type != XML_CDATA_SECTION_NODE) {
4021 #ifdef WITH_XSLT_DEBUG_PARSING
4022 xsltGenericDebug(xsltGenericDebugContext,
4023 "Disable escaping: %s\n", text->content);
4024 #endif
4025 copy->name = xmlStringTextNoenc;
4027 copy = xsltAddChild(ctxt->insert, copy);
4028 text = text->next;
4034 * xsltElement:
4035 * @ctxt: a XSLT process context
4036 * @node: the node in the source tree.
4037 * @inst: the xslt element node
4038 * @castedComp: precomputed information
4040 * Process the xslt element node on the source node
4042 void
4043 xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node,
4044 xmlNodePtr inst, xsltElemPreCompPtr castedComp) {
4045 #ifdef XSLT_REFACTORED
4046 xsltStyleItemElementPtr comp = (xsltStyleItemElementPtr) castedComp;
4047 #else
4048 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
4049 #endif
4050 xmlChar *prop = NULL;
4051 const xmlChar *name, *prefix = NULL, *nsName = NULL;
4052 xmlNodePtr copy;
4053 xmlNodePtr oldInsert;
4055 if (ctxt->insert == NULL)
4056 return;
4059 * A comp->has_name == 0 indicates that we need to skip this instruction,
4060 * since it was evaluated to be invalid already during compilation.
4062 if (!comp->has_name)
4063 return;
4066 * stack and saves
4068 oldInsert = ctxt->insert;
4070 if (comp->name == NULL) {
4071 /* TODO: fix attr acquisition wrt to the XSLT namespace */
4072 prop = xsltEvalAttrValueTemplate(ctxt, inst,
4073 (const xmlChar *) "name", XSLT_NAMESPACE);
4074 if (prop == NULL) {
4075 xsltTransformError(ctxt, NULL, inst,
4076 "xsl:element: The attribute 'name' is missing.\n");
4077 goto error;
4079 if (xmlValidateQName(prop, 0)) {
4080 xsltTransformError(ctxt, NULL, inst,
4081 "xsl:element: The effective name '%s' is not a "
4082 "valid QName.\n", prop);
4083 /* we fall through to catch any further errors, if possible */
4085 name = xsltSplitQName(ctxt->dict, prop, &prefix);
4086 xmlFree(prop);
4087 } else {
4089 * The "name" value was static.
4091 #ifdef XSLT_REFACTORED
4092 prefix = comp->nsPrefix;
4093 name = comp->name;
4094 #else
4095 name = xsltSplitQName(ctxt->dict, comp->name, &prefix);
4096 #endif
4100 * Create the new element
4102 if (ctxt->output->dict == ctxt->dict) {
4103 copy = xmlNewDocNodeEatName(ctxt->output, NULL, (xmlChar *)name, NULL);
4104 } else {
4105 copy = xmlNewDocNode(ctxt->output, NULL, (xmlChar *)name, NULL);
4107 if (copy == NULL) {
4108 xsltTransformError(ctxt, NULL, inst,
4109 "xsl:element : creation of %s failed\n", name);
4110 return;
4112 copy = xsltAddChild(ctxt->insert, copy);
4113 if (copy == NULL) {
4114 xsltTransformError(ctxt, NULL, inst,
4115 "xsl:element : xsltAddChild failed\n");
4116 return;
4120 * Namespace
4121 * ---------
4123 if (comp->has_ns) {
4124 if (comp->ns != NULL) {
4126 * No AVT; just plain text for the namespace name.
4128 if (comp->ns[0] != 0)
4129 nsName = comp->ns;
4130 } else {
4131 xmlChar *tmpNsName;
4133 * Eval the AVT.
4135 /* TODO: check attr acquisition wrt to the XSLT namespace */
4136 tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst,
4137 (const xmlChar *) "namespace", XSLT_NAMESPACE);
4139 * SPEC XSLT 1.0:
4140 * "If the string is empty, then the expanded-name of the
4141 * attribute has a null namespace URI."
4143 if ((tmpNsName != NULL) && (tmpNsName[0] != 0))
4144 nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1);
4145 xmlFree(tmpNsName);
4148 if (xmlStrEqual(nsName, BAD_CAST "http://www.w3.org/2000/xmlns/")) {
4149 xsltTransformError(ctxt, NULL, inst,
4150 "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ "
4151 "forbidden.\n");
4152 goto error;
4154 if (xmlStrEqual(nsName, XML_XML_NAMESPACE)) {
4155 prefix = BAD_CAST "xml";
4156 } else if (xmlStrEqual(prefix, BAD_CAST "xml")) {
4157 prefix = NULL;
4159 } else {
4160 xmlNsPtr ns;
4162 * SPEC XSLT 1.0:
4163 * "If the namespace attribute is not present, then the QName is
4164 * expanded into an expanded-name using the namespace declarations
4165 * in effect for the xsl:element element, including any default
4166 * namespace declaration.
4168 ns = xmlSearchNs(inst->doc, inst, prefix);
4169 if (ns == NULL) {
4171 * TODO: Check this in the compilation layer in case it's a
4172 * static value.
4174 if (prefix != NULL) {
4175 xsltTransformError(ctxt, NULL, inst,
4176 "xsl:element: The QName '%s:%s' has no "
4177 "namespace binding in scope in the stylesheet; "
4178 "this is an error, since the namespace was not "
4179 "specified by the instruction itself.\n", prefix, name);
4181 } else
4182 nsName = ns->href;
4185 * Find/create a matching ns-decl in the result tree.
4187 if (nsName != NULL) {
4188 if (xmlStrEqual(prefix, BAD_CAST "xmlns")) {
4189 /* Don't use a prefix of "xmlns" */
4190 xmlChar *pref = xmlStrdup(BAD_CAST "ns_1");
4192 copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, pref, copy);
4194 xmlFree(pref);
4195 } else {
4196 copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix,
4197 copy);
4199 } else if ((copy->parent != NULL) &&
4200 (copy->parent->type == XML_ELEMENT_NODE) &&
4201 (copy->parent->ns != NULL))
4204 * "Undeclare" the default namespace.
4206 xsltGetSpecialNamespace(ctxt, inst, NULL, NULL, copy);
4209 ctxt->insert = copy;
4211 if (comp->has_use) {
4212 if (comp->use != NULL) {
4213 xsltApplyAttributeSet(ctxt, node, inst, comp->use);
4214 } else {
4215 xmlChar *attrSets = NULL;
4217 * BUG TODO: use-attribute-sets is not a value template.
4218 * use-attribute-sets = qnames
4220 attrSets = xsltEvalAttrValueTemplate(ctxt, inst,
4221 (const xmlChar *)"use-attribute-sets", NULL);
4222 if (attrSets != NULL) {
4223 xsltApplyAttributeSet(ctxt, node, inst, attrSets);
4224 xmlFree(attrSets);
4229 * Instantiate the sequence constructor.
4231 if (inst->children != NULL)
4232 xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children,
4233 NULL);
4235 error:
4236 ctxt->insert = oldInsert;
4237 return;
4242 * xsltComment:
4243 * @ctxt: a XSLT process context
4244 * @node: the node in the source tree.
4245 * @inst: the xslt comment node
4246 * @comp: precomputed information
4248 * Process the xslt comment node on the source node
4250 void
4251 xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node,
4252 xmlNodePtr inst, xsltElemPreCompPtr comp ATTRIBUTE_UNUSED) {
4253 xmlChar *value = NULL;
4254 xmlNodePtr commentNode;
4255 int len;
4257 value = xsltEvalTemplateString(ctxt, node, inst);
4258 /* TODO: use or generate the compiled form */
4259 len = xmlStrlen(value);
4260 if (len > 0) {
4261 if ((value[len-1] == '-') ||
4262 (xmlStrstr(value, BAD_CAST "--"))) {
4263 xsltTransformError(ctxt, NULL, inst,
4264 "xsl:comment : '--' or ending '-' not allowed in comment\n");
4265 /* fall through to try to catch further errors */
4268 #ifdef WITH_XSLT_DEBUG_PROCESS
4269 if (value == NULL) {
4270 XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext,
4271 "xsltComment: empty\n"));
4272 } else {
4273 XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext,
4274 "xsltComment: content %s\n", value));
4276 #endif
4278 commentNode = xmlNewComment(value);
4279 commentNode = xsltAddChild(ctxt->insert, commentNode);
4281 if (value != NULL)
4282 xmlFree(value);
4286 * xsltProcessingInstruction:
4287 * @ctxt: a XSLT process context
4288 * @node: the node in the source tree.
4289 * @inst: the xslt processing-instruction node
4290 * @castedComp: precomputed information
4292 * Process the xslt processing-instruction node on the source node
4294 void
4295 xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node,
4296 xmlNodePtr inst, xsltElemPreCompPtr castedComp) {
4297 #ifdef XSLT_REFACTORED
4298 xsltStyleItemPIPtr comp = (xsltStyleItemPIPtr) castedComp;
4299 #else
4300 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
4301 #endif
4302 const xmlChar *name;
4303 xmlChar *value = NULL;
4304 xmlNodePtr pi;
4307 if (ctxt->insert == NULL)
4308 return;
4309 if (comp->has_name == 0)
4310 return;
4311 if (comp->name == NULL) {
4312 name = xsltEvalAttrValueTemplate(ctxt, inst,
4313 (const xmlChar *)"name", NULL);
4314 if (name == NULL) {
4315 xsltTransformError(ctxt, NULL, inst,
4316 "xsl:processing-instruction : name is missing\n");
4317 goto error;
4319 } else {
4320 name = comp->name;
4322 /* TODO: check that it's both an an NCName and a PITarget. */
4325 value = xsltEvalTemplateString(ctxt, node, inst);
4326 if (xmlStrstr(value, BAD_CAST "?>") != NULL) {
4327 xsltTransformError(ctxt, NULL, inst,
4328 "xsl:processing-instruction: '?>' not allowed within PI content\n");
4329 goto error;
4331 #ifdef WITH_XSLT_DEBUG_PROCESS
4332 if (value == NULL) {
4333 XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext,
4334 "xsltProcessingInstruction: %s empty\n", name));
4335 } else {
4336 XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext,
4337 "xsltProcessingInstruction: %s content %s\n", name, value));
4339 #endif
4341 pi = xmlNewDocPI(ctxt->insert->doc, name, value);
4342 pi = xsltAddChild(ctxt->insert, pi);
4344 error:
4345 if ((name != NULL) && (name != comp->name))
4346 xmlFree((xmlChar *) name);
4347 if (value != NULL)
4348 xmlFree(value);
4352 * xsltCopyOf:
4353 * @ctxt: an XSLT transformation context
4354 * @node: the current node in the source tree
4355 * @inst: the element node of the XSLT copy-of instruction
4356 * @castedComp: precomputed information of the XSLT copy-of instruction
4358 * Process the XSLT copy-of instruction.
4360 void
4361 xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
4362 xmlNodePtr inst, xsltElemPreCompPtr castedComp) {
4363 #ifdef XSLT_REFACTORED
4364 xsltStyleItemCopyOfPtr comp = (xsltStyleItemCopyOfPtr) castedComp;
4365 #else
4366 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
4367 #endif
4368 xmlXPathObjectPtr res = NULL;
4369 xmlNodeSetPtr list = NULL;
4370 int i;
4372 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
4373 return;
4374 if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
4375 xsltTransformError(ctxt, NULL, inst,
4376 "xsl:copy-of : compilation failed\n");
4377 return;
4381 * SPEC XSLT 1.0:
4382 * "The xsl:copy-of element can be used to insert a result tree
4383 * fragment into the result tree, without first converting it to
4384 * a string as xsl:value-of does (see [7.6.1 Generating Text with
4385 * xsl:value-of]). The required select attribute contains an
4386 * expression. When the result of evaluating the expression is a
4387 * result tree fragment, the complete fragment is copied into the
4388 * result tree. When the result is a node-set, all the nodes in the
4389 * set are copied in document order into the result tree; copying
4390 * an element node copies the attribute nodes, namespace nodes and
4391 * children of the element node as well as the element node itself;
4392 * a root node is copied by copying its children. When the result
4393 * is neither a node-set nor a result tree fragment, the result is
4394 * converted to a string and then inserted into the result tree,
4395 * as with xsl:value-of.
4398 #ifdef WITH_XSLT_DEBUG_PROCESS
4399 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
4400 "xsltCopyOf: select %s\n", comp->select));
4401 #endif
4404 * Evaluate the "select" expression.
4406 res = xsltPreCompEval(ctxt, node, comp);
4408 if (res != NULL) {
4409 if (res->type == XPATH_NODESET) {
4411 * Node-set
4412 * --------
4414 #ifdef WITH_XSLT_DEBUG_PROCESS
4415 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
4416 "xsltCopyOf: result is a node set\n"));
4417 #endif
4418 list = res->nodesetval;
4419 if (list != NULL) {
4420 xmlNodePtr cur;
4422 * The list is already sorted in document order by XPath.
4423 * Append everything in this order under ctxt->insert.
4425 for (i = 0;i < list->nodeNr;i++) {
4426 cur = list->nodeTab[i];
4427 if (cur == NULL)
4428 continue;
4429 if ((cur->type == XML_DOCUMENT_NODE) ||
4430 (cur->type == XML_HTML_DOCUMENT_NODE))
4432 xsltCopyTreeList(ctxt, inst,
4433 cur->children, ctxt->insert, 0, 0);
4434 } else if (cur->type == XML_ATTRIBUTE_NODE) {
4435 xsltShallowCopyAttr(ctxt, inst,
4436 ctxt->insert, (xmlAttrPtr) cur);
4437 } else {
4438 xsltCopyTree(ctxt, inst, cur, ctxt->insert, 0, 0);
4442 } else if (res->type == XPATH_XSLT_TREE) {
4444 * Result tree fragment
4445 * --------------------
4446 * E.g. via <xsl:variable ...><foo/></xsl:variable>
4447 * Note that the root node of such trees is an xmlDocPtr in Libxslt.
4449 #ifdef WITH_XSLT_DEBUG_PROCESS
4450 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
4451 "xsltCopyOf: result is a result tree fragment\n"));
4452 #endif
4453 list = res->nodesetval;
4454 if ((list != NULL) && (list->nodeTab != NULL) &&
4455 (list->nodeTab[0] != NULL) &&
4456 (IS_XSLT_REAL_NODE(list->nodeTab[0])))
4458 xsltCopyTreeList(ctxt, inst,
4459 list->nodeTab[0]->children, ctxt->insert, 0, 0);
4461 } else {
4462 xmlChar *value = NULL;
4464 * Convert to a string.
4466 value = xmlXPathCastToString(res);
4467 if (value == NULL) {
4468 xsltTransformError(ctxt, NULL, inst,
4469 "Internal error in xsltCopyOf(): "
4470 "failed to cast an XPath object to string.\n");
4471 ctxt->state = XSLT_STATE_STOPPED;
4472 } else {
4473 if (value[0] != 0) {
4475 * Append content as text node.
4477 xsltCopyTextString(ctxt, ctxt->insert, value, 0);
4479 xmlFree(value);
4481 #ifdef WITH_XSLT_DEBUG_PROCESS
4482 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
4483 "xsltCopyOf: result %s\n", res->stringval));
4484 #endif
4487 } else {
4488 ctxt->state = XSLT_STATE_STOPPED;
4491 if (res != NULL)
4492 xmlXPathFreeObject(res);
4496 * xsltValueOf:
4497 * @ctxt: a XSLT process context
4498 * @node: the node in the source tree.
4499 * @inst: the xslt value-of node
4500 * @castedComp: precomputed information
4502 * Process the xslt value-of node on the source node
4504 void
4505 xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
4506 xmlNodePtr inst, xsltElemPreCompPtr castedComp)
4508 #ifdef XSLT_REFACTORED
4509 xsltStyleItemValueOfPtr comp = (xsltStyleItemValueOfPtr) castedComp;
4510 #else
4511 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
4512 #endif
4513 xmlXPathObjectPtr res = NULL;
4514 xmlChar *value = NULL;
4516 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
4517 return;
4519 if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
4520 xsltTransformError(ctxt, NULL, inst,
4521 "Internal error in xsltValueOf(): "
4522 "The XSLT 'value-of' instruction was not compiled.\n");
4523 return;
4526 #ifdef WITH_XSLT_DEBUG_PROCESS
4527 XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext,
4528 "xsltValueOf: select %s\n", comp->select));
4529 #endif
4531 res = xsltPreCompEval(ctxt, node, comp);
4534 * Cast the XPath object to string.
4536 if (res != NULL) {
4537 value = xmlXPathCastToString(res);
4538 if (value == NULL) {
4539 xsltTransformError(ctxt, NULL, inst,
4540 "Internal error in xsltValueOf(): "
4541 "failed to cast an XPath object to string.\n");
4542 ctxt->state = XSLT_STATE_STOPPED;
4543 goto error;
4545 if (value[0] != 0) {
4546 xsltCopyTextString(ctxt, ctxt->insert, value, comp->noescape);
4548 } else {
4549 xsltTransformError(ctxt, NULL, inst,
4550 "XPath evaluation returned no result.\n");
4551 ctxt->state = XSLT_STATE_STOPPED;
4552 goto error;
4555 #ifdef WITH_XSLT_DEBUG_PROCESS
4556 if (value) {
4557 XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext,
4558 "xsltValueOf: result '%s'\n", value));
4560 #endif
4562 error:
4563 if (value != NULL)
4564 xmlFree(value);
4565 if (res != NULL)
4566 xmlXPathFreeObject(res);
4570 * xsltNumber:
4571 * @ctxt: a XSLT process context
4572 * @node: the node in the source tree.
4573 * @inst: the xslt number node
4574 * @castedComp: precomputed information
4576 * Process the xslt number node on the source node
4578 void
4579 xsltNumber(xsltTransformContextPtr ctxt, xmlNodePtr node,
4580 xmlNodePtr inst, xsltElemPreCompPtr castedComp)
4582 #ifdef XSLT_REFACTORED
4583 xsltStyleItemNumberPtr comp = (xsltStyleItemNumberPtr) castedComp;
4584 #else
4585 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
4586 #endif
4587 xmlXPathContextPtr xpctxt;
4588 xmlNsPtr *oldXPNamespaces;
4589 int oldXPNsNr;
4591 if (comp == NULL) {
4592 xsltTransformError(ctxt, NULL, inst,
4593 "xsl:number : compilation failed\n");
4594 return;
4597 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
4598 return;
4600 comp->numdata.doc = inst->doc;
4601 comp->numdata.node = inst;
4603 xpctxt = ctxt->xpathCtxt;
4604 oldXPNsNr = xpctxt->nsNr;
4605 oldXPNamespaces = xpctxt->namespaces;
4607 #ifdef XSLT_REFACTORED
4608 if (comp->inScopeNs != NULL) {
4609 xpctxt->namespaces = comp->inScopeNs->list;
4610 xpctxt->nsNr = comp->inScopeNs->xpathNumber;
4611 } else {
4612 xpctxt->namespaces = NULL;
4613 xpctxt->nsNr = 0;
4615 #else
4616 xpctxt->namespaces = comp->nsList;
4617 xpctxt->nsNr = comp->nsNr;
4618 #endif
4620 xsltNumberFormat(ctxt, &comp->numdata, node);
4622 xpctxt->nsNr = oldXPNsNr;
4623 xpctxt->namespaces = oldXPNamespaces;
4627 * xsltApplyImports:
4628 * @ctxt: an XSLT transformation context
4629 * @contextNode: the current node in the source tree.
4630 * @inst: the element node of the XSLT 'apply-imports' instruction
4631 * @comp: the compiled instruction
4633 * Process the XSLT apply-imports element.
4635 void
4636 xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
4637 xmlNodePtr inst,
4638 xsltElemPreCompPtr comp ATTRIBUTE_UNUSED)
4640 xsltTemplatePtr templ;
4642 if ((ctxt == NULL) || (inst == NULL))
4643 return;
4645 if (comp == NULL) {
4646 xsltTransformError(ctxt, NULL, inst,
4647 "Internal error in xsltApplyImports(): "
4648 "The XSLT 'apply-imports' instruction was not compiled.\n");
4649 return;
4652 * NOTE that ctxt->currentTemplateRule and ctxt->templ is not the
4653 * same; the former is the "Current Template Rule" as defined by the
4654 * XSLT spec, the latter is simply the template struct being
4655 * currently processed.
4657 if (ctxt->currentTemplateRule == NULL) {
4659 * SPEC XSLT 2.0:
4660 * "[ERR XTDE0560] It is a non-recoverable dynamic error if
4661 * xsl:apply-imports or xsl:next-match is evaluated when the
4662 * current template rule is null."
4664 xsltTransformError(ctxt, NULL, inst,
4665 "It is an error to call 'apply-imports' "
4666 "when there's no current template rule.\n");
4667 return;
4670 * TODO: Check if this is correct.
4672 templ = xsltGetTemplate(ctxt, contextNode,
4673 ctxt->currentTemplateRule->style);
4675 if (templ != NULL) {
4676 xsltTemplatePtr oldCurTemplRule = ctxt->currentTemplateRule;
4678 * Set the current template rule.
4680 ctxt->currentTemplateRule = templ;
4682 * URGENT TODO: Need xsl:with-param be handled somehow here?
4684 xsltApplyXSLTTemplate(ctxt, contextNode, templ->content,
4685 templ, NULL);
4687 ctxt->currentTemplateRule = oldCurTemplRule;
4689 else {
4690 /* Use built-in templates. */
4691 xsltDefaultProcessOneNode(ctxt, contextNode, NULL);
4696 * xsltCallTemplate:
4697 * @ctxt: a XSLT transformation context
4698 * @node: the "current node" in the source tree
4699 * @inst: the XSLT 'call-template' instruction
4700 * @castedComp: the compiled information of the instruction
4702 * Processes the XSLT call-template instruction on the source node.
4704 void
4705 xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
4706 xmlNodePtr inst, xsltElemPreCompPtr castedComp)
4708 #ifdef XSLT_REFACTORED
4709 xsltStyleItemCallTemplatePtr comp =
4710 (xsltStyleItemCallTemplatePtr) castedComp;
4711 #else
4712 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
4713 #endif
4714 xsltStackElemPtr withParams = NULL;
4716 if (ctxt->insert == NULL)
4717 return;
4718 if (comp == NULL) {
4719 xsltTransformError(ctxt, NULL, inst,
4720 "The XSLT 'call-template' instruction was not compiled.\n");
4721 return;
4725 * The template must have been precomputed
4727 if (comp->templ == NULL) {
4728 comp->templ = xsltFindTemplate(ctxt, comp->name, comp->ns);
4729 if (comp->templ == NULL) {
4730 if (comp->ns != NULL) {
4731 xsltTransformError(ctxt, NULL, inst,
4732 "The called template '{%s}%s' was not found.\n",
4733 comp->ns, comp->name);
4734 } else {
4735 xsltTransformError(ctxt, NULL, inst,
4736 "The called template '%s' was not found.\n",
4737 comp->name);
4739 return;
4743 #ifdef WITH_XSLT_DEBUG_PROCESS
4744 if ((comp != NULL) && (comp->name != NULL))
4745 XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
4746 "call-template: name %s\n", comp->name));
4747 #endif
4749 if (inst->children) {
4750 xmlNodePtr cur;
4751 xsltStackElemPtr param;
4753 cur = inst->children;
4754 while (cur != NULL) {
4755 #ifdef WITH_DEBUGGER
4756 if (ctxt->debugStatus != XSLT_DEBUG_NONE)
4757 xslHandleDebugger(cur, node, comp->templ, ctxt);
4758 #endif
4759 if (ctxt->state == XSLT_STATE_STOPPED) break;
4761 * TODO: The "with-param"s could be part of the "call-template"
4762 * structure. Avoid to "search" for params dynamically
4763 * in the XML tree every time.
4765 if (IS_XSLT_ELEM(cur)) {
4766 if (IS_XSLT_NAME(cur, "with-param")) {
4767 param = xsltParseStylesheetCallerParam(ctxt, cur);
4768 if (param != NULL) {
4769 param->next = withParams;
4770 withParams = param;
4772 } else {
4773 xsltGenericError(xsltGenericErrorContext,
4774 "xsl:call-template: misplaced xsl:%s\n", cur->name);
4776 } else {
4777 xsltGenericError(xsltGenericErrorContext,
4778 "xsl:call-template: misplaced %s element\n", cur->name);
4780 cur = cur->next;
4784 * Create a new frame using the params first
4786 xsltApplyXSLTTemplate(ctxt, node, comp->templ->content, comp->templ,
4787 withParams);
4788 if (withParams != NULL)
4789 xsltFreeStackElemList(withParams);
4791 #ifdef WITH_XSLT_DEBUG_PROCESS
4792 if ((comp != NULL) && (comp->name != NULL))
4793 XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
4794 "call-template returned: name %s\n", comp->name));
4795 #endif
4799 * xsltApplyTemplates:
4800 * @ctxt: a XSLT transformation context
4801 * @node: the 'current node' in the source tree
4802 * @inst: the element node of an XSLT 'apply-templates' instruction
4803 * @castedComp: the compiled instruction
4805 * Processes the XSLT 'apply-templates' instruction on the current node.
4807 void
4808 xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node,
4809 xmlNodePtr inst, xsltElemPreCompPtr castedComp)
4811 #ifdef XSLT_REFACTORED
4812 xsltStyleItemApplyTemplatesPtr comp =
4813 (xsltStyleItemApplyTemplatesPtr) castedComp;
4814 #else
4815 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
4816 #endif
4817 int i;
4818 xmlNodePtr cur, oldContextNode;
4819 xmlNodeSetPtr list = NULL, oldList;
4820 xsltStackElemPtr withParams = NULL;
4821 int oldXPProximityPosition, oldXPContextSize;
4822 const xmlChar *oldMode, *oldModeURI;
4823 xmlDocPtr oldXPDoc;
4824 xsltDocumentPtr oldDocInfo;
4825 xmlXPathContextPtr xpctxt;
4827 if (comp == NULL) {
4828 xsltTransformError(ctxt, NULL, inst,
4829 "xsl:apply-templates : compilation failed\n");
4830 return;
4832 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
4833 return;
4835 #ifdef WITH_XSLT_DEBUG_PROCESS
4836 if ((node != NULL) && (node->name != NULL))
4837 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
4838 "xsltApplyTemplates: node: '%s'\n", node->name));
4839 #endif
4841 xpctxt = ctxt->xpathCtxt;
4843 * Save context states.
4845 oldContextNode = ctxt->node;
4846 oldMode = ctxt->mode;
4847 oldModeURI = ctxt->modeURI;
4848 oldDocInfo = ctxt->document;
4849 oldList = ctxt->nodeList;
4852 * The xpath context size and proximity position, as
4853 * well as the xpath and context documents, may be changed
4854 * so we save their initial state and will restore on exit
4856 oldXPContextSize = xpctxt->contextSize;
4857 oldXPProximityPosition = xpctxt->proximityPosition;
4858 oldXPDoc = xpctxt->doc;
4861 * Set up contexts.
4863 ctxt->mode = comp->mode;
4864 ctxt->modeURI = comp->modeURI;
4866 if (comp->select != NULL) {
4867 xmlXPathObjectPtr res = NULL;
4869 if (comp->comp == NULL) {
4870 xsltTransformError(ctxt, NULL, inst,
4871 "xsl:apply-templates : compilation failed\n");
4872 goto error;
4874 #ifdef WITH_XSLT_DEBUG_PROCESS
4875 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
4876 "xsltApplyTemplates: select %s\n", comp->select));
4877 #endif
4879 res = xsltPreCompEval(ctxt, node, comp);
4881 if (res != NULL) {
4882 if (res->type == XPATH_NODESET) {
4883 list = res->nodesetval; /* consume the node set */
4884 res->nodesetval = NULL;
4885 } else {
4886 xsltTransformError(ctxt, NULL, inst,
4887 "The 'select' expression did not evaluate to a "
4888 "node set.\n");
4889 ctxt->state = XSLT_STATE_STOPPED;
4890 xmlXPathFreeObject(res);
4891 goto error;
4893 xmlXPathFreeObject(res);
4895 * Note: An xsl:apply-templates with a 'select' attribute,
4896 * can change the current source doc.
4898 } else {
4899 xsltTransformError(ctxt, NULL, inst,
4900 "Failed to evaluate the 'select' expression.\n");
4901 ctxt->state = XSLT_STATE_STOPPED;
4902 goto error;
4904 if (list == NULL) {
4905 #ifdef WITH_XSLT_DEBUG_PROCESS
4906 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
4907 "xsltApplyTemplates: select didn't evaluate to a node list\n"));
4908 #endif
4909 goto exit;
4913 * NOTE: Previously a document info (xsltDocument) was
4914 * created and attached to the Result Tree Fragment.
4915 * But such a document info is created on demand in
4916 * xsltKeyFunction() (functions.c), so we need to create
4917 * it here beforehand.
4918 * In order to take care of potential keys we need to
4919 * do some extra work for the case when a Result Tree Fragment
4920 * is converted into a nodeset (e.g. exslt:node-set()) :
4921 * We attach a "pseudo-doc" (xsltDocument) to _private.
4922 * This xsltDocument, together with the keyset, will be freed
4923 * when the Result Tree Fragment is freed.
4926 #if 0
4927 if ((ctxt->nbKeys > 0) &&
4928 (list->nodeNr != 0) &&
4929 (list->nodeTab[0]->doc != NULL) &&
4930 XSLT_IS_RES_TREE_FRAG(list->nodeTab[0]->doc))
4933 * NOTE that it's also OK if @effectiveDocInfo will be
4934 * set to NULL.
4936 isRTF = 1;
4937 effectiveDocInfo = list->nodeTab[0]->doc->_private;
4939 #endif
4940 } else {
4942 * Build an XPath node set with the children
4944 list = xmlXPathNodeSetCreate(NULL);
4945 if (list == NULL)
4946 goto error;
4947 if (node->type != XML_NAMESPACE_DECL)
4948 cur = node->children;
4949 else
4950 cur = NULL;
4951 while (cur != NULL) {
4952 if (IS_XSLT_REAL_NODE(cur))
4953 xmlXPathNodeSetAddUnique(list, cur);
4954 cur = cur->next;
4958 #ifdef WITH_XSLT_DEBUG_PROCESS
4959 if (list != NULL)
4960 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
4961 "xsltApplyTemplates: list of %d nodes\n", list->nodeNr));
4962 #endif
4964 if ((list == NULL) || (list->nodeNr == 0))
4965 goto exit;
4968 * Set the context's node set and size; this is also needed for
4969 * for xsltDoSortFunction().
4971 ctxt->nodeList = list;
4973 * Process xsl:with-param and xsl:sort instructions.
4974 * (The code became so verbose just to avoid the
4975 * xmlNodePtr sorts[XSLT_MAX_SORT] if there's no xsl:sort)
4976 * BUG TODO: We are not using namespaced potentially defined on the
4977 * xsl:sort or xsl:with-param elements; XPath expression might fail.
4979 if (inst->children) {
4980 xsltStackElemPtr param;
4982 cur = inst->children;
4983 while (cur) {
4985 #ifdef WITH_DEBUGGER
4986 if (ctxt->debugStatus != XSLT_DEBUG_NONE)
4987 xslHandleDebugger(cur, node, NULL, ctxt);
4988 #endif
4989 if (ctxt->state == XSLT_STATE_STOPPED)
4990 break;
4991 if (cur->type == XML_TEXT_NODE) {
4992 cur = cur->next;
4993 continue;
4995 if (! IS_XSLT_ELEM(cur))
4996 break;
4997 if (IS_XSLT_NAME(cur, "with-param")) {
4998 param = xsltParseStylesheetCallerParam(ctxt, cur);
4999 if (param != NULL) {
5000 param->next = withParams;
5001 withParams = param;
5004 if (IS_XSLT_NAME(cur, "sort")) {
5005 xsltTemplatePtr oldCurTempRule =
5006 ctxt->currentTemplateRule;
5007 int nbsorts = 0;
5008 xmlNodePtr sorts[XSLT_MAX_SORT];
5010 sorts[nbsorts++] = cur;
5011 cur = cur->next;
5013 while (cur) {
5015 #ifdef WITH_DEBUGGER
5016 if (ctxt->debugStatus != XSLT_DEBUG_NONE)
5017 xslHandleDebugger(cur, node, NULL, ctxt);
5018 #endif
5019 if (ctxt->state == XSLT_STATE_STOPPED)
5020 break;
5022 if (cur->type == XML_TEXT_NODE) {
5023 cur = cur->next;
5024 continue;
5027 if (! IS_XSLT_ELEM(cur))
5028 break;
5029 if (IS_XSLT_NAME(cur, "with-param")) {
5030 param = xsltParseStylesheetCallerParam(ctxt, cur);
5031 if (param != NULL) {
5032 param->next = withParams;
5033 withParams = param;
5036 if (IS_XSLT_NAME(cur, "sort")) {
5037 if (nbsorts >= XSLT_MAX_SORT) {
5038 xsltTransformError(ctxt, NULL, cur,
5039 "The number (%d) of xsl:sort instructions exceeds the "
5040 "maximum allowed by this processor's settings.\n",
5041 nbsorts);
5042 ctxt->state = XSLT_STATE_STOPPED;
5043 break;
5044 } else {
5045 sorts[nbsorts++] = cur;
5048 cur = cur->next;
5051 * The "current template rule" is cleared for xsl:sort.
5053 ctxt->currentTemplateRule = NULL;
5055 * Sort.
5057 xsltDoSortFunction(ctxt, sorts, nbsorts);
5058 ctxt->currentTemplateRule = oldCurTempRule;
5059 break;
5061 cur = cur->next;
5064 xpctxt->contextSize = list->nodeNr;
5066 * Apply templates for all selected source nodes.
5068 for (i = 0; i < list->nodeNr; i++) {
5069 cur = list->nodeTab[i];
5071 * The node becomes the "current node".
5073 ctxt->node = cur;
5075 * An xsl:apply-templates can change the current context doc.
5076 * OPTIMIZE TODO: Get rid of the need to set the context doc.
5078 if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL))
5079 xpctxt->doc = cur->doc;
5081 xpctxt->proximityPosition = i + 1;
5083 * Find and apply a template for this node.
5085 xsltProcessOneNode(ctxt, cur, withParams);
5088 exit:
5089 error:
5091 * Free the parameter list.
5093 if (withParams != NULL)
5094 xsltFreeStackElemList(withParams);
5095 if (list != NULL)
5096 xmlXPathFreeNodeSet(list);
5098 * Restore context states.
5100 xpctxt->doc = oldXPDoc;
5101 xpctxt->contextSize = oldXPContextSize;
5102 xpctxt->proximityPosition = oldXPProximityPosition;
5104 ctxt->document = oldDocInfo;
5105 ctxt->nodeList = oldList;
5106 ctxt->node = oldContextNode;
5107 ctxt->mode = oldMode;
5108 ctxt->modeURI = oldModeURI;
5113 * xsltChoose:
5114 * @ctxt: a XSLT process context
5115 * @contextNode: the current node in the source tree
5116 * @inst: the xsl:choose instruction
5117 * @comp: compiled information of the instruction
5119 * Processes the xsl:choose instruction on the source node.
5121 void
5122 xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
5123 xmlNodePtr inst, xsltElemPreCompPtr comp ATTRIBUTE_UNUSED)
5125 xmlNodePtr cur;
5127 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL))
5128 return;
5131 * TODO: Content model checks should be done only at compilation
5132 * time.
5134 cur = inst->children;
5135 if (cur == NULL) {
5136 xsltTransformError(ctxt, NULL, inst,
5137 "xsl:choose: The instruction has no content.\n");
5138 return;
5141 #ifdef XSLT_REFACTORED
5143 * We don't check the content model during transformation.
5145 #else
5146 if ((! IS_XSLT_ELEM(cur)) || (! IS_XSLT_NAME(cur, "when"))) {
5147 xsltTransformError(ctxt, NULL, inst,
5148 "xsl:choose: xsl:when expected first\n");
5149 return;
5151 #endif
5154 int testRes = 0, res = 0;
5156 #ifdef XSLT_REFACTORED
5157 xsltStyleItemWhenPtr wcomp = NULL;
5158 #else
5159 xsltStylePreCompPtr wcomp = NULL;
5160 #endif
5163 * Process xsl:when ---------------------------------------------------
5165 while (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "when")) {
5166 wcomp = cur->psvi;
5168 if ((wcomp == NULL) || (wcomp->test == NULL) ||
5169 (wcomp->comp == NULL))
5171 xsltTransformError(ctxt, NULL, cur,
5172 "Internal error in xsltChoose(): "
5173 "The XSLT 'when' instruction was not compiled.\n");
5174 goto error;
5178 #ifdef WITH_DEBUGGER
5179 if (xslDebugStatus != XSLT_DEBUG_NONE) {
5181 * TODO: Isn't comp->templ always NULL for xsl:choose?
5183 xslHandleDebugger(cur, contextNode, NULL, ctxt);
5185 #endif
5186 #ifdef WITH_XSLT_DEBUG_PROCESS
5187 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
5188 "xsltChoose: test %s\n", wcomp->test));
5189 #endif
5191 #ifdef XSLT_FAST_IF
5192 res = xsltPreCompEvalToBoolean(ctxt, contextNode, wcomp);
5194 if (res == -1) {
5195 ctxt->state = XSLT_STATE_STOPPED;
5196 goto error;
5198 testRes = (res == 1) ? 1 : 0;
5200 #else /* XSLT_FAST_IF */
5202 res = xsltPreCompEval(ctxt, cotextNode, wcomp);
5204 if (res != NULL) {
5205 if (res->type != XPATH_BOOLEAN)
5206 res = xmlXPathConvertBoolean(res);
5207 if (res->type == XPATH_BOOLEAN)
5208 testRes = res->boolval;
5209 else {
5210 #ifdef WITH_XSLT_DEBUG_PROCESS
5211 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
5212 "xsltChoose: test didn't evaluate to a boolean\n"));
5213 #endif
5214 goto error;
5216 xmlXPathFreeObject(res);
5217 res = NULL;
5218 } else {
5219 ctxt->state = XSLT_STATE_STOPPED;
5220 goto error;
5223 #endif /* else of XSLT_FAST_IF */
5225 #ifdef WITH_XSLT_DEBUG_PROCESS
5226 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
5227 "xsltChoose: test evaluate to %d\n", testRes));
5228 #endif
5229 if (testRes)
5230 goto test_is_true;
5232 cur = cur->next;
5236 * Process xsl:otherwise ----------------------------------------------
5238 if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "otherwise")) {
5240 #ifdef WITH_DEBUGGER
5241 if (xslDebugStatus != XSLT_DEBUG_NONE)
5242 xslHandleDebugger(cur, contextNode, NULL, ctxt);
5243 #endif
5245 #ifdef WITH_XSLT_DEBUG_PROCESS
5246 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
5247 "evaluating xsl:otherwise\n"));
5248 #endif
5249 goto test_is_true;
5251 goto exit;
5253 test_is_true:
5255 goto process_sequence;
5258 process_sequence:
5261 * Instantiate the sequence constructor.
5263 xsltApplySequenceConstructor(ctxt, ctxt->node, cur->children,
5264 NULL);
5266 exit:
5267 error:
5268 return;
5272 * xsltIf:
5273 * @ctxt: a XSLT process context
5274 * @contextNode: the current node in the source tree
5275 * @inst: the xsl:if instruction
5276 * @castedComp: compiled information of the instruction
5278 * Processes the xsl:if instruction on the source node.
5280 void
5281 xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
5282 xmlNodePtr inst, xsltElemPreCompPtr castedComp)
5284 int res = 0;
5286 #ifdef XSLT_REFACTORED
5287 xsltStyleItemIfPtr comp = (xsltStyleItemIfPtr) castedComp;
5288 #else
5289 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
5290 #endif
5292 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL))
5293 return;
5294 if ((comp == NULL) || (comp->test == NULL) || (comp->comp == NULL)) {
5295 xsltTransformError(ctxt, NULL, inst,
5296 "Internal error in xsltIf(): "
5297 "The XSLT 'if' instruction was not compiled.\n");
5298 return;
5301 #ifdef WITH_XSLT_DEBUG_PROCESS
5302 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext,
5303 "xsltIf: test %s\n", comp->test));
5304 #endif
5306 #ifdef XSLT_FAST_IF
5308 xmlDocPtr oldLocalFragmentTop = ctxt->localRVT;
5310 res = xsltPreCompEvalToBoolean(ctxt, contextNode, comp);
5313 * Cleanup fragments created during evaluation of the
5314 * "select" expression.
5316 if (oldLocalFragmentTop != ctxt->localRVT)
5317 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
5320 #ifdef WITH_XSLT_DEBUG_PROCESS
5321 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext,
5322 "xsltIf: test evaluate to %d\n", res));
5323 #endif
5325 if (res == -1) {
5326 ctxt->state = XSLT_STATE_STOPPED;
5327 goto error;
5329 if (res == 1) {
5331 * Instantiate the sequence constructor of xsl:if.
5333 xsltApplySequenceConstructor(ctxt,
5334 contextNode, inst->children, NULL);
5337 #else /* XSLT_FAST_IF */
5340 * OLD CODE:
5342 xmlXPathObjectPtr xpobj = xsltPreCompEval(ctxt, contextNode, comp);
5343 if (xpobj != NULL) {
5344 if (xpobj->type != XPATH_BOOLEAN)
5345 xpobj = xmlXPathConvertBoolean(xpobj);
5346 if (xpobj->type == XPATH_BOOLEAN) {
5347 res = xpobj->boolval;
5349 #ifdef WITH_XSLT_DEBUG_PROCESS
5350 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext,
5351 "xsltIf: test evaluate to %d\n", res));
5352 #endif
5353 if (res) {
5354 xsltApplySequenceConstructor(ctxt,
5355 contextNode, inst->children, NULL);
5357 } else {
5359 #ifdef WITH_XSLT_DEBUG_PROCESS
5360 XSLT_TRACE(ctxt, XSLT_TRACE_IF,
5361 xsltGenericDebug(xsltGenericDebugContext,
5362 "xsltIf: test didn't evaluate to a boolean\n"));
5363 #endif
5364 ctxt->state = XSLT_STATE_STOPPED;
5366 xmlXPathFreeObject(xpobj);
5367 } else {
5368 ctxt->state = XSLT_STATE_STOPPED;
5371 #endif /* else of XSLT_FAST_IF */
5373 error:
5374 return;
5378 * xsltForEach:
5379 * @ctxt: an XSLT transformation context
5380 * @contextNode: the "current node" in the source tree
5381 * @inst: the element node of the xsl:for-each instruction
5382 * @castedComp: the compiled information of the instruction
5384 * Process the xslt for-each node on the source node
5386 void
5387 xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
5388 xmlNodePtr inst, xsltElemPreCompPtr castedComp)
5390 #ifdef XSLT_REFACTORED
5391 xsltStyleItemForEachPtr comp = (xsltStyleItemForEachPtr) castedComp;
5392 #else
5393 xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
5394 #endif
5395 int i;
5396 xmlXPathObjectPtr res = NULL;
5397 xmlNodePtr cur, curInst;
5398 xmlNodeSetPtr list = NULL;
5399 xmlNodeSetPtr oldList;
5400 int oldXPProximityPosition, oldXPContextSize;
5401 xmlNodePtr oldContextNode;
5402 xsltTemplatePtr oldCurTemplRule;
5403 xmlDocPtr oldXPDoc;
5404 xsltDocumentPtr oldDocInfo;
5405 xmlXPathContextPtr xpctxt;
5407 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) {
5408 xsltGenericError(xsltGenericErrorContext,
5409 "xsltForEach(): Bad arguments.\n");
5410 return;
5413 if (comp == NULL) {
5414 xsltTransformError(ctxt, NULL, inst,
5415 "Internal error in xsltForEach(): "
5416 "The XSLT 'for-each' instruction was not compiled.\n");
5417 return;
5419 if ((comp->select == NULL) || (comp->comp == NULL)) {
5420 xsltTransformError(ctxt, NULL, inst,
5421 "Internal error in xsltForEach(): "
5422 "The selecting expression of the XSLT 'for-each' "
5423 "instruction was not compiled correctly.\n");
5424 return;
5426 xpctxt = ctxt->xpathCtxt;
5428 #ifdef WITH_XSLT_DEBUG_PROCESS
5429 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext,
5430 "xsltForEach: select %s\n", comp->select));
5431 #endif
5434 * Save context states.
5436 oldDocInfo = ctxt->document;
5437 oldList = ctxt->nodeList;
5438 oldContextNode = ctxt->node;
5440 * The "current template rule" is cleared for the instantiation of
5441 * xsl:for-each.
5443 oldCurTemplRule = ctxt->currentTemplateRule;
5444 ctxt->currentTemplateRule = NULL;
5446 oldXPDoc = xpctxt->doc;
5447 oldXPProximityPosition = xpctxt->proximityPosition;
5448 oldXPContextSize = xpctxt->contextSize;
5451 * Evaluate the 'select' expression.
5453 res = xsltPreCompEval(ctxt, contextNode, comp);
5455 if (res != NULL) {
5456 if (res->type == XPATH_NODESET)
5457 list = res->nodesetval;
5458 else {
5459 xsltTransformError(ctxt, NULL, inst,
5460 "The 'select' expression does not evaluate to a node set.\n");
5462 #ifdef WITH_XSLT_DEBUG_PROCESS
5463 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext,
5464 "xsltForEach: select didn't evaluate to a node list\n"));
5465 #endif
5466 goto error;
5468 } else {
5469 xsltTransformError(ctxt, NULL, inst,
5470 "Failed to evaluate the 'select' expression.\n");
5471 ctxt->state = XSLT_STATE_STOPPED;
5472 goto error;
5475 if ((list == NULL) || (list->nodeNr <= 0))
5476 goto exit;
5478 #ifdef WITH_XSLT_DEBUG_PROCESS
5479 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext,
5480 "xsltForEach: select evaluates to %d nodes\n", list->nodeNr));
5481 #endif
5484 * Set the list; this has to be done already here for xsltDoSortFunction().
5486 ctxt->nodeList = list;
5488 * Handle xsl:sort instructions and skip them for further processing.
5489 * BUG TODO: We are not using namespaced potentially defined on the
5490 * xsl:sort element; XPath expression might fail.
5492 curInst = inst->children;
5493 if (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) {
5494 int nbsorts = 0;
5495 xmlNodePtr sorts[XSLT_MAX_SORT];
5497 sorts[nbsorts++] = curInst;
5499 #ifdef WITH_DEBUGGER
5500 if (xslDebugStatus != XSLT_DEBUG_NONE)
5501 xslHandleDebugger(curInst, contextNode, NULL, ctxt);
5502 #endif
5504 curInst = curInst->next;
5505 while (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) {
5506 if (nbsorts >= XSLT_MAX_SORT) {
5507 xsltTransformError(ctxt, NULL, curInst,
5508 "The number of xsl:sort instructions exceeds the "
5509 "maximum (%d) allowed by this processor.\n",
5510 XSLT_MAX_SORT);
5511 goto error;
5512 } else {
5513 sorts[nbsorts++] = curInst;
5516 #ifdef WITH_DEBUGGER
5517 if (xslDebugStatus != XSLT_DEBUG_NONE)
5518 xslHandleDebugger(curInst, contextNode, NULL, ctxt);
5519 #endif
5520 curInst = curInst->next;
5522 xsltDoSortFunction(ctxt, sorts, nbsorts);
5524 xpctxt->contextSize = list->nodeNr;
5526 * Instantiate the sequence constructor for each selected node.
5528 for (i = 0; i < list->nodeNr; i++) {
5529 cur = list->nodeTab[i];
5531 * The selected node becomes the "current node".
5533 ctxt->node = cur;
5535 * An xsl:for-each can change the current context doc.
5536 * OPTIMIZE TODO: Get rid of the need to set the context doc.
5538 if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL))
5539 xpctxt->doc = cur->doc;
5541 xpctxt->proximityPosition = i + 1;
5543 xsltApplySequenceConstructor(ctxt, cur, curInst, NULL);
5546 exit:
5547 error:
5548 if (res != NULL)
5549 xmlXPathFreeObject(res);
5551 * Restore old states.
5553 ctxt->document = oldDocInfo;
5554 ctxt->nodeList = oldList;
5555 ctxt->node = oldContextNode;
5556 ctxt->currentTemplateRule = oldCurTemplRule;
5558 xpctxt->doc = oldXPDoc;
5559 xpctxt->contextSize = oldXPContextSize;
5560 xpctxt->proximityPosition = oldXPProximityPosition;
5563 /************************************************************************
5565 * Generic interface *
5567 ************************************************************************/
5569 #ifdef XSLT_GENERATE_HTML_DOCTYPE
5570 typedef struct xsltHTMLVersion {
5571 const char *version;
5572 const char *public;
5573 const char *system;
5574 } xsltHTMLVersion;
5576 static xsltHTMLVersion xsltHTMLVersions[] = {
5577 { "5", NULL, "about:legacy-compat" },
5578 { "4.01frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
5579 "http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd"},
5580 { "4.01strict", "-//W3C//DTD HTML 4.01//EN",
5581 "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"},
5582 { "4.01trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
5583 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
5584 { "4.01", "-//W3C//DTD HTML 4.01 Transitional//EN",
5585 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
5586 { "4.0strict", "-//W3C//DTD HTML 4.01//EN",
5587 "http://www.w3.org/TR/html4/strict.dtd"},
5588 { "4.0trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
5589 "http://www.w3.org/TR/html4/loose.dtd"},
5590 { "4.0frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
5591 "http://www.w3.org/TR/html4/frameset.dtd"},
5592 { "4.0", "-//W3C//DTD HTML 4.01 Transitional//EN",
5593 "http://www.w3.org/TR/html4/loose.dtd"},
5594 { "3.2", "-//W3C//DTD HTML 3.2//EN", NULL }
5598 * xsltGetHTMLIDs:
5599 * @version: the version string
5600 * @publicID: used to return the public ID
5601 * @systemID: used to return the system ID
5603 * Returns -1 if not found, 0 otherwise and the system and public
5604 * Identifier for this given verion of HTML
5606 static int
5607 xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID,
5608 const xmlChar **systemID) {
5609 unsigned int i;
5610 if (version == NULL)
5611 return(-1);
5612 for (i = 0;i < (sizeof(xsltHTMLVersions)/sizeof(xsltHTMLVersions[1]));
5613 i++) {
5614 if (!xmlStrcasecmp(version,
5615 (const xmlChar *) xsltHTMLVersions[i].version)) {
5616 if (publicID != NULL)
5617 *publicID = (const xmlChar *) xsltHTMLVersions[i].public;
5618 if (systemID != NULL)
5619 *systemID = (const xmlChar *) xsltHTMLVersions[i].system;
5620 return(0);
5623 return(-1);
5625 #endif
5628 * xsltApplyStripSpaces:
5629 * @ctxt: a XSLT process context
5630 * @node: the root of the XML tree
5632 * Strip the unwanted ignorable spaces from the input tree
5634 void
5635 xsltApplyStripSpaces(xsltTransformContextPtr ctxt, xmlNodePtr node) {
5636 xmlNodePtr current;
5637 #ifdef WITH_XSLT_DEBUG_PROCESS
5638 int nb = 0;
5639 #endif
5642 current = node;
5643 while (current != NULL) {
5645 * Cleanup children empty nodes if asked for
5647 if ((IS_XSLT_REAL_NODE(current)) &&
5648 (current->children != NULL) &&
5649 (xsltFindElemSpaceHandling(ctxt, current))) {
5650 xmlNodePtr delete = NULL, cur = current->children;
5652 while (cur != NULL) {
5653 if (IS_BLANK_NODE(cur))
5654 delete = cur;
5656 cur = cur->next;
5657 if (delete != NULL) {
5658 xmlUnlinkNode(delete);
5659 xmlFreeNode(delete);
5660 delete = NULL;
5661 #ifdef WITH_XSLT_DEBUG_PROCESS
5662 nb++;
5663 #endif
5669 * Skip to next node in document order.
5671 if (node->type == XML_ENTITY_REF_NODE) {
5672 /* process deep in entities */
5673 xsltApplyStripSpaces(ctxt, node->children);
5675 if ((current->children != NULL) &&
5676 (current->type != XML_ENTITY_REF_NODE)) {
5677 current = current->children;
5678 } else if (current->next != NULL) {
5679 current = current->next;
5680 } else {
5681 do {
5682 current = current->parent;
5683 if (current == NULL)
5684 break;
5685 if (current == node)
5686 goto done;
5687 if (current->next != NULL) {
5688 current = current->next;
5689 break;
5691 } while (current != NULL);
5695 done:
5696 #ifdef WITH_XSLT_DEBUG_PROCESS
5697 XSLT_TRACE(ctxt,XSLT_TRACE_STRIP_SPACES,xsltGenericDebug(xsltGenericDebugContext,
5698 "xsltApplyStripSpaces: removed %d ignorable blank node\n", nb));
5699 #endif
5700 return;
5703 static int
5704 xsltCountKeys(xsltTransformContextPtr ctxt)
5706 xsltStylesheetPtr style;
5707 xsltKeyDefPtr keyd;
5709 if (ctxt == NULL)
5710 return(-1);
5713 * Do we have those nastly templates with a key() in the match pattern?
5715 ctxt->hasTemplKeyPatterns = 0;
5716 style = ctxt->style;
5717 while (style != NULL) {
5718 if (style->keyMatch != NULL) {
5719 ctxt->hasTemplKeyPatterns = 1;
5720 break;
5722 style = xsltNextImport(style);
5725 * Count number of key declarations.
5727 ctxt->nbKeys = 0;
5728 style = ctxt->style;
5729 while (style != NULL) {
5730 keyd = style->keys;
5731 while (keyd) {
5732 ctxt->nbKeys++;
5733 keyd = keyd->next;
5735 style = xsltNextImport(style);
5737 return(ctxt->nbKeys);
5741 * xsltCleanupSourceDoc:
5742 * @doc: Document
5744 * Resets source node flags and ids stored in 'psvi' member.
5746 static void
5747 xsltCleanupSourceDoc(xmlDocPtr doc) {
5748 xmlNodePtr cur = (xmlNodePtr) doc;
5749 void **psviPtr;
5751 while (1) {
5752 xsltClearSourceNodeFlags(cur, XSLT_SOURCE_NODE_MASK);
5753 psviPtr = xsltGetPSVIPtr(cur);
5754 if (psviPtr)
5755 *psviPtr = NULL;
5757 if (cur->type == XML_ELEMENT_NODE) {
5758 xmlAttrPtr prop = cur->properties;
5760 while (prop) {
5761 prop->atype &= ~(XSLT_SOURCE_NODE_MASK << 27);
5762 prop->psvi = NULL;
5763 prop = prop->next;
5767 if (cur->children != NULL && cur->type != XML_ENTITY_REF_NODE) {
5768 cur = cur->children;
5769 } else {
5770 if (cur == (xmlNodePtr) doc)
5771 return;
5772 while (cur->next == NULL) {
5773 cur = cur->parent;
5774 if (cur == (xmlNodePtr) doc)
5775 return;
5778 cur = cur->next;
5784 * xsltApplyStylesheetInternal:
5785 * @style: a parsed XSLT stylesheet
5786 * @doc: a parsed XML document
5787 * @params: a NULL terminated array of parameters names/values tuples
5788 * @output: the targetted output
5789 * @profile: profile FILE * output or NULL
5790 * @user: user provided parameter
5792 * Apply the stylesheet to the document
5793 * NOTE: This may lead to a non-wellformed output XML wise !
5795 * Returns the result document or NULL in case of error
5797 static xmlDocPtr
5798 xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc,
5799 const char **params, const char *output,
5800 FILE * profile, xsltTransformContextPtr userCtxt)
5802 xmlDocPtr res = NULL;
5803 xsltTransformContextPtr ctxt = NULL;
5804 xmlNodePtr root, node;
5805 const xmlChar *method;
5806 const xmlChar *doctypePublic;
5807 const xmlChar *doctypeSystem;
5808 const xmlChar *version;
5809 const xmlChar *encoding;
5810 xsltStackElemPtr variables;
5811 xsltStackElemPtr vptr;
5813 xsltInitGlobals();
5815 if ((style == NULL) || (doc == NULL))
5816 return (NULL);
5818 if (style->internalized == 0) {
5819 #ifdef WITH_XSLT_DEBUG
5820 xsltGenericDebug(xsltGenericDebugContext,
5821 "Stylesheet was not fully internalized !\n");
5822 #endif
5824 if (doc->intSubset != NULL) {
5826 * Avoid hitting the DTD when scanning nodes
5827 * but keep it linked as doc->intSubset
5829 xmlNodePtr cur = (xmlNodePtr) doc->intSubset;
5830 if (cur->next != NULL)
5831 cur->next->prev = cur->prev;
5832 if (cur->prev != NULL)
5833 cur->prev->next = cur->next;
5834 if (doc->children == cur)
5835 doc->children = cur->next;
5836 if (doc->last == cur)
5837 doc->last = cur->prev;
5838 cur->prev = cur->next = NULL;
5842 * Check for XPath document order availability
5844 root = xmlDocGetRootElement(doc);
5845 if (root != NULL) {
5846 if (((ptrdiff_t) root->content >= 0) &&
5847 (xslDebugStatus == XSLT_DEBUG_NONE))
5848 xmlXPathOrderDocElems(doc);
5851 if (userCtxt != NULL)
5852 ctxt = userCtxt;
5853 else
5854 ctxt = xsltNewTransformContext(style, doc);
5856 if (ctxt == NULL)
5857 return (NULL);
5859 ctxt->initialContextDoc = doc;
5860 ctxt->initialContextNode = (xmlNodePtr) doc;
5862 if (profile != NULL) {
5863 #ifdef WITH_PROFILER
5864 ctxt->profile = 1;
5865 #else
5866 xsltTransformError(ctxt, NULL, (xmlNodePtr) doc,
5867 "xsltApplyStylesheetInternal: "
5868 "libxslt compiled without profiler\n");
5869 goto error;
5870 #endif
5873 if (output != NULL)
5874 ctxt->outputFile = output;
5875 else
5876 ctxt->outputFile = NULL;
5879 * internalize the modes if needed
5881 if (ctxt->dict != NULL) {
5882 if (ctxt->mode != NULL)
5883 ctxt->mode = xmlDictLookup(ctxt->dict, ctxt->mode, -1);
5884 if (ctxt->modeURI != NULL)
5885 ctxt->modeURI = xmlDictLookup(ctxt->dict, ctxt->modeURI, -1);
5888 XSLT_GET_IMPORT_PTR(method, style, method)
5889 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
5890 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
5891 XSLT_GET_IMPORT_PTR(version, style, version)
5892 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
5894 if ((method != NULL) &&
5895 (!xmlStrEqual(method, (const xmlChar *) "xml")))
5897 if (xmlStrEqual(method, (const xmlChar *) "html")) {
5898 ctxt->type = XSLT_OUTPUT_HTML;
5899 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
5900 res = htmlNewDoc(doctypeSystem, doctypePublic);
5901 } else {
5902 if (version == NULL) {
5903 xmlDtdPtr dtd;
5905 res = htmlNewDoc(NULL, NULL);
5907 * Make sure no DTD node is generated in this case
5909 if (res != NULL) {
5910 dtd = xmlGetIntSubset(res);
5911 if (dtd != NULL) {
5912 xmlUnlinkNode((xmlNodePtr) dtd);
5913 xmlFreeDtd(dtd);
5915 res->intSubset = NULL;
5916 res->extSubset = NULL;
5918 } else {
5920 #ifdef XSLT_GENERATE_HTML_DOCTYPE
5921 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
5922 #endif
5923 res = htmlNewDoc(doctypeSystem, doctypePublic);
5926 if (res == NULL)
5927 goto error;
5928 res->dict = ctxt->dict;
5929 xmlDictReference(res->dict);
5931 #ifdef WITH_XSLT_DEBUG
5932 xsltGenericDebug(xsltGenericDebugContext,
5933 "reusing transformation dict for output\n");
5934 #endif
5935 } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
5936 xsltTransformError(ctxt, NULL, (xmlNodePtr) doc,
5937 "xsltApplyStylesheetInternal: unsupported method xhtml, using html\n");
5938 ctxt->type = XSLT_OUTPUT_HTML;
5939 res = htmlNewDoc(doctypeSystem, doctypePublic);
5940 if (res == NULL)
5941 goto error;
5942 res->dict = ctxt->dict;
5943 xmlDictReference(res->dict);
5945 #ifdef WITH_XSLT_DEBUG
5946 xsltGenericDebug(xsltGenericDebugContext,
5947 "reusing transformation dict for output\n");
5948 #endif
5949 } else if (xmlStrEqual(method, (const xmlChar *) "text")) {
5950 ctxt->type = XSLT_OUTPUT_TEXT;
5951 res = xmlNewDoc(style->version);
5952 if (res == NULL)
5953 goto error;
5954 res->dict = ctxt->dict;
5955 xmlDictReference(res->dict);
5957 #ifdef WITH_XSLT_DEBUG
5958 xsltGenericDebug(xsltGenericDebugContext,
5959 "reusing transformation dict for output\n");
5960 #endif
5961 } else {
5962 xsltTransformError(ctxt, NULL, (xmlNodePtr) doc,
5963 "xsltApplyStylesheetInternal: unsupported method (%s)\n",
5964 method);
5965 goto error;
5967 } else {
5968 ctxt->type = XSLT_OUTPUT_XML;
5969 res = xmlNewDoc(style->version);
5970 if (res == NULL)
5971 goto error;
5972 res->dict = ctxt->dict;
5973 xmlDictReference(ctxt->dict);
5974 #ifdef WITH_XSLT_DEBUG
5975 xsltGenericDebug(xsltGenericDebugContext,
5976 "reusing transformation dict for output\n");
5977 #endif
5979 res->charset = XML_CHAR_ENCODING_UTF8;
5980 if (encoding != NULL)
5981 res->encoding = xmlStrdup(encoding);
5982 variables = style->variables;
5984 ctxt->node = (xmlNodePtr) doc;
5985 ctxt->output = res;
5987 ctxt->xpathCtxt->contextSize = 1;
5988 ctxt->xpathCtxt->proximityPosition = 1;
5989 ctxt->xpathCtxt->node = NULL; /* TODO: Set the context node here? */
5992 * Start the evaluation, evaluate the params, the stylesheets globals
5993 * and start by processing the top node.
5995 if (xsltNeedElemSpaceHandling(ctxt))
5996 xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc));
5998 * Evaluate global params and user-provided params.
6000 if (ctxt->globalVars == NULL)
6001 ctxt->globalVars = xmlHashCreate(20);
6002 if (params != NULL) {
6003 xsltEvalUserParams(ctxt, params);
6006 /* need to be called before evaluating global variables */
6007 xsltCountKeys(ctxt);
6009 xsltEvalGlobalVariables(ctxt);
6011 /* Clean up any unused RVTs. */
6012 xsltReleaseLocalRVTs(ctxt, NULL);
6014 ctxt->insert = (xmlNodePtr) res;
6015 ctxt->varsBase = ctxt->varsNr - 1;
6018 * Start processing the source tree -----------------------------------
6020 xsltProcessOneNode(ctxt, ctxt->node, NULL);
6022 * Remove all remaining vars from the stack.
6024 xsltLocalVariablePop(ctxt, 0, -2);
6025 xsltShutdownCtxtExts(ctxt);
6027 xsltCleanupTemplates(style); /* TODO: <- style should be read only */
6030 * Now cleanup our variables so stylesheet can be re-used
6032 * TODO: this is not needed anymore global variables are copied
6033 * and not evaluated directly anymore, keep this as a check
6035 if (style->variables != variables) {
6036 vptr = style->variables;
6037 while (vptr->next != variables)
6038 vptr = vptr->next;
6039 vptr->next = NULL;
6040 xsltFreeStackElemList(style->variables);
6041 style->variables = variables;
6043 vptr = style->variables;
6044 while (vptr != NULL) {
6045 if (vptr->computed) {
6046 if (vptr->value != NULL) {
6047 xmlXPathFreeObject(vptr->value);
6048 vptr->value = NULL;
6049 vptr->computed = 0;
6052 vptr = vptr->next;
6054 #if 0
6056 * code disabled by wmb; awaiting kb's review
6057 * problem is that global variable(s) may contain xpath objects
6058 * from doc associated with RVT, so can't be freed at this point.
6059 * xsltFreeTransformContext includes a call to xsltFreeRVTs, so
6060 * I assume this shouldn't be required at this point.
6063 * Free all remaining tree fragments.
6065 xsltFreeRVTs(ctxt);
6066 #endif
6068 * Do some post processing work depending on the generated output
6070 root = xmlDocGetRootElement(res);
6071 if (root != NULL) {
6072 const xmlChar *doctype = NULL;
6074 if ((root->ns != NULL) && (root->ns->prefix != NULL))
6075 doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name);
6076 if (doctype == NULL)
6077 doctype = root->name;
6080 * Apply the default selection of the method
6082 if ((method == NULL) &&
6083 (root->ns == NULL) &&
6084 (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) {
6085 xmlNodePtr tmp;
6087 tmp = res->children;
6088 while ((tmp != NULL) && (tmp != root)) {
6089 if (tmp->type == XML_ELEMENT_NODE)
6090 break;
6091 if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp)))
6092 break;
6093 tmp = tmp->next;
6095 if (tmp == root) {
6096 ctxt->type = XSLT_OUTPUT_HTML;
6098 * REVISIT TODO: XML_HTML_DOCUMENT_NODE is set after the
6099 * transformation on the doc, but functions like
6101 res->type = XML_HTML_DOCUMENT_NODE;
6102 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
6103 res->intSubset = xmlCreateIntSubset(res, doctype,
6104 doctypePublic,
6105 doctypeSystem);
6106 #ifdef XSLT_GENERATE_HTML_DOCTYPE
6107 } else if (version != NULL) {
6108 xsltGetHTMLIDs(version, &doctypePublic,
6109 &doctypeSystem);
6110 if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
6111 res->intSubset =
6112 xmlCreateIntSubset(res, doctype,
6113 doctypePublic,
6114 doctypeSystem);
6115 #endif
6120 if (ctxt->type == XSLT_OUTPUT_XML) {
6121 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
6122 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
6123 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
6124 xmlNodePtr last;
6125 /* Need a small "hack" here to assure DTD comes before
6126 possible comment nodes */
6127 node = res->children;
6128 last = res->last;
6129 res->children = NULL;
6130 res->last = NULL;
6131 res->intSubset = xmlCreateIntSubset(res, doctype,
6132 doctypePublic,
6133 doctypeSystem);
6134 if (res->children != NULL) {
6135 res->children->next = node;
6136 node->prev = res->children;
6137 res->last = last;
6138 } else {
6139 res->children = node;
6140 res->last = last;
6145 xmlXPathFreeNodeSet(ctxt->nodeList);
6147 #ifdef WITH_PROFILER
6148 if (profile != NULL) {
6149 xsltSaveProfiling(ctxt, profile);
6151 #endif
6154 * Be pedantic.
6156 if ((ctxt != NULL) && (ctxt->state != XSLT_STATE_OK)) {
6157 xmlFreeDoc(res);
6158 res = NULL;
6160 if ((res != NULL) && (ctxt != NULL) && (output != NULL)) {
6161 int ret;
6163 ret = xsltCheckWrite(ctxt->sec, ctxt, (const xmlChar *) output);
6164 if (ret == 0) {
6165 xsltTransformError(ctxt, NULL, NULL,
6166 "xsltApplyStylesheet: forbidden to save to %s\n",
6167 output);
6168 } else if (ret < 0) {
6169 xsltTransformError(ctxt, NULL, NULL,
6170 "xsltApplyStylesheet: saving to %s may not be possible\n",
6171 output);
6175 #ifdef XSLT_DEBUG_PROFILE_CACHE
6176 printf("# Cache:\n");
6177 printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs);
6178 printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars);
6179 #endif
6181 if (ctxt->sourceDocDirty)
6182 xsltCleanupSourceDoc(doc);
6184 if ((ctxt != NULL) && (userCtxt == NULL))
6185 xsltFreeTransformContext(ctxt);
6187 return (res);
6189 error:
6190 if (res != NULL)
6191 xmlFreeDoc(res);
6193 #ifdef XSLT_DEBUG_PROFILE_CACHE
6194 printf("# Cache:\n");
6195 printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs);
6196 printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars);
6197 #endif
6199 if ((ctxt != NULL) && (userCtxt == NULL))
6200 xsltFreeTransformContext(ctxt);
6201 return (NULL);
6205 * xsltApplyStylesheet:
6206 * @style: a parsed XSLT stylesheet
6207 * @doc: a parsed XML document
6208 * @params: a NULL terminated arry of parameters names/values tuples
6210 * Apply the stylesheet to the document
6211 * NOTE: This may lead to a non-wellformed output XML wise !
6213 * Returns the result document or NULL in case of error
6215 xmlDocPtr
6216 xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
6217 const char **params)
6219 return (xsltApplyStylesheetInternal(style, doc, params, NULL, NULL, NULL));
6223 * xsltProfileStylesheet:
6224 * @style: a parsed XSLT stylesheet
6225 * @doc: a parsed XML document
6226 * @params: a NULL terminated arry of parameters names/values tuples
6227 * @output: a FILE * for the profiling output
6229 * Apply the stylesheet to the document and dump the profiling to
6230 * the given output.
6232 * Returns the result document or NULL in case of error
6234 xmlDocPtr
6235 xsltProfileStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
6236 const char **params, FILE * output)
6238 xmlDocPtr res;
6240 res = xsltApplyStylesheetInternal(style, doc, params, NULL, output, NULL);
6241 return (res);
6245 * xsltApplyStylesheetUser:
6246 * @style: a parsed XSLT stylesheet
6247 * @doc: a parsed XML document
6248 * @params: a NULL terminated array of parameters names/values tuples
6249 * @output: the targetted output
6250 * @profile: profile FILE * output or NULL
6251 * @userCtxt: user provided transform context
6253 * Apply the stylesheet to the document and allow the user to provide
6254 * its own transformation context.
6256 * Returns the result document or NULL in case of error
6258 xmlDocPtr
6259 xsltApplyStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc,
6260 const char **params, const char *output,
6261 FILE * profile, xsltTransformContextPtr userCtxt)
6263 xmlDocPtr res;
6265 res = xsltApplyStylesheetInternal(style, doc, params, output,
6266 profile, userCtxt);
6267 return (res);
6271 * xsltRunStylesheetUser:
6272 * @style: a parsed XSLT stylesheet
6273 * @doc: a parsed XML document
6274 * @params: a NULL terminated array of parameters names/values tuples
6275 * @output: the URL/filename ot the generated resource if available
6276 * @SAX: a SAX handler for progressive callback output (not implemented yet)
6277 * @IObuf: an output buffer for progressive output (not implemented yet)
6278 * @profile: profile FILE * output or NULL
6279 * @userCtxt: user provided transform context
6281 * Apply the stylesheet to the document and generate the output according
6282 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf.
6284 * NOTE: This may lead to a non-wellformed output XML wise !
6285 * NOTE: This may also result in multiple files being generated
6286 * NOTE: using IObuf, the result encoding used will be the one used for
6287 * creating the output buffer, use the following macro to read it
6288 * from the stylesheet
6289 * XSLT_GET_IMPORT_PTR(encoding, style, encoding)
6290 * NOTE: using SAX, any encoding specified in the stylesheet will be lost
6291 * since the interface uses only UTF8
6293 * Returns the number of by written to the main resource or -1 in case of
6294 * error.
6297 xsltRunStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc,
6298 const char **params, const char *output,
6299 xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf,
6300 FILE * profile, xsltTransformContextPtr userCtxt)
6302 xmlDocPtr tmp;
6303 int ret;
6305 if ((output == NULL) && (SAX == NULL) && (IObuf == NULL))
6306 return (-1);
6307 if ((SAX != NULL) && (IObuf != NULL))
6308 return (-1);
6310 /* unsupported yet */
6311 if (SAX != NULL) {
6312 XSLT_TODO /* xsltRunStylesheet xmlSAXHandlerPtr SAX */
6313 return (-1);
6316 tmp = xsltApplyStylesheetInternal(style, doc, params, output, profile,
6317 userCtxt);
6318 if (tmp == NULL) {
6319 xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
6320 "xsltRunStylesheet : run failed\n");
6321 return (-1);
6323 if (IObuf != NULL) {
6324 /* TODO: incomplete, IObuf output not progressive */
6325 ret = xsltSaveResultTo(IObuf, tmp, style);
6326 } else {
6327 ret = xsltSaveResultToFilename(output, tmp, style, 0);
6329 xmlFreeDoc(tmp);
6330 return (ret);
6334 * xsltRunStylesheet:
6335 * @style: a parsed XSLT stylesheet
6336 * @doc: a parsed XML document
6337 * @params: a NULL terminated array of parameters names/values tuples
6338 * @output: the URL/filename ot the generated resource if available
6339 * @SAX: a SAX handler for progressive callback output (not implemented yet)
6340 * @IObuf: an output buffer for progressive output (not implemented yet)
6342 * Apply the stylesheet to the document and generate the output according
6343 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf.
6345 * NOTE: This may lead to a non-wellformed output XML wise !
6346 * NOTE: This may also result in multiple files being generated
6347 * NOTE: using IObuf, the result encoding used will be the one used for
6348 * creating the output buffer, use the following macro to read it
6349 * from the stylesheet
6350 * XSLT_GET_IMPORT_PTR(encoding, style, encoding)
6351 * NOTE: using SAX, any encoding specified in the stylesheet will be lost
6352 * since the interface uses only UTF8
6354 * Returns the number of bytes written to the main resource or -1 in case of
6355 * error.
6358 xsltRunStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
6359 const char **params, const char *output,
6360 xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf)
6362 return(xsltRunStylesheetUser(style, doc, params, output, SAX, IObuf,
6363 NULL, NULL));
6366 static void
6367 xsltMessageWrapper(xsltTransformContextPtr ctxt, xmlNodePtr node,
6368 xmlNodePtr inst, xsltElemPreCompPtr comp ATTRIBUTE_UNUSED) {
6369 xsltMessage(ctxt, node, inst);
6373 * xsltRegisterAllElement:
6374 * @ctxt: the XPath context
6376 * Registers all default XSLT elements in this context
6378 void
6379 xsltRegisterAllElement(xsltTransformContextPtr ctxt)
6381 xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-templates",
6382 XSLT_NAMESPACE,
6383 xsltApplyTemplates);
6384 xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-imports",
6385 XSLT_NAMESPACE,
6386 xsltApplyImports);
6387 xsltRegisterExtElement(ctxt, (const xmlChar *) "call-template",
6388 XSLT_NAMESPACE,
6389 xsltCallTemplate);
6390 xsltRegisterExtElement(ctxt, (const xmlChar *) "element",
6391 XSLT_NAMESPACE,
6392 xsltElement);
6393 xsltRegisterExtElement(ctxt, (const xmlChar *) "attribute",
6394 XSLT_NAMESPACE,
6395 xsltAttribute);
6396 xsltRegisterExtElement(ctxt, (const xmlChar *) "text",
6397 XSLT_NAMESPACE,
6398 xsltText);
6399 xsltRegisterExtElement(ctxt, (const xmlChar *) "processing-instruction",
6400 XSLT_NAMESPACE,
6401 xsltProcessingInstruction);
6402 xsltRegisterExtElement(ctxt, (const xmlChar *) "comment",
6403 XSLT_NAMESPACE,
6404 xsltComment);
6405 xsltRegisterExtElement(ctxt, (const xmlChar *) "copy",
6406 XSLT_NAMESPACE,
6407 xsltCopy);
6408 xsltRegisterExtElement(ctxt, (const xmlChar *) "value-of",
6409 XSLT_NAMESPACE,
6410 xsltValueOf);
6411 xsltRegisterExtElement(ctxt, (const xmlChar *) "number",
6412 XSLT_NAMESPACE,
6413 xsltNumber);
6414 xsltRegisterExtElement(ctxt, (const xmlChar *) "for-each",
6415 XSLT_NAMESPACE,
6416 xsltForEach);
6417 xsltRegisterExtElement(ctxt, (const xmlChar *) "if",
6418 XSLT_NAMESPACE,
6419 xsltIf);
6420 xsltRegisterExtElement(ctxt, (const xmlChar *) "choose",
6421 XSLT_NAMESPACE,
6422 xsltChoose);
6423 xsltRegisterExtElement(ctxt, (const xmlChar *) "sort",
6424 XSLT_NAMESPACE,
6425 xsltSort);
6426 xsltRegisterExtElement(ctxt, (const xmlChar *) "copy-of",
6427 XSLT_NAMESPACE,
6428 xsltCopyOf);
6429 xsltRegisterExtElement(ctxt, (const xmlChar *) "message",
6430 XSLT_NAMESPACE,
6431 xsltMessageWrapper);
6434 * Those don't have callable entry points but are registered anyway
6436 xsltRegisterExtElement(ctxt, (const xmlChar *) "variable",
6437 XSLT_NAMESPACE,
6438 xsltDebug);
6439 xsltRegisterExtElement(ctxt, (const xmlChar *) "param",
6440 XSLT_NAMESPACE,
6441 xsltDebug);
6442 xsltRegisterExtElement(ctxt, (const xmlChar *) "with-param",
6443 XSLT_NAMESPACE,
6444 xsltDebug);
6445 xsltRegisterExtElement(ctxt, (const xmlChar *) "decimal-format",
6446 XSLT_NAMESPACE,
6447 xsltDebug);
6448 xsltRegisterExtElement(ctxt, (const xmlChar *) "when",
6449 XSLT_NAMESPACE,
6450 xsltDebug);
6451 xsltRegisterExtElement(ctxt, (const xmlChar *) "otherwise",
6452 XSLT_NAMESPACE,
6453 xsltDebug);
6454 xsltRegisterExtElement(ctxt, (const xmlChar *) "fallback",
6455 XSLT_NAMESPACE,
6456 xsltDebug);