- Revert 44301
[reactos.git] / dll / 3rdparty / libxslt / transform.c
1 /*
2 * transform.c: Implementation of the XSL Transformation 1.0 engine
3 * transform part, i.e. applying a Stylesheet to a document
4 *
5 * References:
6 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 *
8 * Michael Kay "XSLT Programmer's Reference" pp 637-643
9 * Writing Multiple Output Files
10 *
11 * XSLT-1.1 Working Draft
12 * http://www.w3.org/TR/xslt11#multiple-output
13 *
14 * See Copyright for the status of this software.
15 *
16 * daniel@veillard.com
17 */
18
19 #define IN_LIBXSLT
20 #include "libxslt.h"
21
22 #include <string.h>
23
24 #include <libxml/xmlmemory.h>
25 #include <libxml/parser.h>
26 #include <libxml/tree.h>
27 #include <libxml/valid.h>
28 #include <libxml/hash.h>
29 #include <libxml/encoding.h>
30 #include <libxml/xmlerror.h>
31 #include <libxml/xpath.h>
32 #include <libxml/parserInternals.h>
33 #include <libxml/xpathInternals.h>
34 #include <libxml/HTMLtree.h>
35 #include <libxml/debugXML.h>
36 #include <libxml/uri.h>
37 #include "xslt.h"
38 #include "xsltInternals.h"
39 #include "xsltutils.h"
40 #include "pattern.h"
41 #include "transform.h"
42 #include "variables.h"
43 #include "numbersInternals.h"
44 #include "namespaces.h"
45 #include "attributes.h"
46 #include "templates.h"
47 #include "imports.h"
48 #include "keys.h"
49 #include "documents.h"
50 #include "extensions.h"
51 #include "extra.h"
52 #include "preproc.h"
53 #include "security.h"
54
55 #ifdef WITH_XSLT_DEBUG
56 #define WITH_XSLT_DEBUG_EXTRA
57 #define WITH_XSLT_DEBUG_PROCESS
58 #endif
59
60 #define XSLT_GENERATE_HTML_DOCTYPE
61 #ifdef XSLT_GENERATE_HTML_DOCTYPE
62 static int xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID,
63 const xmlChar **systemID);
64 #endif
65
66 int xsltMaxDepth = 3000;
67
68 /*
69 * Useful macros
70 */
71
72 #ifndef FALSE
73 # define FALSE (0 == 1)
74 # define TRUE (!FALSE)
75 #endif
76
77 #define IS_BLANK_NODE(n) \
78 (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
79
80
81 /*
82 * Forward declarations
83 */
84
85 static xmlNsPtr
86 xsltCopyNamespaceListInternal(xmlNodePtr node, xmlNsPtr cur);
87
88 static xmlNodePtr
89 xsltCopyTreeInternal(xsltTransformContextPtr ctxt,
90 xmlNodePtr invocNode,
91 xmlNodePtr node,
92 xmlNodePtr insert, int isLRE, int topElemVisited);
93
94 static void
95 xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
96 xmlNodePtr contextNode, xmlNodePtr list,
97 xsltTemplatePtr templ);
98
99 static void
100 xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt,
101 xmlNodePtr contextNode,
102 xmlNodePtr list,
103 xsltTemplatePtr templ,
104 xsltStackElemPtr withParams);
105
106 /**
107 * templPush:
108 * @ctxt: the transformation context
109 * @value: the template to push on the stack
110 *
111 * Push a template on the stack
112 *
113 * Returns the new index in the stack or 0 in case of error
114 */
115 static int
116 templPush(xsltTransformContextPtr ctxt, xsltTemplatePtr value)
117 {
118 if (ctxt->templMax == 0) {
119 ctxt->templMax = 4;
120 ctxt->templTab =
121 (xsltTemplatePtr *) xmlMalloc(ctxt->templMax *
122 sizeof(ctxt->templTab[0]));
123 if (ctxt->templTab == NULL) {
124 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
125 return (0);
126 }
127 }
128 if (ctxt->templNr >= ctxt->templMax) {
129 ctxt->templMax *= 2;
130 ctxt->templTab =
131 (xsltTemplatePtr *) xmlRealloc(ctxt->templTab,
132 ctxt->templMax *
133 sizeof(ctxt->templTab[0]));
134 if (ctxt->templTab == NULL) {
135 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
136 return (0);
137 }
138 }
139 ctxt->templTab[ctxt->templNr] = value;
140 ctxt->templ = value;
141 return (ctxt->templNr++);
142 }
143 /**
144 * templPop:
145 * @ctxt: the transformation context
146 *
147 * Pop a template value from the stack
148 *
149 * Returns the stored template value
150 */
151 static xsltTemplatePtr
152 templPop(xsltTransformContextPtr ctxt)
153 {
154 xsltTemplatePtr ret;
155
156 if (ctxt->templNr <= 0)
157 return (0);
158 ctxt->templNr--;
159 if (ctxt->templNr > 0)
160 ctxt->templ = ctxt->templTab[ctxt->templNr - 1];
161 else
162 ctxt->templ = (xsltTemplatePtr) 0;
163 ret = ctxt->templTab[ctxt->templNr];
164 ctxt->templTab[ctxt->templNr] = 0;
165 return (ret);
166 }
167
168 /**
169 * xsltLocalVariablePop:
170 * @ctxt: the transformation context
171 * @limitNr: number of variables which should remain
172 * @level: the depth in the xsl:template's tree
173 *
174 * Pops all variable values at the given @depth from the stack.
175 *
176 * Returns the stored variable value
177 * **NOTE:**
178 * This is an internal routine and should not be called by users!
179 */
180 void
181 xsltLocalVariablePop(xsltTransformContextPtr ctxt, int limitNr, int level)
182 {
183 xsltStackElemPtr variable;
184
185 if (ctxt->varsNr <= 0)
186 return;
187
188 do {
189 if (ctxt->varsNr <= limitNr)
190 break;
191 variable = ctxt->varsTab[ctxt->varsNr - 1];
192 if (variable->level <= level)
193 break;
194 if (variable->level >= 0)
195 xsltFreeStackElemList(variable);
196 ctxt->varsNr--;
197 } while (ctxt->varsNr != 0);
198 if (ctxt->varsNr > 0)
199 ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1];
200 else
201 ctxt->vars = NULL;
202 }
203
204 /**
205 * xsltTemplateParamsCleanup:
206 *
207 * Removes xsl:param and xsl:with-param items from the
208 * variable-stack. Only xsl:with-param items are not freed.
209 */
210 static void
211 xsltTemplateParamsCleanup(xsltTransformContextPtr ctxt)
212 {
213 xsltStackElemPtr param;
214
215 for (; ctxt->varsNr > ctxt->varsBase; ctxt->varsNr--) {
216 param = ctxt->varsTab[ctxt->varsNr -1];
217 /*
218 * Free xsl:param items.
219 * xsl:with-param items will have a level of -1 or -2.
220 */
221 if (param->level >= 0) {
222 xsltFreeStackElemList(param);
223 }
224 }
225 if (ctxt->varsNr > 0)
226 ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1];
227 else
228 ctxt->vars = NULL;
229 }
230
231 /**
232 * profPush:
233 * @ctxt: the transformation context
234 * @value: the profiling value to push on the stack
235 *
236 * Push a profiling value on the stack
237 *
238 * Returns the new index in the stack or 0 in case of error
239 */
240 static int
241 profPush(xsltTransformContextPtr ctxt, long value)
242 {
243 if (ctxt->profMax == 0) {
244 ctxt->profMax = 4;
245 ctxt->profTab =
246 (long *) xmlMalloc(ctxt->profMax * sizeof(ctxt->profTab[0]));
247 if (ctxt->profTab == NULL) {
248 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
249 return (0);
250 }
251 }
252 if (ctxt->profNr >= ctxt->profMax) {
253 ctxt->profMax *= 2;
254 ctxt->profTab =
255 (long *) xmlRealloc(ctxt->profTab,
256 ctxt->profMax * sizeof(ctxt->profTab[0]));
257 if (ctxt->profTab == NULL) {
258 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
259 return (0);
260 }
261 }
262 ctxt->profTab[ctxt->profNr] = value;
263 ctxt->prof = value;
264 return (ctxt->profNr++);
265 }
266 /**
267 * profPop:
268 * @ctxt: the transformation context
269 *
270 * Pop a profiling value from the stack
271 *
272 * Returns the stored profiling value
273 */
274 static long
275 profPop(xsltTransformContextPtr ctxt)
276 {
277 long ret;
278
279 if (ctxt->profNr <= 0)
280 return (0);
281 ctxt->profNr--;
282 if (ctxt->profNr > 0)
283 ctxt->prof = ctxt->profTab[ctxt->profNr - 1];
284 else
285 ctxt->prof = (long) 0;
286 ret = ctxt->profTab[ctxt->profNr];
287 ctxt->profTab[ctxt->profNr] = 0;
288 return (ret);
289 }
290
291 /************************************************************************
292 * *
293 * XInclude default settings *
294 * *
295 ************************************************************************/
296
297 static int xsltDoXIncludeDefault = 0;
298
299 /**
300 * xsltSetXIncludeDefault:
301 * @xinclude: whether to do XInclude processing
302 *
303 * Set whether XInclude should be processed on document being loaded by default
304 */
305 void
306 xsltSetXIncludeDefault(int xinclude) {
307 xsltDoXIncludeDefault = (xinclude != 0);
308 }
309
310 /**
311 * xsltGetXIncludeDefault:
312 *
313 * Provides the default state for XInclude processing
314 *
315 * Returns 0 if there is no processing 1 otherwise
316 */
317 int
318 xsltGetXIncludeDefault(void) {
319 return(xsltDoXIncludeDefault);
320 }
321
322 unsigned long xsltDefaultTrace = (unsigned long) XSLT_TRACE_ALL;
323
324 /**
325 * xsltDebugSetDefaultTrace:
326 * @val: tracing level mask
327 *
328 * Set the default debug tracing level mask
329 */
330 void xsltDebugSetDefaultTrace(xsltDebugTraceCodes val) {
331 xsltDefaultTrace = val;
332 }
333
334 /**
335 * xsltDebugGetDefaultTrace:
336 *
337 * Get the current default debug tracing level mask
338 *
339 * Returns the current default debug tracing level mask
340 */
341 xsltDebugTraceCodes xsltDebugGetDefaultTrace() {
342 return xsltDefaultTrace;
343 }
344
345 /************************************************************************
346 * *
347 * Handling of Transformation Contexts *
348 * *
349 ************************************************************************/
350
351 static xsltTransformCachePtr
352 xsltTransformCacheCreate(void)
353 {
354 xsltTransformCachePtr ret;
355
356 ret = (xsltTransformCachePtr) xmlMalloc(sizeof(xsltTransformCache));
357 if (ret == NULL) {
358 xsltTransformError(NULL, NULL, NULL,
359 "xsltTransformCacheCreate : malloc failed\n");
360 return(NULL);
361 }
362 memset(ret, 0, sizeof(xsltTransformCache));
363 return(ret);
364 }
365
366 static void
367 xsltTransformCacheFree(xsltTransformCachePtr cache)
368 {
369 if (cache == NULL)
370 return;
371 /*
372 * Free tree fragments.
373 */
374 if (cache->RVT) {
375 xmlDocPtr tmp, cur = cache->RVT;
376 while (cur) {
377 tmp = cur;
378 cur = (xmlDocPtr) cur->next;
379 if (tmp->_private != NULL) {
380 /*
381 * Tree the document info.
382 */
383 xsltFreeDocumentKeys((xsltDocumentPtr) tmp->_private);
384 xmlFree(tmp->_private);
385 }
386 xmlFreeDoc(tmp);
387 }
388 }
389 /*
390 * Free vars/params.
391 */
392 if (cache->stackItems) {
393 xsltStackElemPtr tmp, cur = cache->stackItems;
394 while (cur) {
395 tmp = cur;
396 cur = cur->next;
397 /*
398 * REVISIT TODO: Should be call a destruction-function
399 * instead?
400 */
401 xmlFree(tmp);
402 }
403 }
404 xmlFree(cache);
405 }
406
407 /**
408 * xsltNewTransformContext:
409 * @style: a parsed XSLT stylesheet
410 * @doc: the input document
411 *
412 * Create a new XSLT TransformContext
413 *
414 * Returns the newly allocated xsltTransformContextPtr or NULL in case of error
415 */
416 xsltTransformContextPtr
417 xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) {
418 xsltTransformContextPtr cur;
419 xsltDocumentPtr docu;
420 int i;
421
422 xsltInitGlobals();
423
424 cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext));
425 if (cur == NULL) {
426 xsltTransformError(NULL, NULL, (xmlNodePtr)doc,
427 "xsltNewTransformContext : malloc failed\n");
428 return(NULL);
429 }
430 memset(cur, 0, sizeof(xsltTransformContext));
431
432 cur->cache = xsltTransformCacheCreate();
433 if (cur->cache == NULL)
434 goto internal_err;
435 /*
436 * setup of the dictionary must be done early as some of the
437 * processing later like key handling may need it.
438 */
439 cur->dict = xmlDictCreateSub(style->dict);
440 cur->internalized = ((style->internalized) && (cur->dict != NULL));
441 #ifdef WITH_XSLT_DEBUG
442 xsltGenericDebug(xsltGenericDebugContext,
443 "Creating sub-dictionary from stylesheet for transformation\n");
444 #endif
445
446 /*
447 * initialize the template stack
448 */
449 cur->templTab = (xsltTemplatePtr *)
450 xmlMalloc(10 * sizeof(xsltTemplatePtr));
451 if (cur->templTab == NULL) {
452 xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
453 "xsltNewTransformContext: out of memory\n");
454 goto internal_err;
455 }
456 cur->templNr = 0;
457 cur->templMax = 5;
458 cur->templ = NULL;
459
460 /*
461 * initialize the variables stack
462 */
463 cur->varsTab = (xsltStackElemPtr *)
464 xmlMalloc(10 * sizeof(xsltStackElemPtr));
465 if (cur->varsTab == NULL) {
466 xmlGenericError(xmlGenericErrorContext,
467 "xsltNewTransformContext: out of memory\n");
468 goto internal_err;
469 }
470 cur->varsNr = 0;
471 cur->varsMax = 10;
472 cur->vars = NULL;
473 cur->varsBase = 0;
474
475 /*
476 * the profiling stack is not initialized by default
477 */
478 cur->profTab = NULL;
479 cur->profNr = 0;
480 cur->profMax = 0;
481 cur->prof = 0;
482
483 cur->style = style;
484 xmlXPathInit();
485 cur->xpathCtxt = xmlXPathNewContext(doc);
486 if (cur->xpathCtxt == NULL) {
487 xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
488 "xsltNewTransformContext : xmlXPathNewContext failed\n");
489 goto internal_err;
490 }
491 /*
492 * Create an XPath cache.
493 */
494 if (xmlXPathContextSetCache(cur->xpathCtxt, 1, -1, 0) == -1)
495 goto internal_err;
496 /*
497 * Initialize the extras array
498 */
499 if (style->extrasNr != 0) {
500 cur->extrasMax = style->extrasNr + 20;
501 cur->extras = (xsltRuntimeExtraPtr)
502 xmlMalloc(cur->extrasMax * sizeof(xsltRuntimeExtra));
503 if (cur->extras == NULL) {
504 xmlGenericError(xmlGenericErrorContext,
505 "xsltNewTransformContext: out of memory\n");
506 goto internal_err;
507 }
508 cur->extrasNr = style->extrasNr;
509 for (i = 0;i < cur->extrasMax;i++) {
510 cur->extras[i].info = NULL;
511 cur->extras[i].deallocate = NULL;
512 cur->extras[i].val.ptr = NULL;
513 }
514 } else {
515 cur->extras = NULL;
516 cur->extrasNr = 0;
517 cur->extrasMax = 0;
518 }
519
520 XSLT_REGISTER_VARIABLE_LOOKUP(cur);
521 XSLT_REGISTER_FUNCTION_LOOKUP(cur);
522 cur->xpathCtxt->nsHash = style->nsHash;
523 /*
524 * Initialize the registered external modules
525 */
526 xsltInitCtxtExts(cur);
527 /*
528 * Setup document element ordering for later efficiencies
529 * (bug 133289)
530 */
531 if (xslDebugStatus == XSLT_DEBUG_NONE)
532 xmlXPathOrderDocElems(doc);
533 /*
534 * Must set parserOptions before calling xsltNewDocument
535 * (bug 164530)
536 */
537 cur->parserOptions = XSLT_PARSE_OPTIONS;
538 docu = xsltNewDocument(cur, doc);
539 if (docu == NULL) {
540 xsltTransformError(cur, NULL, (xmlNodePtr)doc,
541 "xsltNewTransformContext : xsltNewDocument failed\n");
542 goto internal_err;
543 }
544 docu->main = 1;
545 cur->document = docu;
546 cur->inst = NULL;
547 cur->outputFile = NULL;
548 cur->sec = xsltGetDefaultSecurityPrefs();
549 cur->debugStatus = xslDebugStatus;
550 cur->traceCode = (unsigned long*) &xsltDefaultTrace;
551 cur->xinclude = xsltGetXIncludeDefault();
552 cur->keyInitLevel = 0;
553
554 return(cur);
555
556 internal_err:
557 if (cur != NULL)
558 xsltFreeTransformContext(cur);
559 return(NULL);
560 }
561
562 /**
563 * xsltFreeTransformContext:
564 * @ctxt: an XSLT parser context
565 *
566 * Free up the memory allocated by @ctxt
567 */
568 void
569 xsltFreeTransformContext(xsltTransformContextPtr ctxt) {
570 if (ctxt == NULL)
571 return;
572
573 /*
574 * Shutdown the extension modules associated to the stylesheet
575 * used if needed.
576 */
577 xsltShutdownCtxtExts(ctxt);
578
579 if (ctxt->xpathCtxt != NULL) {
580 ctxt->xpathCtxt->nsHash = NULL;
581 xmlXPathFreeContext(ctxt->xpathCtxt);
582 }
583 if (ctxt->templTab != NULL)
584 xmlFree(ctxt->templTab);
585 if (ctxt->varsTab != NULL)
586 xmlFree(ctxt->varsTab);
587 if (ctxt->profTab != NULL)
588 xmlFree(ctxt->profTab);
589 if ((ctxt->extrasNr > 0) && (ctxt->extras != NULL)) {
590 int i;
591
592 for (i = 0;i < ctxt->extrasNr;i++) {
593 if ((ctxt->extras[i].deallocate != NULL) &&
594 (ctxt->extras[i].info != NULL))
595 ctxt->extras[i].deallocate(ctxt->extras[i].info);
596 }
597 xmlFree(ctxt->extras);
598 }
599 xsltFreeGlobalVariables(ctxt);
600 xsltFreeDocuments(ctxt);
601 xsltFreeCtxtExts(ctxt);
602 xsltFreeRVTs(ctxt);
603 xsltTransformCacheFree(ctxt->cache);
604 xmlDictFree(ctxt->dict);
605 #ifdef WITH_XSLT_DEBUG
606 xsltGenericDebug(xsltGenericDebugContext,
607 "freeing transformation dictionary\n");
608 #endif
609 memset(ctxt, -1, sizeof(xsltTransformContext));
610 xmlFree(ctxt);
611 }
612
613 /************************************************************************
614 * *
615 * Copy of Nodes in an XSLT fashion *
616 * *
617 ************************************************************************/
618
619 xmlNodePtr xsltCopyTree(xsltTransformContextPtr ctxt,
620 xmlNodePtr node, xmlNodePtr insert, int literal);
621
622 /**
623 * xsltAddChild:
624 * @parent: the parent node
625 * @cur: the child node
626 *
627 * Wrapper version of xmlAddChild with a more consistent behaviour on
628 * error. One expect the use to be child = xsltAddChild(parent, child);
629 * and the routine will take care of not leaking on errors or node merge
630 *
631 * Returns the child is successfully attached or NULL if merged or freed
632 */
633 static xmlNodePtr
634 xsltAddChild(xmlNodePtr parent, xmlNodePtr cur) {
635 xmlNodePtr ret;
636
637 if ((cur == NULL) || (parent == NULL))
638 return(NULL);
639 if (parent == NULL) {
640 xmlFreeNode(cur);
641 return(NULL);
642 }
643 ret = xmlAddChild(parent, cur);
644
645 return(ret);
646 }
647
648 /**
649 * xsltAddTextString:
650 * @ctxt: a XSLT process context
651 * @target: the text node where the text will be attached
652 * @string: the text string
653 * @len: the string length in byte
654 *
655 * Extend the current text node with the new string, it handles coalescing
656 *
657 * Returns: the text node
658 */
659 static xmlNodePtr
660 xsltAddTextString(xsltTransformContextPtr ctxt, xmlNodePtr target,
661 const xmlChar *string, int len) {
662 /*
663 * optimization
664 */
665 if ((len <= 0) || (string == NULL) || (target == NULL))
666 return(target);
667
668 if (ctxt->lasttext == target->content) {
669
670 if (ctxt->lasttuse + len >= ctxt->lasttsize) {
671 xmlChar *newbuf;
672 int size;
673
674 size = ctxt->lasttsize + len + 100;
675 size *= 2;
676 newbuf = (xmlChar *) xmlRealloc(target->content,size);
677 if (newbuf == NULL) {
678 xsltTransformError(ctxt, NULL, target,
679 "xsltCopyText: text allocation failed\n");
680 return(NULL);
681 }
682 ctxt->lasttsize = size;
683 ctxt->lasttext = newbuf;
684 target->content = newbuf;
685 }
686 memcpy(&(target->content[ctxt->lasttuse]), string, len);
687 ctxt->lasttuse += len;
688 target->content[ctxt->lasttuse] = 0;
689 } else {
690 xmlNodeAddContent(target, string);
691 ctxt->lasttext = target->content;
692 len = xmlStrlen(target->content);
693 ctxt->lasttsize = len;
694 ctxt->lasttuse = len;
695 }
696 return(target);
697 }
698
699 /**
700 * xsltCopyTextString:
701 * @ctxt: a XSLT process context
702 * @target: the element where the text will be attached
703 * @string: the text string
704 * @noescape: should disable-escaping be activated for this text node.
705 *
706 * Adds @string to a newly created or an existent text node child of
707 * @target.
708 *
709 * Returns: the text node, where the text content of @cur is copied to.
710 * NULL in case of API or internal errors.
711 */
712 xmlNodePtr
713 xsltCopyTextString(xsltTransformContextPtr ctxt, xmlNodePtr target,
714 const xmlChar *string, int noescape)
715 {
716 xmlNodePtr copy;
717 int len;
718
719 if (string == NULL)
720 return(NULL);
721
722 #ifdef WITH_XSLT_DEBUG_PROCESS
723 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
724 "xsltCopyTextString: copy text %s\n",
725 string));
726 #endif
727
728 /*
729 * Play save and reset the merging mechanism for every new
730 * target node.
731 */
732 if ((target == NULL) || (target->children == NULL)) {
733 ctxt->lasttext = NULL;
734 }
735
736 /* handle coalescing of text nodes here */
737 len = xmlStrlen(string);
738 if ((ctxt->type == XSLT_OUTPUT_XML) &&
739 (ctxt->style->cdataSection != NULL) &&
740 (target != NULL) &&
741 (target->type == XML_ELEMENT_NODE) &&
742 (((target->ns == NULL) &&
743 (xmlHashLookup2(ctxt->style->cdataSection,
744 target->name, NULL) != NULL)) ||
745 ((target->ns != NULL) &&
746 (xmlHashLookup2(ctxt->style->cdataSection,
747 target->name, target->ns->href) != NULL))))
748 {
749 /*
750 * Process "cdata-section-elements".
751 */
752 if ((target->last != NULL) &&
753 (target->last->type == XML_CDATA_SECTION_NODE))
754 {
755 return(xsltAddTextString(ctxt, target->last, string, len));
756 }
757 copy = xmlNewCDataBlock(ctxt->output, string, len);
758 } else if (noescape) {
759 /*
760 * Process "disable-output-escaping".
761 */
762 if ((target != NULL) && (target->last != NULL) &&
763 (target->last->type == XML_TEXT_NODE) &&
764 (target->last->name == xmlStringTextNoenc))
765 {
766 return(xsltAddTextString(ctxt, target->last, string, len));
767 }
768 copy = xmlNewTextLen(string, len);
769 if (copy != NULL)
770 copy->name = xmlStringTextNoenc;
771 } else {
772 /*
773 * Default processing.
774 */
775 if ((target != NULL) && (target->last != NULL) &&
776 (target->last->type == XML_TEXT_NODE) &&
777 (target->last->name == xmlStringText)) {
778 return(xsltAddTextString(ctxt, target->last, string, len));
779 }
780 copy = xmlNewTextLen(string, len);
781 }
782 if (copy != NULL) {
783 if (target != NULL)
784 copy = xsltAddChild(target, copy);
785 ctxt->lasttext = copy->content;
786 ctxt->lasttsize = len;
787 ctxt->lasttuse = len;
788 } else {
789 xsltTransformError(ctxt, NULL, target,
790 "xsltCopyTextString: text copy failed\n");
791 ctxt->lasttext = NULL;
792 }
793 return(copy);
794 }
795
796 /**
797 * xsltCopyText:
798 * @ctxt: a XSLT process context
799 * @target: the element where the text will be attached
800 * @cur: the text or CDATA node
801 * @interned: the string is in the target doc dictionary
802 *
803 * Copy the text content of @cur and append it to @target's children.
804 *
805 * Returns: the text node, where the text content of @cur is copied to.
806 * NULL in case of API or internal errors.
807 */
808 static xmlNodePtr
809 xsltCopyText(xsltTransformContextPtr ctxt, xmlNodePtr target,
810 xmlNodePtr cur, int interned)
811 {
812 xmlNodePtr copy;
813
814 if ((cur->type != XML_TEXT_NODE) &&
815 (cur->type != XML_CDATA_SECTION_NODE))
816 return(NULL);
817 if (cur->content == NULL)
818 return(NULL);
819
820 #ifdef WITH_XSLT_DEBUG_PROCESS
821 if (cur->type == XML_CDATA_SECTION_NODE) {
822 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
823 "xsltCopyText: copy CDATA text %s\n",
824 cur->content));
825 } else if (cur->name == xmlStringTextNoenc) {
826 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
827 "xsltCopyText: copy unescaped text %s\n",
828 cur->content));
829 } else {
830 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
831 "xsltCopyText: copy text %s\n",
832 cur->content));
833 }
834 #endif
835
836 /*
837 * Play save and reset the merging mechanism for every new
838 * target node.
839 */
840 if ((target == NULL) || (target->children == NULL)) {
841 ctxt->lasttext = NULL;
842 }
843
844 if ((ctxt->style->cdataSection != NULL) &&
845 (ctxt->type == XSLT_OUTPUT_XML) &&
846 (target != NULL) &&
847 (target->type == XML_ELEMENT_NODE) &&
848 (((target->ns == NULL) &&
849 (xmlHashLookup2(ctxt->style->cdataSection,
850 target->name, NULL) != NULL)) ||
851 ((target->ns != NULL) &&
852 (xmlHashLookup2(ctxt->style->cdataSection,
853 target->name, target->ns->href) != NULL))))
854 {
855 /*
856 * Process "cdata-section-elements".
857 */
858 /*
859 * OPTIMIZE TODO: xsltCopyText() is also used for attribute content.
860 */
861 /*
862 * TODO: Since this doesn't merge adjacent CDATA-section nodes,
863 * we'll get: <![CDATA[x]]><!CDATA[y]]>.
864 * TODO: Reported in #321505.
865 */
866 if ((target->last != NULL) &&
867 (target->last->type == XML_CDATA_SECTION_NODE))
868 {
869 /*
870 * Append to existing CDATA-section node.
871 */
872 copy = xsltAddTextString(ctxt, target->last, cur->content,
873 xmlStrlen(cur->content));
874 goto exit;
875 } else {
876 unsigned int len;
877
878 len = xmlStrlen(cur->content);
879 copy = xmlNewCDataBlock(ctxt->output, cur->content, len);
880 if (copy == NULL)
881 goto exit;
882 ctxt->lasttext = copy->content;
883 ctxt->lasttsize = len;
884 ctxt->lasttuse = len;
885 }
886 } else if ((target != NULL) &&
887 (target->last != NULL) &&
888 /* both escaped or both non-escaped text-nodes */
889 (((target->last->type == XML_TEXT_NODE) &&
890 (target->last->name == cur->name)) ||
891 /* non-escaped text nodes and CDATA-section nodes */
892 (((target->last->type == XML_CDATA_SECTION_NODE) &&
893 (cur->name == xmlStringTextNoenc)))))
894 {
895 /*
896 * we are appending to an existing text node
897 */
898 copy = xsltAddTextString(ctxt, target->last, cur->content,
899 xmlStrlen(cur->content));
900 goto exit;
901 } else if ((interned) && (target != NULL) &&
902 (target->doc != NULL) &&
903 (target->doc->dict == ctxt->dict))
904 {
905 /*
906 * TODO: DO we want to use this also for "text" output?
907 */
908 copy = xmlNewTextLen(NULL, 0);
909 if (copy == NULL)
910 goto exit;
911 if (cur->name == xmlStringTextNoenc)
912 copy->name = xmlStringTextNoenc;
913
914 /*
915 * Must confirm that content is in dict (bug 302821)
916 * TODO: This check should be not needed for text coming
917 * from the stylesheets
918 */
919 if (xmlDictOwns(ctxt->dict, cur->content))
920 copy->content = cur->content;
921 else {
922 if ((copy->content = xmlStrdup(cur->content)) == NULL)
923 return NULL;
924 }
925 } else {
926 /*
927 * normal processing. keep counters to extend the text node
928 * in xsltAddTextString if needed.
929 */
930 unsigned int len;
931
932 len = xmlStrlen(cur->content);
933 copy = xmlNewTextLen(cur->content, len);
934 if (copy == NULL)
935 goto exit;
936 if (cur->name == xmlStringTextNoenc)
937 copy->name = xmlStringTextNoenc;
938 ctxt->lasttext = copy->content;
939 ctxt->lasttsize = len;
940 ctxt->lasttuse = len;
941 }
942 if (copy != NULL) {
943 if (target != NULL) {
944 copy->doc = target->doc;
945 /*
946 * MAYBE TODO: Maybe we should reset the ctxt->lasttext here
947 * to ensure that the optimized text-merging mechanism
948 * won't interfere with normal node-merging in any case.
949 */
950 copy = xsltAddChild(target, copy);
951 }
952 } else {
953 xsltTransformError(ctxt, NULL, target,
954 "xsltCopyText: text copy failed\n");
955 }
956
957 exit:
958 if ((copy == NULL) || (copy->content == NULL)) {
959 xsltTransformError(ctxt, NULL, target,
960 "Internal error in xsltCopyText(): "
961 "Failed to copy the string.\n");
962 ctxt->state = XSLT_STATE_STOPPED;
963 }
964 return(copy);
965 }
966
967 /**
968 * xsltShallowCopyAttr:
969 * @ctxt: a XSLT process context
970 * @invocNode: responsible node in the stylesheet; used for error reports
971 * @target: the element where the attribute will be grafted
972 * @attr: the attribute to be copied
973 *
974 * Do a copy of an attribute.
975 * Called by:
976 * - xsltCopyTreeInternal()
977 * - xsltCopyOf()
978 * - xsltCopy()
979 *
980 * Returns: a new xmlAttrPtr, or NULL in case of error.
981 */
982 static xmlAttrPtr
983 xsltShallowCopyAttr(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
984 xmlNodePtr target, xmlAttrPtr attr)
985 {
986 xmlAttrPtr copy;
987 xmlChar *value;
988
989 if (attr == NULL)
990 return(NULL);
991
992 if (target->type != XML_ELEMENT_NODE) {
993 xsltTransformError(ctxt, NULL, invocNode,
994 "Cannot add an attribute node to a non-element node.\n");
995 return(NULL);
996 }
997
998 if (target->children != NULL) {
999 xsltTransformError(ctxt, NULL, invocNode,
1000 "Attribute nodes must be added before "
1001 "any child nodes to an element.\n");
1002 return(NULL);
1003 }
1004
1005 value = xmlNodeListGetString(attr->doc, attr->children, 1);
1006 if (attr->ns != NULL) {
1007 xmlNsPtr ns;
1008
1009 ns = xsltGetSpecialNamespace(ctxt, invocNode,
1010 attr->ns->href, attr->ns->prefix, target);
1011 if (ns == NULL) {
1012 xsltTransformError(ctxt, NULL, invocNode,
1013 "Namespace fixup error: Failed to acquire an in-scope "
1014 "namespace binding of the copied attribute '{%s}%s'.\n",
1015 attr->ns->href, attr->name);
1016 /*
1017 * TODO: Should we just stop here?
1018 */
1019 }
1020 /*
1021 * Note that xmlSetNsProp() will take care of duplicates
1022 * and assigns the new namespace even to a duplicate.
1023 */
1024 copy = xmlSetNsProp(target, ns, attr->name, value);
1025 } else {
1026 copy = xmlSetNsProp(target, NULL, attr->name, value);
1027 }
1028 if (value != NULL)
1029 xmlFree(value);
1030
1031 if (copy == NULL)
1032 return(NULL);
1033
1034 #if 0
1035 /*
1036 * NOTE: This was optimized according to bug #342695.
1037 * TODO: Can this further be optimized, if source and target
1038 * share the same dict and attr->children is just 1 text node
1039 * which is in the dict? How probable is such a case?
1040 */
1041 /*
1042 * TODO: Do we need to create an empty text node if the value
1043 * is the empty string?
1044 */
1045 value = xmlNodeListGetString(attr->doc, attr->children, 1);
1046 if (value != NULL) {
1047 txtNode = xmlNewDocText(target->doc, NULL);
1048 if (txtNode == NULL)
1049 return(NULL);
1050 if ((target->doc != NULL) &&
1051 (target->doc->dict != NULL))
1052 {
1053 txtNode->content =
1054 (xmlChar *) xmlDictLookup(target->doc->dict,
1055 BAD_CAST value, -1);
1056 xmlFree(value);
1057 } else
1058 txtNode->content = value;
1059 copy->children = txtNode;
1060 }
1061 #endif
1062
1063 return(copy);
1064 }
1065
1066 /**
1067 * xsltCopyAttrListNoOverwrite:
1068 * @ctxt: a XSLT process context
1069 * @invocNode: responsible node in the stylesheet; used for error reports
1070 * @target: the element where the new attributes will be grafted
1071 * @attr: the first attribute in the list to be copied
1072 *
1073 * Copies a list of attribute nodes, starting with @attr, over to the
1074 * @target element node.
1075 *
1076 * Called by:
1077 * - xsltCopyTreeInternal()
1078 *
1079 * Returns 0 on success and -1 on errors and internal errors.
1080 */
1081 static int
1082 xsltCopyAttrListNoOverwrite(xsltTransformContextPtr ctxt,
1083 xmlNodePtr invocNode,
1084 xmlNodePtr target, xmlAttrPtr attr)
1085 {
1086 xmlAttrPtr copy;
1087 xmlNsPtr origNs = NULL, copyNs = NULL;
1088 xmlChar *value;
1089
1090 /*
1091 * Don't use xmlCopyProp() here, since it will try to
1092 * reconciliate namespaces.
1093 */
1094 while (attr != NULL) {
1095 /*
1096 * Find a namespace node in the tree of @target.
1097 * Avoid searching for the same ns.
1098 */
1099 if (attr->ns != origNs) {
1100 origNs = attr->ns;
1101 if (attr->ns != NULL) {
1102 copyNs = xsltGetSpecialNamespace(ctxt, invocNode,
1103 attr->ns->href, attr->ns->prefix, target);
1104 if (copyNs == NULL)
1105 return(-1);
1106 } else
1107 copyNs = NULL;
1108 }
1109 /*
1110 * If attribute has a value, we need to copy it (watching out
1111 * for possible entities)
1112 */
1113 if ((attr->children) && (attr->children->type == XML_TEXT_NODE) &&
1114 (attr->children->next == NULL)) {
1115 copy = xmlNewNsProp(target, copyNs, attr->name,
1116 attr->children->content);
1117 } else if (attr->children != NULL) {
1118 value = xmlNodeListGetString(attr->doc, attr->children, 1);
1119 copy = xmlNewNsProp(target, copyNs, attr->name, BAD_CAST value);
1120 xmlFree(value);
1121 } else {
1122 copy = xmlNewNsProp(target, copyNs, attr->name, NULL);
1123 }
1124
1125 if (copy == NULL)
1126 return(-1);
1127
1128 attr = attr->next;
1129 }
1130 return(0);
1131 }
1132
1133 /**
1134 * xsltShallowCopyElem:
1135 * @ctxt: the XSLT process context
1136 * @node: the element node in the source tree
1137 * or the Literal Result Element
1138 * @insert: the parent in the result tree
1139 * @isLRE: if @node is a Literal Result Element
1140 *
1141 * Make a copy of the element node @node
1142 * and insert it as last child of @insert.
1143 *
1144 * URGENT TODO: The problem with this one (for the non-refactored code)
1145 * is that it is used for both, Literal Result Elements *and*
1146 * copying input nodes.
1147 *
1148 * BIG NOTE: This is only called for XML_ELEMENT_NODEs.
1149 *
1150 * Called from:
1151 * xsltApplySequenceConstructor()
1152 * (for Literal Result Elements - which is a problem)
1153 * xsltCopy() (for shallow-copying elements via xsl:copy)
1154 *
1155 * Returns a pointer to the new node, or NULL in case of error
1156 */
1157 static xmlNodePtr
1158 xsltShallowCopyElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
1159 xmlNodePtr insert, int isLRE)
1160 {
1161 xmlNodePtr copy;
1162
1163 if ((node->type == XML_DTD_NODE) || (insert == NULL))
1164 return(NULL);
1165 if ((node->type == XML_TEXT_NODE) ||
1166 (node->type == XML_CDATA_SECTION_NODE))
1167 return(xsltCopyText(ctxt, insert, node, 0));
1168
1169 copy = xmlDocCopyNode(node, insert->doc, 0);
1170 if (copy != NULL) {
1171 copy->doc = ctxt->output;
1172 copy = xsltAddChild(insert, copy);
1173
1174 if (node->type == XML_ELEMENT_NODE) {
1175 /*
1176 * Add namespaces as they are needed
1177 */
1178 if (node->nsDef != NULL) {
1179 /*
1180 * TODO: Remove the LRE case in the refactored code
1181 * gets enabled.
1182 */
1183 if (isLRE)
1184 xsltCopyNamespaceList(ctxt, copy, node->nsDef);
1185 else
1186 xsltCopyNamespaceListInternal(copy, node->nsDef);
1187 }
1188
1189 /*
1190 * URGENT TODO: The problem with this is that it does not
1191 * copy over all namespace nodes in scope.
1192 * The damn thing about this is, that we would need to
1193 * use the xmlGetNsList(), for every single node; this is
1194 * also done in xsltCopyTreeInternal(), but only for the top node.
1195 */
1196 if (node->ns != NULL) {
1197 if (isLRE) {
1198 /*
1199 * REVISIT TODO: Since the non-refactored code still does
1200 * ns-aliasing, we need to call xsltGetNamespace() here.
1201 * Remove this when ready.
1202 */
1203 copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy);
1204 } else {
1205 copy->ns = xsltGetSpecialNamespace(ctxt,
1206 node, node->ns->href, node->ns->prefix, copy);
1207
1208 }
1209 } else if ((insert->type == XML_ELEMENT_NODE) &&
1210 (insert->ns != NULL))
1211 {
1212 /*
1213 * "Undeclare" the default namespace.
1214 */
1215 xsltGetSpecialNamespace(ctxt, node, NULL, NULL, copy);
1216 }
1217 }
1218 } else {
1219 xsltTransformError(ctxt, NULL, node,
1220 "xsltShallowCopyElem: copy %s failed\n", node->name);
1221 }
1222 return(copy);
1223 }
1224
1225 /**
1226 * xsltCopyTreeList:
1227 * @ctxt: a XSLT process context
1228 * @invocNode: responsible node in the stylesheet; used for error reports
1229 * @list: the list of element nodes in the source tree.
1230 * @insert: the parent in the result tree.
1231 * @isLRE: is this a literal result element list
1232 * @topElemVisited: indicates if a top-most element was already processed
1233 *
1234 * Make a copy of the full list of tree @list
1235 * and insert it as last children of @insert
1236 *
1237 * NOTE: Not to be used for Literal Result Elements.
1238 *
1239 * Used by:
1240 * - xsltCopyOf()
1241 *
1242 * Returns a pointer to the new list, or NULL in case of error
1243 */
1244 static xmlNodePtr
1245 xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
1246 xmlNodePtr list,
1247 xmlNodePtr insert, int isLRE, int topElemVisited)
1248 {
1249 xmlNodePtr copy, ret = NULL;
1250
1251 while (list != NULL) {
1252 copy = xsltCopyTreeInternal(ctxt, invocNode,
1253 list, insert, isLRE, topElemVisited);
1254 if (copy != NULL) {
1255 if (ret == NULL) {
1256 ret = copy;
1257 }
1258 }
1259 list = list->next;
1260 }
1261 return(ret);
1262 }
1263
1264 /**
1265 * xsltCopyNamespaceListInternal:
1266 * @node: the target node
1267 * @cur: the first namespace
1268 *
1269 * Do a copy of a namespace list. If @node is non-NULL the
1270 * new namespaces are added automatically.
1271 * Called by:
1272 * xsltCopyTreeInternal()
1273 *
1274 * QUESTION: What is the exact difference between this function
1275 * and xsltCopyNamespaceList() in "namespaces.c"?
1276 * ANSWER: xsltCopyNamespaceList() tries to apply ns-aliases.
1277 *
1278 * Returns: a new xmlNsPtr, or NULL in case of error.
1279 */
1280 static xmlNsPtr
1281 xsltCopyNamespaceListInternal(xmlNodePtr elem, xmlNsPtr ns) {
1282 xmlNsPtr ret = NULL;
1283 xmlNsPtr p = NULL, q, luNs;
1284
1285 if (ns == NULL)
1286 return(NULL);
1287 /*
1288 * One can add namespaces only on element nodes
1289 */
1290 if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE))
1291 elem = NULL;
1292
1293 do {
1294 if (ns->type != XML_NAMESPACE_DECL)
1295 break;
1296 /*
1297 * Avoid duplicating namespace declarations on the tree.
1298 */
1299 if (elem != NULL) {
1300 if ((elem->ns != NULL) &&
1301 xmlStrEqual(elem->ns->prefix, ns->prefix) &&
1302 xmlStrEqual(elem->ns->href, ns->href))
1303 {
1304 ns = ns->next;
1305 continue;
1306 }
1307 luNs = xmlSearchNs(elem->doc, elem, ns->prefix);
1308 if ((luNs != NULL) && (xmlStrEqual(luNs->href, ns->href)))
1309 {
1310 ns = ns->next;
1311 continue;
1312 }
1313 }
1314 q = xmlNewNs(elem, ns->href, ns->prefix);
1315 if (p == NULL) {
1316 ret = p = q;
1317 } else if (q != NULL) {
1318 p->next = q;
1319 p = q;
1320 }
1321 ns = ns->next;
1322 } while (ns != NULL);
1323 return(ret);
1324 }
1325
1326 /**
1327 * xsltShallowCopyNsNode:
1328 * @ctxt: the XSLT transformation context
1329 * @invocNode: responsible node in the stylesheet; used for error reports
1330 * @insert: the target element node in the result tree
1331 * @ns: the namespace node
1332 *
1333 * This is used for copying ns-nodes with xsl:copy-of and xsl:copy.
1334 *
1335 * Returns a new/existing ns-node, or NULL.
1336 */
1337 static xmlNsPtr
1338 xsltShallowCopyNsNode(xsltTransformContextPtr ctxt,
1339 xmlNodePtr invocNode,
1340 xmlNodePtr insert,
1341 xmlNsPtr ns)
1342 {
1343 /*
1344 * TODO: Contrary to header comments, this is declared as int.
1345 * be modified to return a node pointer, or NULL if any error
1346 */
1347 xmlNsPtr tmpns;
1348
1349 if ((insert == NULL) || (insert->type != XML_ELEMENT_NODE))
1350 return(NULL);
1351
1352 if (insert->children != NULL) {
1353 xsltTransformError(ctxt, NULL, invocNode,
1354 "Namespace nodes must be added before "
1355 "any child nodes are added to an element.\n");
1356 return(NULL);
1357 }
1358 /*
1359 * BIG NOTE: Xalan-J simply overwrites any ns-decls with
1360 * an equal prefix. We definitively won't do that.
1361 *
1362 * MSXML 4.0 and the .NET ignores ns-decls for which an
1363 * equal prefix is already in use.
1364 *
1365 * Saxon raises an error like:
1366 * "net.sf.saxon.xpath.DynamicError: Cannot create two namespace
1367 * nodes with the same name".
1368 *
1369 * NOTE: We'll currently follow MSXML here.
1370 * REVISIT TODO: Check if it's better to follow Saxon here.
1371 */
1372 if (ns->prefix == NULL) {
1373 /*
1374 * If we are adding ns-nodes to an element using e.g.
1375 * <xsl:copy-of select="/foo/namespace::*">, then we need
1376 * to ensure that we don't incorrectly declare a default
1377 * namespace on an element in no namespace, which otherwise
1378 * would move the element incorrectly into a namespace, if
1379 * the node tree is serialized.
1380 */
1381 if (insert->ns == NULL)
1382 goto occupied;
1383 } else if ((ns->prefix[0] == 'x') &&
1384 xmlStrEqual(ns->prefix, BAD_CAST "xml"))
1385 {
1386 /*
1387 * The XML namespace is built in.
1388 */
1389 return(NULL);
1390 }
1391
1392 if (insert->nsDef != NULL) {
1393 tmpns = insert->nsDef;
1394 do {
1395 if ((tmpns->prefix == NULL) == (ns->prefix == NULL)) {
1396 if ((tmpns->prefix == ns->prefix) ||
1397 xmlStrEqual(tmpns->prefix, ns->prefix))
1398 {
1399 /*
1400 * Same prefix.
1401 */
1402 if (xmlStrEqual(tmpns->href, ns->href))
1403 return(NULL);
1404 goto occupied;
1405 }
1406 }
1407 tmpns = tmpns->next;
1408 } while (tmpns != NULL);
1409 }
1410 tmpns = xmlSearchNs(insert->doc, insert, ns->prefix);
1411 if ((tmpns != NULL) && xmlStrEqual(tmpns->href, ns->href))
1412 return(NULL);
1413 /*
1414 * Declare a new namespace.
1415 * TODO: The problem (wrt efficiency) with this xmlNewNs() is
1416 * that it will again search the already declared namespaces
1417 * for a duplicate :-/
1418 */
1419 return(xmlNewNs(insert, ns->href, ns->prefix));
1420
1421 occupied:
1422 /*
1423 * TODO: We could as well raise an error here (like Saxon does),
1424 * or at least generate a warning.
1425 */
1426 return(NULL);
1427 }
1428
1429 /**
1430 * xsltCopyTreeInternal:
1431 * @ctxt: the XSLT transformation context
1432 * @invocNode: responsible node in the stylesheet; used for error reports
1433 * @node: the element node in the source tree
1434 * @insert: the parent in the result tree
1435 * @isLRE: indicates if @node is a Literal Result Element
1436 * @topElemVisited: indicates if a top-most element was already processed
1437 *
1438 * Make a copy of the full tree under the element node @node
1439 * and insert it as last child of @insert
1440 *
1441 * NOTE: Not to be used for Literal Result Elements.
1442 *
1443 * Used by:
1444 * - xsltCopyOf()
1445 *
1446 * Returns a pointer to the new tree, or NULL in case of error
1447 */
1448 static xmlNodePtr
1449 xsltCopyTreeInternal(xsltTransformContextPtr ctxt,
1450 xmlNodePtr invocNode,
1451 xmlNodePtr node,
1452 xmlNodePtr insert, int isLRE, int topElemVisited)
1453 {
1454 xmlNodePtr copy;
1455
1456 if (node == NULL)
1457 return(NULL);
1458 switch (node->type) {
1459 case XML_ELEMENT_NODE:
1460 case XML_ENTITY_REF_NODE:
1461 case XML_ENTITY_NODE:
1462 case XML_PI_NODE:
1463 case XML_COMMENT_NODE:
1464 case XML_DOCUMENT_NODE:
1465 case XML_HTML_DOCUMENT_NODE:
1466 #ifdef LIBXML_DOCB_ENABLED
1467 case XML_DOCB_DOCUMENT_NODE:
1468 #endif
1469 break;
1470 case XML_TEXT_NODE: {
1471 int noenc = (node->name == xmlStringTextNoenc);
1472 return(xsltCopyTextString(ctxt, insert, node->content, noenc));
1473 }
1474 case XML_CDATA_SECTION_NODE:
1475 return(xsltCopyTextString(ctxt, insert, node->content, 0));
1476 case XML_ATTRIBUTE_NODE:
1477 return((xmlNodePtr)
1478 xsltShallowCopyAttr(ctxt, invocNode, insert, (xmlAttrPtr) node));
1479 case XML_NAMESPACE_DECL:
1480 return((xmlNodePtr) xsltShallowCopyNsNode(ctxt, invocNode,
1481 insert, (xmlNsPtr) node));
1482
1483 case XML_DOCUMENT_TYPE_NODE:
1484 case XML_DOCUMENT_FRAG_NODE:
1485 case XML_NOTATION_NODE:
1486 case XML_DTD_NODE:
1487 case XML_ELEMENT_DECL:
1488 case XML_ATTRIBUTE_DECL:
1489 case XML_ENTITY_DECL:
1490 case XML_XINCLUDE_START:
1491 case XML_XINCLUDE_END:
1492 return(NULL);
1493 }
1494 if (XSLT_IS_RES_TREE_FRAG(node)) {
1495 if (node->children != NULL)
1496 copy = xsltCopyTreeList(ctxt, invocNode,
1497 node->children, insert, 0, 0);
1498 else
1499 copy = NULL;
1500 return(copy);
1501 }
1502 copy = xmlDocCopyNode(node, insert->doc, 0);
1503 if (copy != NULL) {
1504 copy->doc = ctxt->output;
1505 copy = xsltAddChild(insert, copy);
1506 /*
1507 * The node may have been coalesced into another text node.
1508 */
1509 if (insert->last != copy)
1510 return(insert->last);
1511 copy->next = NULL;
1512
1513 if (node->type == XML_ELEMENT_NODE) {
1514 /*
1515 * Copy in-scope namespace nodes.
1516 *
1517 * REVISIT: Since we try to reuse existing in-scope ns-decls by
1518 * using xmlSearchNsByHref(), this will eventually change
1519 * the prefix of an original ns-binding; thus it might
1520 * break QNames in element/attribute content.
1521 * OPTIMIZE TODO: If we had a xmlNsPtr * on the transformation
1522 * context, plus a ns-lookup function, which writes directly
1523 * to a given list, then we wouldn't need to create/free the
1524 * nsList every time.
1525 */
1526 if ((topElemVisited == 0) &&
1527 (node->parent != NULL) &&
1528 (node->parent->type != XML_DOCUMENT_NODE) &&
1529 (node->parent->type != XML_HTML_DOCUMENT_NODE))
1530 {
1531 xmlNsPtr *nsList, *curns, ns;
1532
1533 /*
1534 * If this is a top-most element in a tree to be
1535 * copied, then we need to ensure that all in-scope
1536 * namespaces are copied over. For nodes deeper in the
1537 * tree, it is sufficient to reconcile only the ns-decls
1538 * (node->nsDef entries).
1539 */
1540
1541 nsList = xmlGetNsList(node->doc, node);
1542 if (nsList != NULL) {
1543 curns = nsList;
1544 do {
1545 /*
1546 * Search by prefix first in order to break as less
1547 * QNames in element/attribute content as possible.
1548 */
1549 ns = xmlSearchNs(insert->doc, insert,
1550 (*curns)->prefix);
1551
1552 if ((ns == NULL) ||
1553 (! xmlStrEqual(ns->href, (*curns)->href)))
1554 {
1555 ns = NULL;
1556 /*
1557 * Search by namespace name.
1558 * REVISIT TODO: Currently disabled.
1559 */
1560 #if 0
1561 ns = xmlSearchNsByHref(insert->doc,
1562 insert, (*curns)->href);
1563 #endif
1564 }
1565 if (ns == NULL) {
1566 /*
1567 * Declare a new namespace on the copied element.
1568 */
1569 ns = xmlNewNs(copy, (*curns)->href,
1570 (*curns)->prefix);
1571 /* TODO: Handle errors */
1572 }
1573 if (node->ns == *curns) {
1574 /*
1575 * If this was the original's namespace then set
1576 * the generated counterpart on the copy.
1577 */
1578 copy->ns = ns;
1579 }
1580 curns++;
1581 } while (*curns != NULL);
1582 xmlFree(nsList);
1583 }
1584 } else if (node->nsDef != NULL) {
1585 /*
1586 * Copy over all namespace declaration attributes.
1587 */
1588 if (node->nsDef != NULL) {
1589 if (isLRE)
1590 xsltCopyNamespaceList(ctxt, copy, node->nsDef);
1591 else
1592 xsltCopyNamespaceListInternal(copy, node->nsDef);
1593 }
1594 }
1595 /*
1596 * Set the namespace.
1597 */
1598 if (node->ns != NULL) {
1599 if (copy->ns == NULL) {
1600 /*
1601 * This will map copy->ns to one of the newly created
1602 * in-scope ns-decls, OR create a new ns-decl on @copy.
1603 */
1604 copy->ns = xsltGetSpecialNamespace(ctxt, invocNode,
1605 node->ns->href, node->ns->prefix, copy);
1606 }
1607 } else if ((insert->type == XML_ELEMENT_NODE) &&
1608 (insert->ns != NULL))
1609 {
1610 /*
1611 * "Undeclare" the default namespace on @copy with xmlns="".
1612 */
1613 xsltGetSpecialNamespace(ctxt, invocNode, NULL, NULL, copy);
1614 }
1615 /*
1616 * Copy attribute nodes.
1617 */
1618 if (node->properties != NULL) {
1619 xsltCopyAttrListNoOverwrite(ctxt, invocNode,
1620 copy, node->properties);
1621 }
1622 if (topElemVisited == 0)
1623 topElemVisited = 1;
1624 }
1625 /*
1626 * Copy the subtree.
1627 */
1628 if (node->children != NULL) {
1629 xsltCopyTreeList(ctxt, invocNode,
1630 node->children, copy, isLRE, topElemVisited);
1631 }
1632 } else {
1633 xsltTransformError(ctxt, NULL, invocNode,
1634 "xsltCopyTreeInternal: Copying of '%s' failed.\n", node->name);
1635 }
1636 return(copy);
1637 }
1638
1639 /**
1640 * xsltCopyTree:
1641 * @ctxt: the XSLT transformation context
1642 * @node: the element node in the source tree
1643 * @insert: the parent in the result tree
1644 * @literal: indicates if @node is a Literal Result Element
1645 *
1646 * Make a copy of the full tree under the element node @node
1647 * and insert it as last child of @insert
1648 * For literal result element, some of the namespaces may not be copied
1649 * over according to section 7.1.
1650 * TODO: Why is this a public function?
1651 *
1652 * Returns a pointer to the new tree, or NULL in case of error
1653 */
1654 xmlNodePtr
1655 xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
1656 xmlNodePtr insert, int literal)
1657 {
1658 return(xsltCopyTreeInternal(ctxt, node, node, insert, literal, 0));
1659
1660 }
1661
1662 /************************************************************************
1663 * *
1664 * Error/fallback processing *
1665 * *
1666 ************************************************************************/
1667
1668 /**
1669 * xsltApplyFallbacks:
1670 * @ctxt: a XSLT process context
1671 * @node: the node in the source tree.
1672 * @inst: the node generating the error
1673 *
1674 * Process possible xsl:fallback nodes present under @inst
1675 *
1676 * Returns the number of xsl:fallback element found and processed
1677 */
1678 static int
1679 xsltApplyFallbacks(xsltTransformContextPtr ctxt, xmlNodePtr node,
1680 xmlNodePtr inst) {
1681
1682 xmlNodePtr child;
1683 int ret = 0;
1684
1685 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) ||
1686 (inst->children == NULL))
1687 return(0);
1688
1689 child = inst->children;
1690 while (child != NULL) {
1691 if ((IS_XSLT_ELEM(child)) &&
1692 (xmlStrEqual(child->name, BAD_CAST "fallback"))) {
1693 #ifdef WITH_XSLT_DEBUG_PARSING
1694 xsltGenericDebug(xsltGenericDebugContext,
1695 "applying xsl:fallback\n");
1696 #endif
1697 ret++;
1698 xsltApplySequenceConstructor(ctxt, node, child->children,
1699 NULL);
1700 }
1701 child = child->next;
1702 }
1703 return(ret);
1704 }
1705
1706 /************************************************************************
1707 * *
1708 * Default processing *
1709 * *
1710 ************************************************************************/
1711
1712 /**
1713 * xsltDefaultProcessOneNode:
1714 * @ctxt: a XSLT process context
1715 * @node: the node in the source tree.
1716 * @params: extra parameters passed to the template if any
1717 *
1718 * Process the source node with the default built-in template rule:
1719 * <xsl:template match="*|/">
1720 * <xsl:apply-templates/>
1721 * </xsl:template>
1722 *
1723 * and
1724 *
1725 * <xsl:template match="text()|@*">
1726 * <xsl:value-of select="."/>
1727 * </xsl:template>
1728 *
1729 * Note also that namespace declarations are copied directly:
1730 *
1731 * the built-in template rule is the only template rule that is applied
1732 * for namespace nodes.
1733 */
1734 static void
1735 xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
1736 xsltStackElemPtr params) {
1737 xmlNodePtr copy;
1738 xmlNodePtr delete = NULL, cur;
1739 int nbchild = 0, oldSize;
1740 int childno = 0, oldPos;
1741 xsltTemplatePtr template;
1742
1743 CHECK_STOPPED;
1744 /*
1745 * Handling of leaves
1746 */
1747 switch (node->type) {
1748 case XML_DOCUMENT_NODE:
1749 case XML_HTML_DOCUMENT_NODE:
1750 case XML_ELEMENT_NODE:
1751 break;
1752 case XML_CDATA_SECTION_NODE:
1753 #ifdef WITH_XSLT_DEBUG_PROCESS
1754 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1755 "xsltDefaultProcessOneNode: copy CDATA %s\n",
1756 node->content));
1757 #endif
1758 copy = xsltCopyText(ctxt, ctxt->insert, node, 0);
1759 if (copy == NULL) {
1760 xsltTransformError(ctxt, NULL, node,
1761 "xsltDefaultProcessOneNode: cdata copy failed\n");
1762 }
1763 return;
1764 case XML_TEXT_NODE:
1765 #ifdef WITH_XSLT_DEBUG_PROCESS
1766 if (node->content == NULL) {
1767 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1768 "xsltDefaultProcessOneNode: copy empty text\n"));
1769 return;
1770 } else {
1771 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1772 "xsltDefaultProcessOneNode: copy text %s\n",
1773 node->content));
1774 }
1775 #endif
1776 copy = xsltCopyText(ctxt, ctxt->insert, node, 0);
1777 if (copy == NULL) {
1778 xsltTransformError(ctxt, NULL, node,
1779 "xsltDefaultProcessOneNode: text copy failed\n");
1780 }
1781 return;
1782 case XML_ATTRIBUTE_NODE:
1783 cur = node->children;
1784 while ((cur != NULL) && (cur->type != XML_TEXT_NODE))
1785 cur = cur->next;
1786 if (cur == NULL) {
1787 xsltTransformError(ctxt, NULL, node,
1788 "xsltDefaultProcessOneNode: no text for attribute\n");
1789 } else {
1790 #ifdef WITH_XSLT_DEBUG_PROCESS
1791 if (cur->content == NULL) {
1792 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1793 "xsltDefaultProcessOneNode: copy empty text\n"));
1794 } else {
1795 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1796 "xsltDefaultProcessOneNode: copy text %s\n",
1797 cur->content));
1798 }
1799 #endif
1800 copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
1801 if (copy == NULL) {
1802 xsltTransformError(ctxt, NULL, node,
1803 "xsltDefaultProcessOneNode: text copy failed\n");
1804 }
1805 }
1806 return;
1807 default:
1808 return;
1809 }
1810 /*
1811 * Handling of Elements: first pass, cleanup and counting
1812 */
1813 cur = node->children;
1814 while (cur != NULL) {
1815 switch (cur->type) {
1816 case XML_TEXT_NODE:
1817 case XML_CDATA_SECTION_NODE:
1818 case XML_DOCUMENT_NODE:
1819 case XML_HTML_DOCUMENT_NODE:
1820 case XML_ELEMENT_NODE:
1821 case XML_PI_NODE:
1822 case XML_COMMENT_NODE:
1823 nbchild++;
1824 break;
1825 case XML_DTD_NODE:
1826 /* Unlink the DTD, it's still reachable using doc->intSubset */
1827 if (cur->next != NULL)
1828 cur->next->prev = cur->prev;
1829 if (cur->prev != NULL)
1830 cur->prev->next = cur->next;
1831 break;
1832 default:
1833 #ifdef WITH_XSLT_DEBUG_PROCESS
1834 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1835 "xsltDefaultProcessOneNode: skipping node type %d\n",
1836 cur->type));
1837 #endif
1838 delete = cur;
1839 }
1840 cur = cur->next;
1841 if (delete != NULL) {
1842 #ifdef WITH_XSLT_DEBUG_PROCESS
1843 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1844 "xsltDefaultProcessOneNode: removing ignorable blank node\n"));
1845 #endif
1846 xmlUnlinkNode(delete);
1847 xmlFreeNode(delete);
1848 delete = NULL;
1849 }
1850 }
1851 if (delete != NULL) {
1852 #ifdef WITH_XSLT_DEBUG_PROCESS
1853 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1854 "xsltDefaultProcessOneNode: removing ignorable blank node\n"));
1855 #endif
1856 xmlUnlinkNode(delete);
1857 xmlFreeNode(delete);
1858 delete = NULL;
1859 }
1860
1861 /*
1862 * Handling of Elements: second pass, actual processing
1863 */
1864 oldSize = ctxt->xpathCtxt->contextSize;
1865 oldPos = ctxt->xpathCtxt->proximityPosition;
1866 cur = node->children;
1867 while (cur != NULL) {
1868 childno++;
1869 switch (cur->type) {
1870 case XML_DOCUMENT_NODE:
1871 case XML_HTML_DOCUMENT_NODE:
1872 case XML_ELEMENT_NODE:
1873 ctxt->xpathCtxt->contextSize = nbchild;
1874 ctxt->xpathCtxt->proximityPosition = childno;
1875 xsltProcessOneNode(ctxt, cur, params);
1876 break;
1877 case XML_CDATA_SECTION_NODE:
1878 template = xsltGetTemplate(ctxt, cur, NULL);
1879 if (template) {
1880 #ifdef WITH_XSLT_DEBUG_PROCESS
1881 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1882 "xsltDefaultProcessOneNode: applying template for CDATA %s\n",
1883 cur->content));
1884 #endif
1885 /*
1886 * Instantiate the xsl:template.
1887 */
1888 xsltApplyXSLTTemplate(ctxt, cur, template->content,
1889 template, params);
1890 } else /* if (ctxt->mode == NULL) */ {
1891 #ifdef WITH_XSLT_DEBUG_PROCESS
1892 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1893 "xsltDefaultProcessOneNode: copy CDATA %s\n",
1894 cur->content));
1895 #endif
1896 copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
1897 if (copy == NULL) {
1898 xsltTransformError(ctxt, NULL, cur,
1899 "xsltDefaultProcessOneNode: cdata copy failed\n");
1900 }
1901 }
1902 break;
1903 case XML_TEXT_NODE:
1904 template = xsltGetTemplate(ctxt, cur, NULL);
1905 if (template) {
1906 #ifdef WITH_XSLT_DEBUG_PROCESS
1907 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1908 "xsltDefaultProcessOneNode: applying template for text %s\n",
1909 cur->content));
1910 #endif
1911 ctxt->xpathCtxt->contextSize = nbchild;
1912 ctxt->xpathCtxt->proximityPosition = childno;
1913 /*
1914 * Instantiate the xsl:template.
1915 */
1916 xsltApplyXSLTTemplate(ctxt, cur, template->content,
1917 template, params);
1918 } else /* if (ctxt->mode == NULL) */ {
1919 #ifdef WITH_XSLT_DEBUG_PROCESS
1920 if (cur->content == NULL) {
1921 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1922 "xsltDefaultProcessOneNode: copy empty text\n"));
1923 } else {
1924 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1925 "xsltDefaultProcessOneNode: copy text %s\n",
1926 cur->content));
1927 }
1928 #endif
1929 copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
1930 if (copy == NULL) {
1931 xsltTransformError(ctxt, NULL, cur,
1932 "xsltDefaultProcessOneNode: text copy failed\n");
1933 }
1934 }
1935 break;
1936 case XML_PI_NODE:
1937 case XML_COMMENT_NODE:
1938 template = xsltGetTemplate(ctxt, cur, NULL);
1939 if (template) {
1940 #ifdef WITH_XSLT_DEBUG_PROCESS
1941 if (cur->type == XML_PI_NODE) {
1942 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1943 "xsltDefaultProcessOneNode: template found for PI %s\n",
1944 cur->name));
1945 } else if (cur->type == XML_COMMENT_NODE) {
1946 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1947 "xsltDefaultProcessOneNode: template found for comment\n"));
1948 }
1949 #endif
1950 ctxt->xpathCtxt->contextSize = nbchild;
1951 ctxt->xpathCtxt->proximityPosition = childno;
1952 /*
1953 * Instantiate the xsl:template.
1954 */
1955 xsltApplyXSLTTemplate(ctxt, cur, template->content,
1956 template, params);
1957 }
1958 break;
1959 default:
1960 break;
1961 }
1962 cur = cur->next;
1963 }
1964 ctxt->xpathCtxt->contextSize = oldSize;
1965 ctxt->xpathCtxt->proximityPosition = oldPos;
1966 }
1967
1968 /**
1969 * xsltProcessOneNode:
1970 * @ctxt: a XSLT process context
1971 * @contextNode: the "current node" in the source tree
1972 * @withParams: extra parameters (e.g. xsl:with-param) passed to the
1973 * template if any
1974 *
1975 * Process the source node.
1976 */
1977 void
1978 xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
1979 xsltStackElemPtr withParams)
1980 {
1981 xsltTemplatePtr templ;
1982 xmlNodePtr oldNode;
1983
1984 templ = xsltGetTemplate(ctxt, contextNode, NULL);
1985 /*
1986 * If no template is found, apply the default rule.
1987 */
1988 if (templ == NULL) {
1989 #ifdef WITH_XSLT_DEBUG_PROCESS
1990 if (contextNode->type == XML_DOCUMENT_NODE) {
1991 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1992 "xsltProcessOneNode: no template found for /\n"));
1993 } else if (contextNode->type == XML_CDATA_SECTION_NODE) {
1994 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1995 "xsltProcessOneNode: no template found for CDATA\n"));
1996 } else if (contextNode->type == XML_ATTRIBUTE_NODE) {
1997 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1998 "xsltProcessOneNode: no template found for attribute %s\n",
1999 ((xmlAttrPtr) contextNode)->name));
2000 } else {
2001 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2002 "xsltProcessOneNode: no template found for %s\n", contextNode->name));
2003 }
2004 #endif
2005 oldNode = ctxt->node;
2006 ctxt->node = contextNode;
2007 xsltDefaultProcessOneNode(ctxt, contextNode, withParams);
2008 ctxt->node = oldNode;
2009 return;
2010 }
2011
2012 if (contextNode->type == XML_ATTRIBUTE_NODE) {
2013 xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule;
2014 /*
2015 * Set the "current template rule".
2016 */
2017 ctxt->currentTemplateRule = templ;
2018
2019 #ifdef WITH_XSLT_DEBUG_PROCESS
2020 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2021 "xsltProcessOneNode: applying template '%s' for attribute %s\n",
2022 templ->match, contextNode->name));
2023 #endif
2024 xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams);
2025
2026 ctxt->currentTemplateRule = oldCurTempRule;
2027 } else {
2028 xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule;
2029 /*
2030 * Set the "current template rule".
2031 */
2032 ctxt->currentTemplateRule = templ;
2033
2034 #ifdef WITH_XSLT_DEBUG_PROCESS
2035 if (contextNode->type == XML_DOCUMENT_NODE) {
2036 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2037 "xsltProcessOneNode: applying template '%s' for /\n",
2038 templ->match));
2039 } else {
2040 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2041 "xsltProcessOneNode: applying template '%s' for %s\n",
2042 templ->match, contextNode->name));
2043 }
2044 #endif
2045 xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams);
2046
2047 ctxt->currentTemplateRule = oldCurTempRule;
2048 }
2049 }
2050
2051 static xmlNodePtr
2052 xsltDebuggerStartSequenceConstructor(xsltTransformContextPtr ctxt,
2053 xmlNodePtr contextNode,
2054 xmlNodePtr list,
2055 xsltTemplatePtr templ,
2056 int *addCallResult)
2057 {
2058 xmlNodePtr debugedNode = NULL;
2059
2060 if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
2061 if (templ) {
2062 *addCallResult = xslAddCall(templ, templ->elem);
2063 } else {
2064 *addCallResult = xslAddCall(NULL, list);
2065 }
2066 switch (ctxt->debugStatus) {
2067 case XSLT_DEBUG_RUN_RESTART:
2068 case XSLT_DEBUG_QUIT:
2069 if (*addCallResult)
2070 xslDropCall();
2071 return(NULL);
2072 }
2073 if (templ) {
2074 xslHandleDebugger(templ->elem, contextNode, templ, ctxt);
2075 debugedNode = templ->elem;
2076 } else if (list) {
2077 xslHandleDebugger(list, contextNode, templ, ctxt);
2078 debugedNode = list;
2079 } else if (ctxt->inst) {
2080 xslHandleDebugger(ctxt->inst, contextNode, templ, ctxt);
2081 debugedNode = ctxt->inst;
2082 }
2083 }
2084 return(debugedNode);
2085 }
2086
2087 /**
2088 * xsltLocalVariablePush:
2089 * @ctxt: the transformation context
2090 * @variable: variable to be pushed to the variable stack
2091 * @level: new value for variable's level
2092 *
2093 * Places the variable onto the local variable stack
2094 *
2095 * Returns: 0 for success, -1 for any error
2096 * **NOTE:**
2097 * This is an internal routine and should not be called by users!
2098 */
2099 int
2100 xsltLocalVariablePush(xsltTransformContextPtr ctxt,
2101 xsltStackElemPtr variable,
2102 int level)
2103 {
2104 if (ctxt->varsMax == 0) {
2105 ctxt->varsMax = 10;
2106 ctxt->varsTab =
2107 (xsltStackElemPtr *) xmlMalloc(ctxt->varsMax *
2108 sizeof(ctxt->varsTab[0]));
2109 if (ctxt->varsTab == NULL) {
2110 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
2111 return (-1);
2112 }
2113 }
2114 if (ctxt->varsNr >= ctxt->varsMax) {
2115 ctxt->varsMax *= 2;
2116 ctxt->varsTab =
2117 (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab,
2118 ctxt->varsMax *
2119 sizeof(ctxt->varsTab[0]));
2120 if (ctxt->varsTab == NULL) {
2121 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
2122 return (-1);
2123 }
2124 }
2125 ctxt->varsTab[ctxt->varsNr++] = variable;
2126 ctxt->vars = variable;
2127 variable->level = level;
2128 return(0);
2129 }
2130
2131 /**
2132 * xsltReleaseLocalRVTs:
2133 *
2134 * Fragments which are results of extension instructions
2135 * are preserved; all other fragments are freed/cached.
2136 */
2137 static void
2138 xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt, xmlDocPtr base)
2139 {
2140 xmlDocPtr cur = ctxt->localRVT, tmp;
2141
2142 while ((cur != NULL) && (cur != base)) {
2143 if (cur->psvi == (void *) ((long) 1)) {
2144 cur = (xmlDocPtr) cur->next;
2145 } else {
2146 tmp = cur;
2147 cur = (xmlDocPtr) cur->next;
2148
2149 if (tmp == ctxt->localRVT)
2150 ctxt->localRVT = cur;
2151
2152 /*
2153 * We need ctxt->localRVTBase for extension instructions
2154 * which return values (like EXSLT's function).
2155 */
2156 if (tmp == ctxt->localRVTBase)
2157 ctxt->localRVTBase = cur;
2158
2159 if (tmp->prev)
2160 tmp->prev->next = (xmlNodePtr) cur;
2161 if (cur)
2162 cur->prev = tmp->prev;
2163 xsltReleaseRVT(ctxt, tmp);
2164 }
2165 }
2166 }
2167
2168 /**
2169 * xsltApplySequenceConstructor:
2170 * @ctxt: a XSLT process context
2171 * @contextNode: the "current node" in the source tree
2172 * @list: the nodes of a sequence constructor;
2173 * (plus leading xsl:param elements)
2174 * @templ: the compiled xsl:template (optional)
2175 *
2176 * Processes a sequence constructor.
2177 *
2178 * NOTE: ctxt->currentTemplateRule was introduced to reflect the
2179 * semantics of "current template rule". I.e. the field ctxt->templ
2180 * is not intended to reflect this, thus always pushed onto the
2181 * template stack.
2182 */
2183 static void
2184 xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
2185 xmlNodePtr contextNode, xmlNodePtr list,
2186 xsltTemplatePtr templ)
2187 {
2188 xmlNodePtr oldInsert, oldInst, oldCurInst, oldContextNode;
2189 xmlNodePtr cur, insert, copy = NULL;
2190 int level = 0, oldVarsNr;
2191 xmlDocPtr oldLocalFragmentTop, oldLocalFragmentBase;
2192
2193 #ifdef XSLT_REFACTORED
2194 xsltStylePreCompPtr info;
2195 #endif
2196
2197 #ifdef WITH_DEBUGGER
2198 int addCallResult = 0;
2199 xmlNodePtr debuggedNode = NULL;
2200 #endif
2201
2202 if (ctxt == NULL)
2203 return;
2204
2205 #ifdef WITH_DEBUGGER
2206 if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
2207 debuggedNode =
2208 xsltDebuggerStartSequenceConstructor(ctxt, contextNode,
2209 list, templ, &addCallResult);
2210 if (debuggedNode == NULL)
2211 return;
2212 }
2213 #endif
2214
2215 if (list == NULL)
2216 return;
2217 CHECK_STOPPED;
2218
2219 oldLocalFragmentTop = ctxt->localRVT;
2220 oldInsert = insert = ctxt->insert;
2221 oldInst = oldCurInst = ctxt->inst;
2222 oldContextNode = ctxt->node;
2223 /*
2224 * Save current number of variables on the stack; new vars are popped when
2225 * exiting.
2226 */
2227 oldVarsNr = ctxt->varsNr;
2228 /*
2229 * Process the sequence constructor.
2230 */
2231 cur = list;
2232 while (cur != NULL) {
2233 ctxt->inst = cur;
2234
2235 #ifdef WITH_DEBUGGER
2236 switch (ctxt->debugStatus) {
2237 case XSLT_DEBUG_RUN_RESTART:
2238 case XSLT_DEBUG_QUIT:
2239 break;
2240
2241 }
2242 #endif
2243 /*
2244 * Test; we must have a valid insertion point.
2245 */
2246 if (insert == NULL) {
2247
2248 #ifdef WITH_XSLT_DEBUG_PROCESS
2249 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2250 "xsltApplySequenceConstructor: insert == NULL !\n"));
2251 #endif
2252 goto error;
2253 }
2254
2255 #ifdef WITH_DEBUGGER
2256 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (debuggedNode != cur))
2257 xslHandleDebugger(cur, contextNode, templ, ctxt);
2258 #endif
2259
2260 #ifdef XSLT_REFACTORED
2261 if (cur->type == XML_ELEMENT_NODE) {
2262 info = (xsltStylePreCompPtr) cur->psvi;
2263 /*
2264 * We expect a compiled representation on:
2265 * 1) XSLT instructions of this XSLT version (1.0)
2266 * (with a few exceptions)
2267 * 2) Literal result elements
2268 * 3) Extension instructions
2269 * 4) XSLT instructions of future XSLT versions
2270 * (forwards-compatible mode).
2271 */
2272 if (info == NULL) {
2273 /*
2274 * Handle the rare cases where we don't expect a compiled
2275 * representation on an XSLT element.
2276 */
2277 if (IS_XSLT_ELEM_FAST(cur) && IS_XSLT_NAME(cur, "message")) {
2278 xsltMessage(ctxt, contextNode, cur);
2279 goto skip_children;
2280 }
2281 /*
2282 * Something really went wrong:
2283 */
2284 xsltTransformError(ctxt, NULL, cur,
2285 "Internal error in xsltApplySequenceConstructor(): "
2286 "The element '%s' in the stylesheet has no compiled "
2287 "representation.\n",
2288 cur->name);
2289 goto skip_children;
2290 }
2291
2292 if (info->type == XSLT_FUNC_LITERAL_RESULT_ELEMENT) {
2293 xsltStyleItemLRElementInfoPtr lrInfo =
2294 (xsltStyleItemLRElementInfoPtr) info;
2295 /*
2296 * Literal result elements
2297 * --------------------------------------------------------
2298 */
2299 #ifdef WITH_XSLT_DEBUG_PROCESS
2300 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
2301 xsltGenericDebug(xsltGenericDebugContext,
2302 "xsltApplySequenceConstructor: copy literal result "
2303 "element '%s'\n", cur->name));
2304 #endif
2305 /*
2306 * Copy the raw element-node.
2307 * OLD: if ((copy = xsltShallowCopyElem(ctxt, cur, insert))
2308 * == NULL)
2309 * goto error;
2310 */
2311 copy = xmlDocCopyNode(cur, insert->doc, 0);
2312 if (copy == NULL) {
2313 xsltTransformError(ctxt, NULL, cur,
2314 "Internal error in xsltApplySequenceConstructor(): "
2315 "Failed to copy literal result element '%s'.\n",
2316 cur->name);
2317 goto error;
2318 } else {
2319 /*
2320 * Add the element-node to the result tree.
2321 */
2322 copy->doc = ctxt->output;
2323 copy = xsltAddChild(insert, copy);
2324 /*
2325 * Create effective namespaces declarations.
2326 * OLD: xsltCopyNamespaceList(ctxt, copy, cur->nsDef);
2327 */
2328 if (lrInfo->effectiveNs != NULL) {
2329 xsltEffectiveNsPtr effNs = lrInfo->effectiveNs;
2330 xmlNsPtr ns, lastns = NULL;
2331
2332 while (effNs != NULL) {
2333 /*
2334 * Avoid generating redundant namespace
2335 * declarations; thus lookup if there is already
2336 * such a ns-decl in the result.
2337 */
2338 ns = xmlSearchNs(copy->doc, copy, effNs->prefix);
2339 if ((ns != NULL) &&
2340 (xmlStrEqual(ns->href, effNs->nsName)))
2341 {
2342 effNs = effNs->next;
2343 continue;
2344 }
2345 ns = xmlNewNs(copy, effNs->nsName, effNs->prefix);
2346 if (ns == NULL) {
2347 xsltTransformError(ctxt, NULL, cur,
2348 "Internal error in "
2349 "xsltApplySequenceConstructor(): "
2350 "Failed to copy a namespace "
2351 "declaration.\n");
2352 goto error;
2353 }
2354
2355 if (lastns == NULL)
2356 copy->nsDef = ns;
2357 else
2358 lastns->next =ns;
2359 lastns = ns;
2360
2361 effNs = effNs->next;
2362 }
2363
2364 }
2365 /*
2366 * NOTE that we don't need to apply ns-alising: this was
2367 * already done at compile-time.
2368 */
2369 if (cur->ns != NULL) {
2370 /*
2371 * If there's no such ns-decl in the result tree,
2372 * then xsltGetSpecialNamespace() will
2373 * create a ns-decl on the copied node.
2374 */
2375 copy->ns = xsltGetSpecialNamespace(ctxt, cur,
2376 cur->ns->href, cur->ns->prefix, copy);
2377 } else {
2378 /*
2379 * Undeclare the default namespace if needed.
2380 * This can be skipped, if the result element has
2381 * no ns-decls, in which case the result element
2382 * obviously does not declare a default namespace;
2383 * AND there's either no parent, or the parent
2384 * element is in no namespace; this means there's no
2385 * default namespace is scope to care about.
2386 *
2387 * REVISIT: This might result in massive
2388 * generation of ns-decls if nodes in a default
2389 * namespaces are mixed with nodes in no namespace.
2390 *
2391 */
2392 if (copy->nsDef ||
2393 ((insert != NULL) &&
2394 (insert->type == XML_ELEMENT_NODE) &&
2395 (insert->ns != NULL)))
2396 {
2397 xsltGetSpecialNamespace(ctxt, cur,
2398 NULL, NULL, copy);
2399 }
2400 }
2401 }
2402 /*
2403 * SPEC XSLT 2.0 "Each attribute of the literal result
2404 * element, other than an attribute in the XSLT namespace,
2405 * is processed to produce an attribute for the element in
2406 * the result tree."
2407 * NOTE: See bug #341325.
2408 */
2409 if (cur->properties != NULL) {
2410 xsltAttrListTemplateProcess(ctxt, copy, cur->properties);
2411 }
2412 } else if (IS_XSLT_ELEM_FAST(cur)) {
2413 /*
2414 * XSLT instructions
2415 * --------------------------------------------------------
2416 */
2417 if (info->type == XSLT_FUNC_UNKOWN_FORWARDS_COMPAT) {
2418 /*
2419 * We hit an unknown XSLT element.
2420 * Try to apply one of the fallback cases.
2421 */
2422 ctxt->insert = insert;
2423 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
2424 xsltTransformError(ctxt, NULL, cur,
2425 "The is no fallback behaviour defined for "
2426 "the unknown XSLT element '%s'.\n",
2427 cur->name);
2428 }
2429 ctxt->insert = oldInsert;
2430 } else if (info->func != NULL) {
2431 /*
2432 * Execute the XSLT instruction.
2433 */
2434 ctxt->insert = insert;
2435
2436 info->func(ctxt, contextNode, cur,
2437 (xsltElemPreCompPtr) info);
2438
2439 /*
2440 * Cleanup temporary tree fragments.
2441 */
2442 if (oldLocalFragmentTop != ctxt->localRVT)
2443 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
2444
2445 ctxt->insert = oldInsert;
2446 } else if (info->type == XSLT_FUNC_VARIABLE) {
2447 xsltStackElemPtr tmpvar = ctxt->vars;
2448
2449 xsltParseStylesheetVariable(ctxt, cur);
2450
2451 if (tmpvar != ctxt->vars) {
2452 /*
2453 * TODO: Using a @tmpvar is an annoying workaround, but
2454 * the current mechanisms do not provide any other way
2455 * of knowing if the var was really pushed onto the
2456 * stack.
2457 */
2458 ctxt->vars->level = level;
2459 }
2460 } else if (info->type == XSLT_FUNC_MESSAGE) {
2461 /*
2462 * TODO: Won't be hit, since we don't compile xsl:message.
2463 */
2464 xsltMessage(ctxt, contextNode, cur);
2465 } else {
2466 xsltTransformError(ctxt, NULL, cur,
2467 "Unexpected XSLT element '%s'.\n", cur->name);
2468 }
2469 goto skip_children;
2470
2471 } else {
2472 xsltTransformFunction func;
2473 /*
2474 * Extension intructions (elements)
2475 * --------------------------------------------------------
2476 */
2477 if (cur->psvi == xsltExtMarker) {
2478 /*
2479 * The xsltExtMarker was set during the compilation
2480 * of extension instructions if there was no registered
2481 * handler for this specific extension function at
2482 * compile-time.
2483 * Libxslt will now lookup if a handler is
2484 * registered in the context of this transformation.
2485 */
2486 func = (xsltTransformFunction)
2487 xsltExtElementLookup(ctxt, cur->name, cur->ns->href);
2488 } else
2489 func = ((xsltElemPreCompPtr) cur->psvi)->func;
2490
2491 if (func == NULL) {
2492 /*
2493 * No handler available.
2494 * Try to execute fallback behaviour via xsl:fallback.
2495 */
2496 #ifdef WITH_XSLT_DEBUG_PROCESS
2497 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
2498 xsltGenericDebug(xsltGenericDebugContext,
2499 "xsltApplySequenceConstructor: unknown extension %s\n",
2500 cur->name));
2501 #endif
2502 ctxt->insert = insert;
2503 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
2504 xsltTransformError(ctxt, NULL, cur,
2505 "Unknown extension instruction '{%s}%s'.\n",
2506 cur->ns->href, cur->name);
2507 }
2508 ctxt->insert = oldInsert;
2509 } else {
2510 /*
2511 * Execute the handler-callback.
2512 */
2513 #ifdef WITH_XSLT_DEBUG_PROCESS
2514 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2515 "xsltApplySequenceConstructor: extension construct %s\n",
2516 cur->name));
2517 #endif
2518 ctxt->insert = insert;
2519 /*
2520 * We need the fragment base for extension instructions
2521 * which return values (like EXSLT's function).
2522 */
2523 oldLocalFragmentBase = ctxt->localRVTBase;
2524 ctxt->localRVTBase = NULL;
2525
2526 func(ctxt, contextNode, cur, cur->psvi);
2527
2528 ctxt->localRVTBase = oldLocalFragmentBase;
2529 /*
2530 * Cleanup temporary tree fragments.
2531 */
2532 if (oldLocalFragmentTop != ctxt->localRVT)
2533 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
2534
2535 ctxt->insert = oldInsert;
2536 }
2537 goto skip_children;
2538 }
2539
2540 } else if (XSLT_IS_TEXT_NODE(cur)) {
2541 /*
2542 * Text
2543 * ------------------------------------------------------------
2544 */
2545 #ifdef WITH_XSLT_DEBUG_PROCESS
2546 if (cur->name == xmlStringTextNoenc) {
2547 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
2548 xsltGenericDebug(xsltGenericDebugContext,
2549 "xsltApplySequenceConstructor: copy unescaped text '%s'\n",
2550 cur->content));
2551 } else {
2552 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
2553 xsltGenericDebug(xsltGenericDebugContext,
2554 "xsltApplySequenceConstructor: copy text '%s'\n",
2555 cur->content));
2556 }
2557 #endif
2558 if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL)
2559 goto error;
2560 }
2561
2562 #else /* XSLT_REFACTORED */
2563
2564 if (IS_XSLT_ELEM(cur)) {
2565 /*
2566 * This is an XSLT node
2567 */
2568 xsltStylePreCompPtr info = (xsltStylePreCompPtr) cur->psvi;
2569
2570 if (info == NULL) {
2571 if (IS_XSLT_NAME(cur, "message")) {
2572 xsltMessage(ctxt, contextNode, cur);
2573 } else {
2574 /*
2575 * That's an error try to apply one of the fallback cases
2576 */
2577 ctxt->insert = insert;
2578 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
2579 xsltGenericError(xsltGenericErrorContext,
2580 "xsltApplySequenceConstructor: %s was not compiled\n",
2581 cur->name);
2582 }
2583 ctxt->insert = oldInsert;
2584 }
2585 goto skip_children;
2586 }
2587
2588 if (info->func != NULL) {
2589 oldCurInst = ctxt->inst;
2590 ctxt->inst = cur;
2591 ctxt->insert = insert;
2592 oldLocalFragmentBase = ctxt->localRVTBase;
2593 ctxt->localRVTBase = NULL;
2594
2595 info->func(ctxt, contextNode, cur, (xsltElemPreCompPtr) info);
2596
2597 ctxt->localRVTBase = oldLocalFragmentBase;
2598 /*
2599 * Cleanup temporary tree fragments.
2600 */
2601 if (oldLocalFragmentTop != ctxt->localRVT)
2602 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
2603
2604 ctxt->insert = oldInsert;
2605 ctxt->inst = oldCurInst;
2606 goto skip_children;
2607 }
2608
2609 if (IS_XSLT_NAME(cur, "variable")) {
2610 xsltStackElemPtr tmpvar = ctxt->vars;
2611
2612 oldCurInst = ctxt->inst;
2613 ctxt->inst = cur;
2614
2615 xsltParseStylesheetVariable(ctxt, cur);
2616
2617 ctxt->inst = oldCurInst;
2618
2619 if (tmpvar != ctxt->vars) {
2620 /*
2621 * TODO: Using a @tmpvar is an annoying workaround, but
2622 * the current mechanisms do not provide any other way
2623 * of knowing if the var was really pushed onto the
2624 * stack.
2625 */
2626 ctxt->vars->level = level;
2627 }
2628 } else if (IS_XSLT_NAME(cur, "message")) {
2629 xsltMessage(ctxt, contextNode, cur);
2630 } else {
2631 xsltTransformError(ctxt, NULL, cur,
2632 "Unexpected XSLT element '%s'.\n", cur->name);
2633 }
2634 goto skip_children;
2635 } else if ((cur->type == XML_TEXT_NODE) ||
2636 (cur->type == XML_CDATA_SECTION_NODE)) {
2637
2638 /*
2639 * This text comes from the stylesheet
2640 * For stylesheets, the set of whitespace-preserving
2641 * element names consists of just xsl:text.
2642 */
2643 #ifdef WITH_XSLT_DEBUG_PROCESS
2644 if (cur->type == XML_CDATA_SECTION_NODE) {
2645 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2646 "xsltApplySequenceConstructor: copy CDATA text %s\n",
2647 cur->content));
2648 } else if (cur->name == xmlStringTextNoenc) {
2649 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2650 "xsltApplySequenceConstructor: copy unescaped text %s\n",
2651 cur->content));
2652 } else {
2653 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2654 "xsltApplySequenceConstructor: copy text %s\n",
2655 cur->content));
2656 }
2657 #endif
2658 if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL)
2659 goto error;
2660 } else if ((cur->type == XML_ELEMENT_NODE) &&
2661 (cur->ns != NULL) && (cur->psvi != NULL)) {
2662 xsltTransformFunction function;
2663
2664 oldCurInst = ctxt->inst;
2665 ctxt->inst = cur;
2666 /*
2667 * Flagged as an extension element
2668 */
2669 if (cur->psvi == xsltExtMarker)
2670 function = (xsltTransformFunction)
2671 xsltExtElementLookup(ctxt, cur->name, cur->ns->href);
2672 else
2673 function = ((xsltElemPreCompPtr) cur->psvi)->func;
2674
2675 if (function == NULL) {
2676 xmlNodePtr child;
2677 int found = 0;
2678
2679 #ifdef WITH_XSLT_DEBUG_PROCESS
2680 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2681 "xsltApplySequenceConstructor: unknown extension %s\n",
2682 cur->name));
2683 #endif
2684 /*
2685 * Search if there are fallbacks
2686 */
2687 child = cur->children;
2688 while (child != NULL) {
2689 if ((IS_XSLT_ELEM(child)) &&
2690 (IS_XSLT_NAME(child, "fallback")))
2691 {
2692 found = 1;
2693 xsltApplySequenceConstructor(ctxt, contextNode,
2694 child->children, NULL);
2695 }
2696 child = child->next;
2697 }
2698
2699 if (!found) {
2700 xsltTransformError(ctxt, NULL, cur,
2701 "xsltApplySequenceConstructor: failed to find extension %s\n",
2702 cur->name);
2703 }
2704 } else {
2705 #ifdef WITH_XSLT_DEBUG_PROCESS
2706 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2707 "xsltApplySequenceConstructor: extension construct %s\n",
2708 cur->name));
2709 #endif
2710
2711 ctxt->insert = insert;
2712 /*
2713 * We need the fragment base for extension instructions
2714 * which return values (like EXSLT's function).
2715 */
2716 oldLocalFragmentBase = ctxt->localRVTBase;
2717 ctxt->localRVTBase = NULL;
2718
2719 function(ctxt, contextNode, cur, cur->psvi);
2720 /*
2721 * Cleanup temporary tree fragments.
2722 */
2723 if (oldLocalFragmentTop != ctxt->localRVT)
2724 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
2725
2726 ctxt->localRVTBase = oldLocalFragmentBase;
2727 ctxt->insert = oldInsert;
2728
2729 }
2730 ctxt->inst = oldCurInst;
2731 goto skip_children;
2732 } else if (cur->type == XML_ELEMENT_NODE) {
2733 #ifdef WITH_XSLT_DEBUG_PROCESS
2734 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2735 "xsltApplySequenceConstructor: copy node %s\n",
2736 cur->name));
2737 #endif
2738 oldCurInst = ctxt->inst;
2739 ctxt->inst = cur;
2740
2741 if ((copy = xsltShallowCopyElem(ctxt, cur, insert, 1)) == NULL)
2742 goto error;
2743 /*
2744 * Add extra namespaces inherited from the current template
2745 * if we are in the first level children and this is a
2746 * "real" template.
2747 */
2748 if ((templ != NULL) && (oldInsert == insert) &&
2749 (ctxt->templ != NULL) && (ctxt->templ->inheritedNs != NULL)) {
2750 int i;
2751 xmlNsPtr ns, ret;
2752
2753 for (i = 0; i < ctxt->templ->inheritedNsNr; i++) {
2754 const xmlChar *URI = NULL;
2755 xsltStylesheetPtr style;
2756 ns = ctxt->templ->inheritedNs[i];
2757
2758 /* Note that the XSLT namespace was already excluded
2759 * in xsltGetInheritedNsList().
2760 */
2761 #if 0
2762 if (xmlStrEqual(ns->href, XSLT_NAMESPACE))
2763 continue;
2764 #endif
2765 style = ctxt->style;
2766 while (style != NULL) {
2767 if (style->nsAliases != NULL)
2768 URI = (const xmlChar *)
2769 xmlHashLookup(style->nsAliases, ns->href);
2770 if (URI != NULL)
2771 break;
2772
2773 style = xsltNextImport(style);
2774 }
2775 if (URI == UNDEFINED_DEFAULT_NS)
2776 continue;
2777 if (URI == NULL)
2778 URI = ns->href;
2779 /*
2780 * TODO: The following will still be buggy for the
2781 * non-refactored code.
2782 */
2783 ret = xmlSearchNs(copy->doc, copy, ns->prefix);
2784 if ((ret == NULL) || (!xmlStrEqual(ret->href, URI)))
2785 {
2786 xmlNewNs(copy, URI, ns->prefix);
2787 }
2788 }
2789 if (copy->ns != NULL) {
2790 /*
2791 * Fix the node namespace if needed
2792 */
2793 copy->ns = xsltGetNamespace(ctxt, cur, copy->ns, copy);
2794 }
2795 }
2796 /*
2797 * all the attributes are directly inherited
2798 */
2799 if (cur->properties != NULL) {
2800 xsltAttrListTemplateProcess(ctxt, copy, cur->properties);
2801 }
2802 ctxt->inst = oldCurInst;
2803 }
2804 #endif /* else of XSLT_REFACTORED */
2805
2806 /*
2807 * Descend into content in document order.
2808 */
2809 if (cur->children != NULL) {
2810 if (cur->children->type != XML_ENTITY_DECL) {
2811 cur = cur->children;
2812 level++;
2813 if (copy != NULL)
2814 insert = copy;
2815 continue;
2816 }
2817 }
2818
2819 skip_children:
2820 /*
2821 * If xslt:message was just processed, we might have hit a
2822 * terminate='yes'; if so, then break the loop and clean up.
2823 * TODO: Do we need to check this also before trying to descend
2824 * into the content?
2825 */
2826 if (ctxt->state == XSLT_STATE_STOPPED)
2827 break;
2828 if (cur->next != NULL) {
2829 cur = cur->next;
2830 continue;
2831 }
2832
2833 do {
2834 cur = cur->parent;
2835 level--;
2836 /*
2837 * Pop variables/params (xsl:variable and xsl:param).
2838 */
2839 if ((ctxt->varsNr > oldVarsNr) && (ctxt->vars->level > level)) {
2840 xsltLocalVariablePop(ctxt, oldVarsNr, level);
2841 }
2842
2843 insert = insert->parent;
2844 if (cur == NULL)
2845 break;
2846 if (cur == list->parent) {
2847 cur = NULL;
2848 break;
2849 }
2850 if (cur->next != NULL) {
2851 cur = cur->next;
2852 break;
2853 }
2854 } while (cur != NULL);
2855 }
2856
2857 error:
2858 /*
2859 * In case of errors: pop remaining variables.
2860 */
2861 if (ctxt->varsNr > oldVarsNr)
2862 xsltLocalVariablePop(ctxt, oldVarsNr, -1);
2863
2864 ctxt->node = oldContextNode;
2865 ctxt->inst = oldInst;
2866 ctxt->insert = oldInsert;
2867
2868 #ifdef WITH_DEBUGGER
2869 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) {
2870 xslDropCall();
2871 }
2872 #endif
2873 }
2874
2875 /*
2876 * xsltApplyXSLTTemplate:
2877 * @ctxt: a XSLT transformation context
2878 * @contextNode: the node in the source tree.
2879 * @list: the nodes of a sequence constructor;
2880 * (plus leading xsl:param elements)
2881 * @templ: the compiled xsl:template declaration;
2882 * NULL if a sequence constructor
2883 * @withParams: a set of caller-parameters (xsl:with-param) or NULL
2884 *
2885 * Called by:
2886 * - xsltApplyImports()
2887 * - xsltCallTemplate()
2888 * - xsltDefaultProcessOneNode()
2889 * - xsltProcessOneNode()
2890 */
2891 static void
2892 xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt,
2893 xmlNodePtr contextNode,
2894 xmlNodePtr list,
2895 xsltTemplatePtr templ,
2896 xsltStackElemPtr withParams)
2897 {
2898 int oldVarsBase = 0;
2899 long start = 0;
2900 xmlNodePtr cur;
2901 xsltStackElemPtr tmpParam = NULL;
2902 xmlDocPtr oldUserFragmentTop, oldLocalFragmentTop;
2903
2904 #ifdef XSLT_REFACTORED
2905 xsltStyleItemParamPtr iparam;
2906 #else
2907 xsltStylePreCompPtr iparam;
2908 #endif
2909
2910 #ifdef WITH_DEBUGGER
2911 int addCallResult = 0;
2912 #endif
2913
2914 if (ctxt == NULL)
2915 return;
2916 if (templ == NULL) {
2917 xsltTransformError(ctxt, NULL, list,
2918 "xsltApplyXSLTTemplate: Bad arguments; @templ is mandatory.\n");
2919 return;
2920 }
2921
2922 #ifdef WITH_DEBUGGER
2923 if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
2924 if (xsltDebuggerStartSequenceConstructor(ctxt, contextNode,
2925 list, templ, &addCallResult) == NULL)
2926 return;
2927 }
2928 #endif
2929
2930 if (list == NULL)
2931 return;
2932 CHECK_STOPPED;
2933
2934 /*
2935 * Check for infinite recursion: stop if the maximum of nested templates
2936 * is excceeded. Adjust xsltMaxDepth if you need more.
2937 */
2938 if (((ctxt->templNr >= xsltMaxDepth) ||
2939 (ctxt->varsNr >= 5 * xsltMaxDepth)))
2940 {
2941 xsltTransformError(ctxt, NULL, list,
2942 "xsltApplyXSLTTemplate: A potential infinite template recursion "
2943 "was detected.\n"
2944 "You can adjust xsltMaxDepth (--maxdepth) in order to "
2945 "raise the maximum number of nested template calls and "
2946 "variables/params (currently set to %d).\n",
2947 xsltMaxDepth);
2948 xsltDebug(ctxt, contextNode, list, NULL);
2949 return;
2950 }
2951
2952 oldUserFragmentTop = ctxt->tmpRVT;
2953 ctxt->tmpRVT = NULL;
2954 oldLocalFragmentTop = ctxt->localRVT;
2955
2956 /*
2957 * Initiate a distinct scope of local params/variables.
2958 */
2959 oldVarsBase = ctxt->varsBase;
2960 ctxt->varsBase = ctxt->varsNr;
2961
2962 ctxt->node = contextNode;
2963 if (ctxt->profile) {
2964 templ->nbCalls++;
2965 start = xsltTimestamp();
2966 profPush(ctxt, 0);
2967 }
2968 /*
2969 * Push the xsl:template declaration onto the stack.
2970 */
2971 templPush(ctxt, templ);
2972
2973 #ifdef WITH_XSLT_DEBUG_PROCESS
2974 if (templ->name != NULL)
2975 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2976 "applying xsl:template '%s'\n", templ->name));
2977 #endif
2978 /*
2979 * Process xsl:param instructions and skip those elements for
2980 * further processing.
2981 */
2982 cur = list;
2983 do {
2984 if (cur->type == XML_TEXT_NODE) {
2985 cur = cur->next;
2986 continue;
2987 }
2988 if ((cur->type != XML_ELEMENT_NODE) ||
2989 (cur->name[0] != 'p') ||
2990 (cur->psvi == NULL) ||
2991 (! xmlStrEqual(cur->name, BAD_CAST "param")) ||
2992 (! IS_XSLT_ELEM(cur)))
2993 {
2994 break;
2995 }
2996
2997 list = cur->next;
2998
2999 #ifdef XSLT_REFACTORED
3000 iparam = (xsltStyleItemParamPtr) cur->psvi;
3001 #else
3002 iparam = (xsltStylePreCompPtr) cur->psvi;
3003 #endif
3004
3005 /*
3006 * Substitute xsl:param for a given xsl:with-param.
3007 * Since the XPath expression will reference the params/vars
3008 * by index, we need to slot the xsl:with-params in the
3009 * order of encountered xsl:params to keep the sequence of
3010 * params/variables in the stack exactly as it was at
3011 * compile time,
3012 */
3013 tmpParam = NULL;
3014 if (withParams) {
3015 tmpParam = withParams;
3016 do {
3017 if ((tmpParam->name == (iparam->name)) &&
3018 (tmpParam->nameURI == (iparam->ns)))
3019 {
3020 /*
3021 * Push the caller-parameter.
3022 */
3023 xsltLocalVariablePush(ctxt, tmpParam, -1);
3024 break;
3025 }
3026 tmpParam = tmpParam->next;
3027 } while (tmpParam != NULL);
3028 }
3029 /*
3030 * Push the xsl:param.
3031 */
3032 if (tmpParam == NULL) {
3033 /*
3034 * Note that we must assume that the added parameter
3035 * has a @depth of 0.
3036 */
3037 xsltParseStylesheetParam(ctxt, cur);
3038 }
3039 cur = cur->next;
3040 } while (cur != NULL);
3041 /*
3042 * Process the sequence constructor.
3043 */
3044 xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
3045
3046 /*
3047 * Remove remaining xsl:param and xsl:with-param items from
3048 * the stack. Don't free xsl:with-param items.
3049 */
3050 if (ctxt->varsNr > ctxt->varsBase)
3051 xsltTemplateParamsCleanup(ctxt);
3052 ctxt->varsBase = oldVarsBase;
3053
3054 /*
3055 * Clean up remaining local tree fragments.
3056 * This also frees fragments which are the result of
3057 * extension instructions. Should normally not be hit; but
3058 * just for the case xsltExtensionInstructionResultFinalize()
3059 * was not called by the extension author.
3060 */
3061 if (oldLocalFragmentTop != ctxt->localRVT) {
3062 xmlDocPtr curdoc = ctxt->localRVT, tmp;
3063
3064 do {
3065 tmp = curdoc;
3066 curdoc = (xmlDocPtr) curdoc->next;
3067 /* Need to housekeep localRVTBase */
3068 if (tmp == ctxt->localRVTBase)
3069 ctxt->localRVTBase = curdoc;
3070 if (tmp->prev)
3071 tmp->prev->next = (xmlNodePtr) curdoc;
3072 if (curdoc)
3073 curdoc->prev = tmp->prev;
3074 xsltReleaseRVT(ctxt, tmp);
3075 } while (curdoc != oldLocalFragmentTop);
3076 }
3077 ctxt->localRVT = oldLocalFragmentTop;
3078
3079 /*
3080 * Release user-created fragments stored in the scope
3081 * of xsl:template. Note that this mechanism is deprecated:
3082 * user code should now use xsltRegisterLocalRVT() instead
3083 * of the obsolete xsltRegisterTmpRVT().
3084 */
3085 if (ctxt->tmpRVT) {
3086 xmlDocPtr curdoc = ctxt->tmpRVT, tmp;
3087
3088 while (curdoc != NULL) {
3089 tmp = curdoc;
3090 curdoc = (xmlDocPtr) curdoc->next;
3091 xsltReleaseRVT(ctxt, tmp);
3092 }
3093 }
3094 ctxt->tmpRVT = oldUserFragmentTop;
3095
3096 /*
3097 * Pop the xsl:template declaration from the stack.
3098 */
3099 templPop(ctxt);
3100 if (ctxt->profile) {
3101 long spent, child, total, end;
3102
3103 end = xsltTimestamp();
3104 child = profPop(ctxt);
3105 total = end - start;
3106 spent = total - child;
3107 if (spent <= 0) {
3108 /*
3109 * Not possible unless the original calibration failed
3110 * we can try to correct it on the fly.
3111 */
3112 xsltCalibrateAdjust(spent);
3113 spent = 0;
3114 }
3115
3116 templ->time += spent;
3117 if (ctxt->profNr > 0)
3118 ctxt->profTab[ctxt->profNr - 1] += total;
3119 }
3120
3121 #ifdef WITH_DEBUGGER
3122 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) {
3123 xslDropCall();
3124 }
3125 #endif
3126 }
3127
3128
3129 /**
3130 * xsltApplyOneTemplate:
3131 * @ctxt: a XSLT process context
3132 * @contextNode: the node in the source tree.
3133 * @list: the nodes of a sequence constructor
3134 * @templ: not used
3135 * @params: a set of parameters (xsl:param) or NULL
3136 *
3137 * Processes a sequence constructor on the current node in the source tree.
3138 *
3139 * @params are the already computed variable stack items; this function
3140 * pushes them on the variable stack, and pops them before exiting; it's
3141 * left to the caller to free or reuse @params afterwards. The initial
3142 * states of the variable stack will always be restored before this
3143 * function exits.
3144 * NOTE that this does *not* initiate a new distinct variable scope; i.e.
3145 * variables already on the stack are visible to the process. The caller's
3146 * side needs to start a new variable scope if needed (e.g. in exsl:function).
3147 *
3148 * @templ is obsolete and not used anymore (e.g. <exslt:function> does not
3149 * provide a @templ); a non-NULL @templ might raise an error in the future.
3150 *
3151 * BIG NOTE: This function is not intended to process the content of an
3152 * xsl:template; it does not expect xsl:param instructions in @list and
3153 * will report errors if found.
3154 *
3155 * Called by:
3156 * - xsltEvalVariable() (variables.c)
3157 * - exsltFuncFunctionFunction() (libexsl/functions.c)
3158 */
3159 void
3160 xsltApplyOneTemplate(xsltTransformContextPtr ctxt,
3161 xmlNodePtr contextNode,
3162 xmlNodePtr list,
3163 xsltTemplatePtr templ ATTRIBUTE_UNUSED,
3164 xsltStackElemPtr params)
3165 {
3166 if ((ctxt == NULL) || (list == NULL))
3167 return;
3168 CHECK_STOPPED;
3169
3170 if (params) {
3171 /*
3172 * This code should be obsolete - was previously used
3173 * by libexslt/functions.c, but due to bug 381319 the
3174 * logic there was changed.
3175 */
3176 int oldVarsNr = ctxt->varsNr;
3177
3178 /*
3179 * Push the given xsl:param(s) onto the variable stack.
3180 */
3181 while (params != NULL) {
3182 xsltLocalVariablePush(ctxt, params, -1);
3183 params = params->next;
3184 }
3185 xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
3186 /*
3187 * Pop the given xsl:param(s) from the stack but don't free them.
3188 */
3189 xsltLocalVariablePop(ctxt, oldVarsNr, -2);
3190 } else
3191 xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
3192 }
3193
3194 /************************************************************************
3195 * *
3196 * XSLT-1.1 extensions *
3197 * *
3198 ************************************************************************/
3199
3200 /**
3201 * xsltDocumentElem:
3202 * @ctxt: an XSLT processing context
3203 * @node: The current node
3204 * @inst: the instruction in the stylesheet
3205 * @castedComp: precomputed information
3206 *
3207 * Process an EXSLT/XSLT-1.1 document element
3208 */
3209 void
3210 xsltDocumentElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
3211 xmlNodePtr inst, xsltStylePreCompPtr castedComp)
3212 {
3213 #ifdef XSLT_REFACTORED
3214 xsltStyleItemDocumentPtr comp = (xsltStyleItemDocumentPtr) castedComp;
3215 #else
3216 xsltStylePreCompPtr comp = castedComp;
3217 #endif
3218 xsltStylesheetPtr style = NULL;
3219 int ret;
3220 xmlChar *filename = NULL, *prop, *elements;
3221 xmlChar *element, *end;
3222 xmlDocPtr res = NULL;
3223 xmlDocPtr oldOutput;
3224 xmlNodePtr oldInsert, root;
3225 const char *oldOutputFile;
3226 xsltOutputType oldType;
3227 xmlChar *URL = NULL;
3228 const xmlChar *method;
3229 const xmlChar *doctypePublic;
3230 const xmlChar *doctypeSystem;
3231 const xmlChar *version;
3232 const xmlChar *encoding;
3233
3234 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
3235 return;
3236
3237 if (comp->filename == NULL) {
3238
3239 if (xmlStrEqual(inst->name, (const xmlChar *) "output")) {
3240 /*
3241 * The element "output" is in the namespace XSLT_SAXON_NAMESPACE
3242 * (http://icl.com/saxon)
3243 * The @file is in no namespace.
3244 */
3245 #ifdef WITH_XSLT_DEBUG_EXTRA
3246 xsltGenericDebug(xsltGenericDebugContext,
3247 "Found saxon:output extension\n");
3248 #endif
3249 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3250 (const xmlChar *) "file",
3251 XSLT_SAXON_NAMESPACE);
3252
3253 if (URL == NULL)
3254 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3255 (const xmlChar *) "href",
3256 XSLT_SAXON_NAMESPACE);
3257 } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) {
3258 #ifdef WITH_XSLT_DEBUG_EXTRA
3259 xsltGenericDebug(xsltGenericDebugContext,
3260 "Found xalan:write extension\n");
3261 #endif
3262 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3263 (const xmlChar *)
3264 "select",
3265 XSLT_XALAN_NAMESPACE);
3266 if (URL != NULL) {
3267 xmlXPathCompExprPtr cmp;
3268 xmlChar *val;
3269
3270 /*
3271 * Trying to handle bug #59212
3272 * The value of the "select" attribute is an
3273 * XPath expression.
3274 * (see http://xml.apache.org/xalan-j/extensionslib.html#redirect)
3275 */
3276 cmp = xmlXPathCompile(URL);
3277 val = xsltEvalXPathString(ctxt, cmp);
3278 xmlXPathFreeCompExpr(cmp);
3279 xmlFree(URL);
3280 URL = val;
3281 }
3282 if (URL == NULL)
3283 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3284 (const xmlChar *)
3285 "file",
3286 XSLT_XALAN_NAMESPACE);
3287 if (URL == NULL)
3288 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3289 (const xmlChar *)
3290 "href",
3291 XSLT_XALAN_NAMESPACE);
3292 } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) {
3293 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3294 (const xmlChar *) "href",
3295 NULL);
3296 }
3297
3298 } else {
3299 URL = xmlStrdup(comp->filename);
3300 }
3301
3302 if (URL == NULL) {
3303 xsltTransformError(ctxt, NULL, inst,
3304 "xsltDocumentElem: href/URI-Reference not found\n");
3305 return;
3306 }
3307
3308 /*
3309 * If the computation failed, it's likely that the URL wasn't escaped
3310 */
3311 filename = xmlBuildURI(URL, (const xmlChar *) ctxt->outputFile);
3312 if (filename == NULL) {
3313 xmlChar *escURL;
3314
3315 escURL=xmlURIEscapeStr(URL, BAD_CAST ":/.?,");
3316 if (escURL != NULL) {
3317 filename = xmlBuildURI(escURL, (const xmlChar *) ctxt->outputFile);
3318 xmlFree(escURL);
3319 }
3320 }
3321
3322 if (filename == NULL) {
3323 xsltTransformError(ctxt, NULL, inst,
3324 "xsltDocumentElem: URL computation failed for %s\n",
3325 URL);
3326 xmlFree(URL);
3327 return;
3328 }
3329
3330 /*
3331 * Security checking: can we write to this resource
3332 */
3333 if (ctxt->sec != NULL) {
3334 ret = xsltCheckWrite(ctxt->sec, ctxt, filename);
3335 if (ret == 0) {
3336 xsltTransformError(ctxt, NULL, inst,
3337 "xsltDocumentElem: write rights for %s denied\n",
3338 filename);
3339 xmlFree(URL);
3340 xmlFree(filename);
3341 return;
3342 }
3343 }
3344
3345 oldOutputFile = ctxt->outputFile;
3346 oldOutput = ctxt->output;
3347 oldInsert = ctxt->insert;
3348 oldType = ctxt->type;
3349 ctxt->outputFile = (const char *) filename;
3350
3351 style = xsltNewStylesheet();
3352 if (style == NULL) {
3353 xsltTransformError(ctxt, NULL, inst,
3354 "xsltDocumentElem: out of memory\n");
3355 goto error;
3356 }
3357
3358 /*
3359 * Version described in 1.1 draft allows full parameterization
3360 * of the output.
3361 */
3362 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3363 (const xmlChar *) "version",
3364 NULL);
3365 if (prop != NULL) {
3366 if (style->version != NULL)
3367 xmlFree(style->version);
3368 style->version = prop;
3369 }
3370 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3371 (const xmlChar *) "encoding",
3372 NULL);
3373 if (prop != NULL) {
3374 if (style->encoding != NULL)
3375 xmlFree(style->encoding);
3376 style->encoding = prop;
3377 }
3378 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3379 (const xmlChar *) "method",
3380 NULL);
3381 if (prop != NULL) {
3382 const xmlChar *URI;
3383
3384 if (style->method != NULL)
3385 xmlFree(style->method);
3386 style->method = NULL;
3387 if (style->methodURI != NULL)
3388 xmlFree(style->methodURI);
3389 style->methodURI = NULL;
3390
3391 URI = xsltGetQNameURI(inst, &prop);
3392 if (prop == NULL) {
3393 if (style != NULL) style->errors++;
3394 } else if (URI == NULL) {
3395 if ((xmlStrEqual(prop, (const xmlChar *) "xml")) ||
3396 (xmlStrEqual(prop, (const xmlChar *) "html")) ||
3397 (xmlStrEqual(prop, (const xmlChar *) "text"))) {
3398 style->method = prop;
3399 } else {
3400 xsltTransformError(ctxt, NULL, inst,
3401 "invalid value for method: %s\n", prop);
3402 if (style != NULL) style->warnings++;
3403 }
3404 } else {
3405 style->method = prop;
3406 style->methodURI = xmlStrdup(URI);
3407 }
3408 }
3409 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3410 (const xmlChar *)
3411 "doctype-system", NULL);
3412 if (prop != NULL) {
3413 if (style->doctypeSystem != NULL)
3414 xmlFree(style->doctypeSystem);
3415 style->doctypeSystem = prop;
3416 }
3417 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3418 (const xmlChar *)
3419 "doctype-public", NULL);
3420 if (prop != NULL) {
3421 if (style->doctypePublic != NULL)
3422 xmlFree(style->doctypePublic);
3423 style->doctypePublic = prop;
3424 }
3425 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3426 (const xmlChar *) "standalone",
3427 NULL);
3428 if (prop != NULL) {
3429 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
3430 style->standalone = 1;
3431 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
3432 style->standalone = 0;
3433 } else {
3434 xsltTransformError(ctxt, NULL, inst,
3435 "invalid value for standalone: %s\n",
3436 prop);
3437 if (style != NULL) style->warnings++;
3438 }
3439 xmlFree(prop);
3440 }
3441
3442 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3443 (const xmlChar *) "indent",
3444 NULL);
3445 if (prop != NULL) {
3446 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
3447 style->indent = 1;
3448 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
3449 style->indent = 0;
3450 } else {
3451 xsltTransformError(ctxt, NULL, inst,
3452 "invalid value for indent: %s\n", prop);
3453 if (style != NULL) style->warnings++;
3454 }
3455 xmlFree(prop);
3456 }
3457
3458 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3459 (const xmlChar *)
3460 "omit-xml-declaration",
3461 NULL);
3462 if (prop != NULL) {
3463 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
3464 style->omitXmlDeclaration = 1;
3465 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
3466 style->omitXmlDeclaration = 0;
3467 } else {
3468 xsltTransformError(ctxt, NULL, inst,
3469 "invalid value for omit-xml-declaration: %s\n",
3470 prop);
3471 if (style != NULL) style->warnings++;
3472 }
3473 xmlFree(prop);
3474 }
3475
3476 elements = xsltEvalAttrValueTemplate(ctxt, inst,
3477 (const xmlChar *)
3478 "cdata-section-elements",
3479 NULL);
3480 if (elements != NULL) {
3481 if (style->stripSpaces == NULL)
3482 style->stripSpaces = xmlHashCreate(10);
3483 if (style->stripSpaces == NULL)
3484 return;
3485
3486 element = elements;
3487 while (*element != 0) {
3488 while (IS_BLANK_CH(*element))
3489 element++;
3490 if (*element == 0)
3491 break;
3492 end = element;
3493 while ((*end != 0) && (!IS_BLANK_CH(*end)))
3494 end++;
3495 element = xmlStrndup(element, end - element);
3496 if (element) {
3497 const xmlChar *URI;
3498
3499 #ifdef WITH_XSLT_DEBUG_PARSING
3500 xsltGenericDebug(xsltGenericDebugContext,
3501 "add cdata section output element %s\n",
3502 element);
3503 #endif
3504 URI = xsltGetQNameURI(inst, &element);
3505
3506 xmlHashAddEntry2(style->stripSpaces, element, URI,
3507 (xmlChar *) "cdata");
3508 xmlFree(element);
3509 }
3510 element = end;
3511 }
3512 xmlFree(elements);
3513 }
3514
3515 /*
3516 * Create a new document tree and process the element template
3517 */
3518 XSLT_GET_IMPORT_PTR(method, style, method)
3519 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
3520 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
3521 XSLT_GET_IMPORT_PTR(version, style, version)
3522 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
3523
3524 if ((method != NULL) &&
3525 (!xmlStrEqual(method, (const xmlChar *) "xml"))) {
3526 if (xmlStrEqual(method, (const xmlChar *) "html")) {
3527 ctxt->type = XSLT_OUTPUT_HTML;
3528 if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
3529 res = htmlNewDoc(doctypeSystem, doctypePublic);
3530 else {
3531 if (version != NULL) {
3532 #ifdef XSLT_GENERATE_HTML_DOCTYPE
3533 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
3534 #endif
3535 }
3536 res = htmlNewDocNoDtD(doctypeSystem, doctypePublic);
3537 }
3538 if (res == NULL)
3539 goto error;
3540 res->dict = ctxt->dict;
3541 xmlDictReference(res->dict);
3542 } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
3543 xsltTransformError(ctxt, NULL, inst,
3544 "xsltDocumentElem: unsupported method xhtml\n",
3545 style->method);
3546 ctxt->type = XSLT_OUTPUT_HTML;
3547 res = htmlNewDocNoDtD(doctypeSystem, doctypePublic);
3548 if (res == NULL)
3549 goto error;
3550 res->dict = ctxt->dict;
3551 xmlDictReference(res->dict);
3552 } else if (xmlStrEqual(method, (const xmlChar *) "text")) {
3553 ctxt->type = XSLT_OUTPUT_TEXT;
3554 res = xmlNewDoc(style->version);
3555 if (res == NULL)
3556 goto error;
3557 res->dict = ctxt->dict;
3558 xmlDictReference(res->dict);
3559 #ifdef WITH_XSLT_DEBUG
3560 xsltGenericDebug(xsltGenericDebugContext,
3561 "reusing transformation dict for output\n");
3562 #endif
3563 } else {
3564 xsltTransformError(ctxt, NULL, inst,
3565 "xsltDocumentElem: unsupported method %s\n",
3566 style->method);
3567 goto error;
3568 }
3569 } else {
3570 ctxt->type = XSLT_OUTPUT_XML;
3571 res = xmlNewDoc(style->version);
3572 if (res == NULL)
3573 goto error;
3574 res->dict = ctxt->dict;
3575 xmlDictReference(res->dict);
3576 #ifdef WITH_XSLT_DEBUG
3577 xsltGenericDebug(xsltGenericDebugContext,
3578 "reusing transformation dict for output\n");
3579 #endif
3580 }
3581 res->charset = XML_CHAR_ENCODING_UTF8;
3582 if (encoding != NULL)
3583 res->encoding = xmlStrdup(encoding);
3584 ctxt->output = res;
3585 ctxt->insert = (xmlNodePtr) res;
3586 xsltApplySequenceConstructor(ctxt, node, inst->children, NULL);
3587
3588 /*
3589 * Do some post processing work depending on the generated output
3590 */
3591 root = xmlDocGetRootElement(res);
3592 if (root != NULL) {
3593 const xmlChar *doctype = NULL;
3594
3595 if ((root->ns != NULL) && (root->ns->prefix != NULL))
3596 doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name);
3597 if (doctype == NULL)
3598 doctype = root->name;
3599
3600 /*
3601 * Apply the default selection of the method
3602 */
3603 if ((method == NULL) &&
3604 (root->ns == NULL) &&
3605 (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) {
3606 xmlNodePtr tmp;
3607
3608 tmp = res->children;
3609 while ((tmp != NULL) && (tmp != root)) {
3610 if (tmp->type == XML_ELEMENT_NODE)
3611 break;
3612 if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp)))
3613 break;
3614 tmp = tmp->next;
3615 }
3616 if (tmp == root) {
3617 ctxt->type = XSLT_OUTPUT_HTML;
3618 res->type = XML_HTML_DOCUMENT_NODE;
3619 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
3620 res->intSubset = xmlCreateIntSubset(res, doctype,
3621 doctypePublic,
3622 doctypeSystem);
3623 #ifdef XSLT_GENERATE_HTML_DOCTYPE
3624 } else if (version != NULL) {
3625 xsltGetHTMLIDs(version, &doctypePublic,
3626 &doctypeSystem);
3627 if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
3628 res->intSubset =
3629 xmlCreateIntSubset(res, doctype,
3630 doctypePublic,
3631 doctypeSystem);
3632 #endif
3633 }
3634 }
3635
3636 }
3637 if (ctxt->type == XSLT_OUTPUT_XML) {
3638 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
3639 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
3640 if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
3641 res->intSubset = xmlCreateIntSubset(res, doctype,
3642 doctypePublic,
3643 doctypeSystem);
3644 }
3645 }
3646
3647 /*
3648 * Save the result
3649 */
3650 ret = xsltSaveResultToFilename((const char *) filename,
3651 res, style, 0);
3652 if (ret < 0) {
3653 xsltTransformError(ctxt, NULL, inst,
3654 "xsltDocumentElem: unable to save to %s\n",
3655 filename);
3656 ctxt->state = XSLT_STATE_ERROR;
3657 #ifdef WITH_XSLT_DEBUG_EXTRA
3658 } else {
3659 xsltGenericDebug(xsltGenericDebugContext,
3660 "Wrote %d bytes to %s\n", ret, filename);
3661 #endif
3662 }
3663
3664 error:
3665 ctxt->output = oldOutput;
3666 ctxt->insert = oldInsert;
3667 ctxt->type = oldType;
3668 ctxt->outputFile = oldOutputFile;
3669 if (URL != NULL)
3670 xmlFree(URL);
3671 if (filename != NULL)
3672 xmlFree(filename);
3673 if (style != NULL)
3674 xsltFreeStylesheet(style);
3675 if (res != NULL)
3676 xmlFreeDoc(res);
3677 }
3678
3679 /************************************************************************
3680 * *
3681 * Most of the XSLT-1.0 transformations *
3682 * *
3683 ************************************************************************/
3684
3685 /**
3686 * xsltSort:
3687 * @ctxt: a XSLT process context
3688 * @node: the node in the source tree.
3689 * @inst: the xslt sort node
3690 * @comp: precomputed information
3691 *
3692 * function attached to xsl:sort nodes, but this should not be
3693 * called directly
3694 */
3695 void
3696 xsltSort(xsltTransformContextPtr ctxt,
3697 xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst,
3698 xsltStylePreCompPtr comp) {
3699 if (comp == NULL) {
3700 xsltTransformError(ctxt, NULL, inst,
3701 "xsl:sort : compilation failed\n");
3702 return;
3703 }
3704 xsltTransformError(ctxt, NULL, inst,
3705 "xsl:sort : improper use this should not be reached\n");
3706 }
3707
3708 /**
3709 * xsltCopy:
3710 * @ctxt: an XSLT process context
3711 * @node: the node in the source tree
3712 * @inst: the element node of the XSLT-copy instruction
3713 * @castedComp: computed information of the XSLT-copy instruction
3714 *
3715 * Execute the XSLT-copy instruction on the source node.
3716 */
3717 void
3718 xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node,
3719 xmlNodePtr inst, xsltStylePreCompPtr castedComp)
3720 {
3721 #ifdef XSLT_REFACTORED
3722 xsltStyleItemCopyPtr comp = (xsltStyleItemCopyPtr) castedComp;
3723 #else
3724 xsltStylePreCompPtr comp = castedComp;
3725 #endif
3726 xmlNodePtr copy, oldInsert;
3727
3728 oldInsert = ctxt->insert;
3729 if (ctxt->insert != NULL) {
3730 switch (node->type) {
3731 case XML_TEXT_NODE:
3732 case XML_CDATA_SECTION_NODE:
3733 /*
3734 * This text comes from the stylesheet
3735 * For stylesheets, the set of whitespace-preserving
3736 * element names consists of just xsl:text.
3737 */
3738 #ifdef WITH_XSLT_DEBUG_PROCESS
3739 if (node->type == XML_CDATA_SECTION_NODE) {
3740 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
3741 "xsltCopy: CDATA text %s\n", node->content));
3742 } else {
3743 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
3744 "xsltCopy: text %s\n", node->content));
3745 }
3746 #endif
3747 xsltCopyText(ctxt, ctxt->insert, node, 0);
3748 break;
3749 case XML_DOCUMENT_NODE:
3750 case XML_HTML_DOCUMENT_NODE:
3751 break;
3752 case XML_ELEMENT_NODE:
3753 /*
3754 * REVISIT NOTE: The "fake" is a doc-node, not an element node.
3755 * REMOVED:
3756 * if (xmlStrEqual(node->name, BAD_CAST " fake node libxslt"))
3757 * return;
3758 */
3759
3760 #ifdef WITH_XSLT_DEBUG_PROCESS
3761 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
3762 "xsltCopy: node %s\n", node->name));
3763 #endif
3764 copy = xsltShallowCopyElem(ctxt, node, ctxt->insert, 0);
3765 ctxt->insert = copy;
3766 if (comp->use != NULL) {
3767 xsltApplyAttributeSet(ctxt, node, inst, comp->use);
3768 }
3769 break;
3770 case XML_ATTRIBUTE_NODE: {
3771 #ifdef WITH_XSLT_DEBUG_PROCESS
3772 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
3773 "xsltCopy: attribute %s\n", node->name));
3774 #endif
3775 /*
3776 * REVISIT: We could also raise an error if the parent is not
3777 * an element node.
3778 * OPTIMIZE TODO: Can we set the value/children of the
3779 * attribute without an intermediate copy of the string value?
3780 */
3781 xsltShallowCopyAttr(ctxt, inst, ctxt->insert, (xmlAttrPtr) node);
3782 break;
3783 }
3784 case XML_PI_NODE:
3785 #ifdef WITH_XSLT_DEBUG_PROCESS
3786 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
3787 "xsltCopy: PI %s\n", node->name));
3788 #endif
3789 copy = xmlNewDocPI(ctxt->insert->doc, node->name,
3790 node->content);
3791 copy = xsltAddChild(ctxt->insert, copy);
3792 break;
3793 case XML_COMMENT_NODE:
3794 #ifdef WITH_XSLT_DEBUG_PROCESS
3795 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
3796 "xsltCopy: comment\n"));
3797 #endif
3798 copy = xmlNewComment(node->content);
3799 copy = xsltAddChild(ctxt->insert, copy);
3800 break;
3801 case XML_NAMESPACE_DECL:
3802 #ifdef WITH_XSLT_DEBUG_PROCESS
3803 XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
3804 "xsltCopy: namespace declaration\n"));
3805 #endif
3806 xsltShallowCopyNsNode(ctxt, inst, ctxt->insert, (xmlNsPtr)node);
3807 break;
3808 default:
3809 break;
3810
3811 }
3812 }
3813
3814 switch (node->type) {
3815 case XML_DOCUMENT_NODE:
3816 case XML_HTML_DOCUMENT_NODE:
3817 case XML_ELEMENT_NODE:
3818 xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children,
3819 NULL);
3820 break;
3821 default:
3822 break;
3823 }
3824 ctxt->insert = oldInsert;
3825 }
3826
3827 /**
3828 * xsltText:
3829 * @ctxt: a XSLT process context
3830 * @node: the node in the source tree.
3831 * @inst: the xslt text node
3832 * @comp: precomputed information
3833 *
3834 * Process the xslt text node on the source node
3835 */
3836 void
3837 xsltText(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED,
3838 xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
3839 if ((inst->children != NULL) && (comp != NULL)) {
3840 xmlNodePtr text = inst->children;
3841 xmlNodePtr copy;
3842
3843 while (text != NULL) {
3844 if ((text->type != XML_TEXT_NODE) &&
3845 (text->type != XML_CDATA_SECTION_NODE)) {
3846 xsltTransformError(ctxt, NULL, inst,
3847 "xsl:text content problem\n");
3848 break;
3849 }
3850 copy = xmlNewDocText(ctxt->output, text->content);
3851 if (text->type != XML_CDATA_SECTION_NODE) {
3852 #ifdef WITH_XSLT_DEBUG_PARSING
3853 xsltGenericDebug(xsltGenericDebugContext,
3854 "Disable escaping: %s\n", text->content);
3855 #endif
3856 copy->name = xmlStringTextNoenc;
3857 }
3858 copy = xsltAddChild(ctxt->insert, copy);
3859 text = text->next;
3860 }
3861 }
3862 }
3863
3864 /**
3865 * xsltElement:
3866 * @ctxt: a XSLT process context
3867 * @node: the node in the source tree.
3868 * @inst: the xslt element node
3869 * @castedComp: precomputed information
3870 *
3871 * Process the xslt element node on the source node
3872 */
3873 void
3874 xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node,
3875 xmlNodePtr inst, xsltStylePreCompPtr castedComp) {
3876 #ifdef XSLT_REFACTORED
3877 xsltStyleItemElementPtr comp = (xsltStyleItemElementPtr) castedComp;
3878 #else
3879 xsltStylePreCompPtr comp = castedComp;
3880 #endif
3881 xmlChar *prop = NULL;
3882 const xmlChar *name, *prefix = NULL, *nsName = NULL;
3883 xmlNodePtr copy;
3884 xmlNodePtr oldInsert;
3885
3886 if (ctxt->insert == NULL)
3887 return;
3888
3889 /*
3890 * A comp->has_name == 0 indicates that we need to skip this instruction,
3891 * since it was evaluated to be invalid already during compilation.
3892 */
3893 if (!comp->has_name)
3894 return;
3895
3896 /*
3897 * stack and saves
3898 */
3899 oldInsert = ctxt->insert;
3900
3901 if (comp->name == NULL) {
3902 /* TODO: fix attr acquisition wrt to the XSLT namespace */
3903 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3904 (const xmlChar *) "name", XSLT_NAMESPACE);
3905 if (prop == NULL) {
3906 xsltTransformError(ctxt, NULL, inst,
3907 "xsl:element: The attribute 'name' is missing.\n");
3908 goto error;
3909 }
3910 if (xmlValidateQName(prop, 0)) {
3911 xsltTransformError(ctxt, NULL, inst,
3912 "xsl:element: The effective name '%s' is not a "
3913 "valid QName.\n", prop);
3914 /* we fall through to catch any further errors, if possible */
3915 }
3916 name = xsltSplitQName(ctxt->dict, prop, &prefix);
3917 xmlFree(prop);
3918 if ((prefix != NULL) &&
3919 (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3)))
3920 {
3921 /*
3922 * TODO: Should we really disallow an "xml" prefix?
3923 */
3924 goto error;
3925 }
3926 } else {
3927 /*
3928 * The "name" value was static.
3929 */
3930 #ifdef XSLT_REFACTORED
3931 prefix = comp->nsPrefix;
3932 name = comp->name;
3933 #else
3934 name = xsltSplitQName(ctxt->dict, comp->name, &prefix);
3935 #endif
3936 }
3937
3938 /*
3939 * Create the new element
3940 */
3941 if (ctxt->output->dict == ctxt->dict) {
3942 copy = xmlNewDocNodeEatName(ctxt->output, NULL, (xmlChar *)name, NULL);
3943 } else {
3944 copy = xmlNewDocNode(ctxt->output, NULL, (xmlChar *)name, NULL);
3945 }
3946 if (copy == NULL) {
3947 xsltTransformError(ctxt, NULL, inst,
3948 "xsl:element : creation of %s failed\n", name);
3949 return;
3950 }
3951 copy = xsltAddChild(ctxt->insert, copy);
3952
3953 /*
3954 * Namespace
3955 * ---------
3956 */
3957 if (comp->has_ns) {
3958 if (comp->ns != NULL) {
3959 /*
3960 * No AVT; just plain text for the namespace name.
3961 */
3962 if (comp->ns[0] != 0)
3963 nsName = comp->ns;
3964 } else {
3965 xmlChar *tmpNsName;
3966 /*
3967 * Eval the AVT.
3968 */
3969 /* TODO: check attr acquisition wrt to the XSLT namespace */
3970 tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst,
3971 (const xmlChar *) "namespace", XSLT_NAMESPACE);
3972 /*
3973 * SPEC XSLT 1.0:
3974 * "If the string is empty, then the expanded-name of the
3975 * attribute has a null namespace URI."
3976 */
3977 if ((tmpNsName != NULL) && (tmpNsName[0] != 0))
3978 nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1);
3979 xmlFree(tmpNsName);
3980 };
3981 } else {
3982 xmlNsPtr ns;
3983 /*
3984 * SPEC XSLT 1.0:
3985 * "If the namespace attribute is not present, then the QName is
3986 * expanded into an expanded-name using the namespace declarations
3987 * in effect for the xsl:element element, including any default
3988 * namespace declaration.
3989 */
3990 ns = xmlSearchNs(inst->doc, inst, prefix);
3991 if (ns == NULL) {
3992 /*
3993 * TODO: Check this in the compilation layer in case it's a
3994 * static value.
3995 */
3996 if (prefix != NULL) {
3997 xsltTransformError(ctxt, NULL, inst,
3998 "xsl:element: The QName '%s:%s' has no "
3999 "namespace binding in scope in the stylesheet; "
4000 "this is an error, since the namespace was not "
4001 "specified by the instruction itself.\n", prefix, name);
4002 }
4003 } else
4004 nsName = ns->href;
4005 }
4006 /*
4007 * Find/create a matching ns-decl in the result tree.
4008 */
4009 if (nsName != NULL) {
4010 copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix, copy);
4011 } else if ((copy->parent != NULL) &&
4012 (copy->parent->type == XML_ELEMENT_NODE) &&
4013 (copy->parent->ns != NULL))
4014 {
4015 /*
4016 * "Undeclare" the default namespace.
4017 */
4018 xsltGetSpecialNamespace(ctxt, inst, NULL, NULL, copy);
4019 }
4020
4021 ctxt->insert = copy;
4022
4023 if (comp->has_use) {
4024 if (comp->use != NULL) {
4025 xsltApplyAttributeSet(ctxt, node, inst, comp->use);
4026 } else {
4027 xmlChar *attrSets = NULL;
4028 /*
4029 * BUG TODO: use-attribute-sets is not a value template.
4030 * use-attribute-sets = qnames
4031 */
4032 attrSets = xsltEvalAttrValueTemplate(ctxt, inst,
4033 (const xmlChar *)"use-attribute-sets", NULL);
4034 if (attrSets != NULL) {
4035 xsltApplyAttributeSet(ctxt, node, inst, attrSets);
4036 xmlFree(attrSets);
4037 }
4038 }
4039 }
4040 /*
4041 * Instantiate the sequence constructor.
4042 */
4043 if (inst->children != NULL)
4044 xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children,
4045 NULL);
4046
4047 error:
4048 ctxt->insert = oldInsert;
4049 return;
4050 }
4051
4052
4053 /**
4054 * xsltComment:
4055 * @ctxt: a XSLT process context
4056 * @node: the node in the source tree.
4057 * @inst: the xslt comment node
4058 * @comp: precomputed information
4059 *
4060 * Process the xslt comment node on the source node
4061 */
4062 void
4063 xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node,
4064 xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
4065 xmlChar *value = NULL;
4066 xmlNodePtr commentNode;
4067 int len;
4068
4069 value = xsltEvalTemplateString(ctxt, node, inst);
4070 /* TODO: use or generate the compiled form */
4071 len = xmlStrlen(value);
4072 if (len > 0) {
4073 if ((value[len-1] == '-') ||
4074 (xmlStrstr(value, BAD_CAST "--"))) {
4075 xsltTransformError(ctxt, NULL, inst,
4076 "xsl:comment : '--' or ending '-' not allowed in comment\n");
4077 /* fall through to try to catch further errors */
4078 }
4079 }
4080 #ifdef WITH_XSLT_DEBUG_PROCESS
4081 if (value == NULL) {
4082 XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext,
4083 "xsltComment: empty\n"));
4084 } else {
4085 XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext,
4086 "xsltComment: content %s\n", value));
4087 }
4088 #endif
4089
4090 commentNode = xmlNewComment(value);
4091 commentNode = xsltAddChild(ctxt->insert, commentNode);
4092
4093 if (value != NULL)
4094 xmlFree(value);
4095 }
4096
4097 /**
4098 * xsltProcessingInstruction:
4099 * @ctxt: a XSLT process context
4100 * @node: the node in the source tree.
4101 * @inst: the xslt processing-instruction node
4102 * @castedComp: precomputed information
4103 *
4104 * Process the xslt processing-instruction node on the source node
4105 */
4106 void
4107 xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node,
4108 xmlNodePtr inst, xsltStylePreCompPtr castedComp) {
4109 #ifdef XSLT_REFACTORED
4110 xsltStyleItemPIPtr comp = (xsltStyleItemPIPtr) castedComp;
4111 #else
4112 xsltStylePreCompPtr comp = castedComp;
4113 #endif
4114 const xmlChar *name;
4115 xmlChar *value = NULL;
4116 xmlNodePtr pi;
4117
4118
4119 if (ctxt->insert == NULL)
4120 return;
4121 if (comp->has_name == 0)
4122 return;
4123 if (comp->name == NULL) {
4124 name = xsltEvalAttrValueTemplate(ctxt, inst,
4125 (const xmlChar *)"name", NULL);
4126 if (name == NULL) {
4127 xsltTransformError(ctxt, NULL, inst,
4128 "xsl:processing-instruction : name is missing\n");
4129 goto error;
4130 }
4131 } else {
4132 name = comp->name;
4133 }
4134 /* TODO: check that it's both an an NCName and a PITarget. */
4135
4136
4137 value = xsltEvalTemplateString(ctxt, node, inst);
4138 if (xmlStrstr(value, BAD_CAST "?>") != NULL) {
4139 xsltTransformError(ctxt, NULL, inst,
4140 "xsl:processing-instruction: '?>' not allowed within PI content\n");
4141 goto error;
4142 }
4143 #ifdef WITH_XSLT_DEBUG_PROCESS
4144 if (value == NULL) {
4145 XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext,
4146 "xsltProcessingInstruction: %s empty\n", name));
4147 } else {
4148 XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext,
4149 "xsltProcessingInstruction: %s content %s\n", name, value));
4150 }
4151 #endif
4152
4153 pi = xmlNewDocPI(ctxt->insert->doc, name, value);
4154 pi = xsltAddChild(ctxt->insert, pi);
4155
4156 error:
4157 if ((name != NULL) && (name != comp->name))
4158 xmlFree((xmlChar *) name);
4159 if (value != NULL)
4160 xmlFree(value);
4161 }
4162
4163 /**
4164 * xsltCopyOf:
4165 * @ctxt: an XSLT transformation context
4166 * @node: the current node in the source tree
4167 * @inst: the element node of the XSLT copy-of instruction
4168 * @castedComp: precomputed information of the XSLT copy-of instruction
4169 *
4170 * Process the XSLT copy-of instruction.
4171 */
4172 void
4173 xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
4174 xmlNodePtr inst, xsltStylePreCompPtr castedComp) {
4175 #ifdef XSLT_REFACTORED
4176 xsltStyleItemCopyOfPtr comp = (xsltStyleItemCopyOfPtr) castedComp;
4177 #else
4178 xsltStylePreCompPtr comp = castedComp;
4179 #endif
4180 xmlXPathObjectPtr res = NULL;
4181 xmlNodeSetPtr list = NULL;
4182 int i;
4183 xmlDocPtr oldXPContextDoc;
4184 xmlNsPtr *oldXPNamespaces;
4185 xmlNodePtr oldXPContextNode;
4186 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
4187 xmlXPathContextPtr xpctxt;
4188
4189 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
4190 return;
4191 if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
4192 xsltTransformError(ctxt, NULL, inst,
4193 "xsl:copy-of : compilation failed\n");
4194 return;
4195 }
4196
4197 /*
4198 * SPEC XSLT 1.0:
4199 * "The xsl:copy-of element can be used to insert a result tree
4200 * fragment into the result tree, without first converting it to
4201 * a string as xsl:value-of does (see [7.6.1 Generating Text with
4202 * xsl:value-of]). The required select attribute contains an
4203 * expression. When the result of evaluating the expression is a
4204 * result tree fragment, the complete fragment is copied into the
4205 * result tree. When the result is a node-set, all the nodes in the
4206 * set are copied in document order into the result tree; copying
4207 * an element node copies the attribute nodes, namespace nodes and
4208 * children of the element node as well as the element node itself;
4209 * a root node is copied by copying its children. When the result
4210 * is neither a node-set nor a result tree fragment, the result is
4211 * converted to a string and then inserted into the result tree,
4212 * as with xsl:value-of.
4213 */
4214
4215 #ifdef WITH_XSLT_DEBUG_PROCESS
4216 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
4217 "xsltCopyOf: select %s\n", comp->select));
4218 #endif
4219
4220 /*
4221 * Evaluate the "select" expression.
4222 */
4223 xpctxt = ctxt->xpathCtxt;
4224 oldXPContextDoc = xpctxt->doc;
4225 oldXPContextNode = xpctxt->node;
4226 oldXPProximityPosition = xpctxt->proximityPosition;
4227 oldXPContextSize = xpctxt->contextSize;
4228 oldXPNsNr = xpctxt->nsNr;
4229 oldXPNamespaces = xpctxt->namespaces;
4230
4231 xpctxt->node = node;
4232 if (comp != NULL) {
4233
4234 #ifdef XSLT_REFACTORED
4235 if (comp->inScopeNs != NULL) {
4236 xpctxt->namespaces = comp->inScopeNs->list;
4237 xpctxt->nsNr = comp->inScopeNs->xpathNumber;
4238 } else {
4239 xpctxt->namespaces = NULL;
4240 xpctxt->nsNr = 0;
4241 }
4242 #else
4243 xpctxt->namespaces = comp->nsList;
4244 xpctxt->nsNr = comp->nsNr;
4245 #endif
4246 } else {
4247 xpctxt->namespaces = NULL;
4248 xpctxt->nsNr = 0;
4249 }
4250
4251 res = xmlXPathCompiledEval(comp->comp, xpctxt);
4252
4253 xpctxt->doc = oldXPContextDoc;
4254 xpctxt->node = oldXPContextNode;
4255 xpctxt->contextSize = oldXPContextSize;
4256 xpctxt->proximityPosition = oldXPProximityPosition;
4257 xpctxt->nsNr = oldXPNsNr;
4258 xpctxt->namespaces = oldXPNamespaces;
4259
4260 if (res != NULL) {
4261 if (res->type == XPATH_NODESET) {
4262 /*
4263 * Node-set
4264 * --------
4265 */
4266 #ifdef WITH_XSLT_DEBUG_PROCESS
4267 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
4268 "xsltCopyOf: result is a node set\n"));
4269 #endif
4270 list = res->nodesetval;
4271 if (list != NULL) {
4272 xmlNodePtr cur;
4273 /*
4274 * The list is already sorted in document order by XPath.
4275 * Append everything in this order under ctxt->insert.
4276 */
4277 for (i = 0;i < list->nodeNr;i++) {
4278 cur = list->nodeTab[i];
4279 if (cur == NULL)
4280 continue;
4281 if ((cur->type == XML_DOCUMENT_NODE) ||
4282 (cur->type == XML_HTML_DOCUMENT_NODE))
4283 {
4284 xsltCopyTreeList(ctxt, inst,
4285 cur->children, ctxt->insert, 0, 0);
4286 } else if (cur->type == XML_ATTRIBUTE_NODE) {
4287 xsltShallowCopyAttr(ctxt, inst,
4288 ctxt->insert, (xmlAttrPtr) cur);
4289 } else {
4290 xsltCopyTreeInternal(ctxt, inst,
4291 cur, ctxt->insert, 0, 0);
4292 }
4293 }
4294 }
4295 } else if (res->type == XPATH_XSLT_TREE) {
4296 /*
4297 * Result tree fragment
4298 * --------------------
4299 * E.g. via <xsl:variable ...><foo/></xsl:variable>
4300 * Note that the root node of such trees is an xmlDocPtr in Libxslt.
4301 */
4302 #ifdef WITH_XSLT_DEBUG_PROCESS
4303 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
4304 "xsltCopyOf: result is a result tree fragment\n"));
4305 #endif
4306 list = res->nodesetval;
4307 if ((list != NULL) && (list->nodeTab != NULL) &&
4308 (list->nodeTab[0] != NULL) &&
4309 (IS_XSLT_REAL_NODE(list->nodeTab[0])))
4310 {
4311 xsltCopyTreeList(ctxt, inst,
4312 list->nodeTab[0]->children, ctxt->insert, 0, 0);
4313 }
4314 } else {
4315 xmlChar *value = NULL;
4316 /*
4317 * Convert to a string.
4318 */
4319 value = xmlXPathCastToString(res);
4320 if (value == NULL) {
4321 xsltTransformError(ctxt, NULL, inst,
4322 "Internal error in xsltCopyOf(): "
4323 "failed to cast an XPath object to string.\n");
4324 ctxt->state = XSLT_STATE_STOPPED;
4325 } else {
4326 if (value[0] != 0) {
4327 /*
4328 * Append content as text node.
4329 */
4330 xsltCopyTextString(ctxt, ctxt->insert, value, 0);
4331 }
4332 xmlFree(value);
4333
4334 #ifdef WITH_XSLT_DEBUG_PROCESS
4335 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
4336 "xsltCopyOf: result %s\n", res->stringval));
4337 #endif
4338 }
4339 }
4340 } else {
4341 ctxt->state = XSLT_STATE_STOPPED;
4342 }
4343
4344 if (res != NULL)
4345 xmlXPathFreeObject(res);
4346 }
4347
4348 /**
4349 * xsltValueOf:
4350 * @ctxt: a XSLT process context
4351 * @node: the node in the source tree.
4352 * @inst: the xslt value-of node
4353 * @castedComp: precomputed information
4354 *
4355 * Process the xslt value-of node on the source node
4356 */
4357 void
4358 xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
4359 xmlNodePtr inst, xsltStylePreCompPtr castedComp)
4360 {
4361 #ifdef XSLT_REFACTORED
4362 xsltStyleItemValueOfPtr comp = (xsltStyleItemValueOfPtr) castedComp;
4363 #else
4364 xsltStylePreCompPtr comp = castedComp;
4365 #endif
4366 xmlXPathObjectPtr res = NULL;
4367 xmlNodePtr copy = NULL;
4368 xmlChar *value = NULL;
4369 xmlDocPtr oldXPContextDoc;
4370 xmlNsPtr *oldXPNamespaces;
4371 xmlNodePtr oldXPContextNode;
4372 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
4373 xmlXPathContextPtr xpctxt;
4374
4375 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
4376 return;
4377
4378 if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
4379 xsltTransformError(ctxt, NULL, inst,
4380 "Internal error in xsltValueOf(): "
4381 "The XSLT 'value-of' instruction was not compiled.\n");
4382 return;
4383 }
4384
4385 #ifdef WITH_XSLT_DEBUG_PROCESS
4386 XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext,
4387 "xsltValueOf: select %s\n", comp->select));
4388 #endif
4389
4390 xpctxt = ctxt->xpathCtxt;
4391 oldXPContextDoc = xpctxt->doc;
4392 oldXPContextNode = xpctxt->node;
4393 oldXPProximityPosition = xpctxt->proximityPosition;
4394 oldXPContextSize = xpctxt->contextSize;
4395 oldXPNsNr = xpctxt->nsNr;
4396 oldXPNamespaces = xpctxt->namespaces;
4397
4398 xpctxt->node = node;
4399 if (comp != NULL) {
4400
4401 #ifdef XSLT_REFACTORED
4402 if (comp->inScopeNs != NULL) {
4403 xpctxt->namespaces = comp->inScopeNs->list;
4404 xpctxt->nsNr = comp->inScopeNs->xpathNumber;
4405 } else {
4406 xpctxt->namespaces = NULL;
4407 xpctxt->nsNr = 0;
4408 }
4409 #else
4410 xpctxt->namespaces = comp->nsList;
4411 xpctxt->nsNr = comp->nsNr;
4412 #endif
4413 } else {
4414 xpctxt->namespaces = NULL;
4415 xpctxt->nsNr = 0;
4416 }
4417
4418 res = xmlXPathCompiledEval(comp->comp, xpctxt);
4419
4420 xpctxt->doc = oldXPContextDoc;
4421 xpctxt->node = oldXPContextNode;
4422 xpctxt->contextSize = oldXPContextSize;
4423 xpctxt->proximityPosition = oldXPProximityPosition;
4424 xpctxt->nsNr = oldXPNsNr;
4425 xpctxt->namespaces = oldXPNamespaces;
4426
4427 /*
4428 * Cast the XPath object to string.
4429 */
4430 if (res != NULL) {
4431 value = xmlXPathCastToString(res);
4432 if (value == NULL) {
4433 xsltTransformError(ctxt, NULL, inst,
4434 "Internal error in xsltValueOf(): "
4435 "failed to cast an XPath object to string.\n");
4436 ctxt->state = XSLT_STATE_STOPPED;
4437 goto error;
4438 }
4439 if (value[0] != 0) {
4440 copy = xsltCopyTextString(ctxt,
4441 ctxt->insert, value, comp->noescape);
4442 }
4443 } else {
4444 xsltTransformError(ctxt, NULL, inst,
4445 "XPath evaluation returned no result.\n");
4446 ctxt->state = XSLT_STATE_STOPPED;
4447 goto error;
4448 }
4449
4450 #ifdef WITH_XSLT_DEBUG_PROCESS
4451 if (value) {
4452 XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext,
4453 "xsltValueOf: result '%s'\n", value));
4454 }
4455 #endif
4456
4457 error:
4458 if (value != NULL)
4459 xmlFree(value);
4460 if (res != NULL)
4461 xmlXPathFreeObject(res);
4462 }
4463
4464 /**
4465 * xsltNumber:
4466 * @ctxt: a XSLT process context
4467 * @node: the node in the source tree.
4468 * @inst: the xslt number node
4469 * @castedComp: precomputed information
4470 *
4471 * Process the xslt number node on the source node
4472 */
4473 void
4474 xsltNumber(xsltTransformContextPtr ctxt, xmlNodePtr node,
4475 xmlNodePtr inst, xsltStylePreCompPtr castedComp)
4476 {
4477 #ifdef XSLT_REFACTORED
4478 xsltStyleItemNumberPtr comp = (xsltStyleItemNumberPtr) castedComp;
4479 #else
4480 xsltStylePreCompPtr comp = castedComp;
4481 #endif
4482 if (comp == NULL) {
4483 xsltTransformError(ctxt, NULL, inst,
4484 "xsl:number : compilation failed\n");
4485 return;
4486 }
4487
4488 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
4489 return;
4490
4491 comp->numdata.doc = inst->doc;
4492 comp->numdata.node = inst;
4493
4494 xsltNumberFormat(ctxt, &comp->numdata, node);
4495 }
4496
4497 /**
4498 * xsltApplyImports:
4499 * @ctxt: an XSLT transformation context
4500 * @contextNode: the current node in the source tree.
4501 * @inst: the element node of the XSLT 'apply-imports' instruction
4502 * @comp: the compiled instruction
4503 *
4504 * Process the XSLT apply-imports element.
4505 */
4506 void
4507 xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
4508 xmlNodePtr inst,
4509 xsltStylePreCompPtr comp ATTRIBUTE_UNUSED)
4510 {
4511 xsltTemplatePtr templ;
4512
4513 if ((ctxt == NULL) || (inst == NULL))
4514 return;
4515
4516 if (comp == NULL) {
4517 xsltTransformError(ctxt, NULL, inst,
4518 "Internal error in xsltApplyImports(): "
4519 "The XSLT 'apply-imports' instruction was not compiled.\n");
4520 return;
4521 }
4522 /*
4523 * NOTE that ctxt->currentTemplateRule and ctxt->templ is not the
4524 * same; the former is the "Current Template Rule" as defined by the
4525 * XSLT spec, the latter is simply the template struct being
4526 * currently processed.
4527 */
4528 if (ctxt->currentTemplateRule == NULL) {
4529 /*
4530 * SPEC XSLT 2.0:
4531 * "[ERR XTDE0560] It is a non-recoverable dynamic error if
4532 * xsl:apply-imports or xsl:next-match is evaluated when the
4533 * current template rule is null."
4534 */
4535 xsltTransformError(ctxt, NULL, inst,
4536 "It is an error to call 'apply-imports' "
4537 "when there's no current template rule.\n");
4538 return;
4539 }
4540 /*
4541 * TODO: Check if this is correct.
4542 */
4543 templ = xsltGetTemplate(ctxt, contextNode,
4544 ctxt->currentTemplateRule->style);
4545
4546 if (templ != NULL) {
4547 xsltTemplatePtr oldCurTemplRule = ctxt->currentTemplateRule;
4548 /*
4549 * Set the current template rule.
4550 */
4551 ctxt->currentTemplateRule = templ;
4552 /*
4553 * URGENT TODO: Need xsl:with-param be handled somehow here?
4554 */
4555 xsltApplyXSLTTemplate(ctxt, contextNode, templ->content,
4556 templ, NULL);
4557
4558 ctxt->currentTemplateRule = oldCurTemplRule;
4559 }
4560 }
4561
4562 /**
4563 * xsltCallTemplate:
4564 * @ctxt: a XSLT transformation context
4565 * @node: the "current node" in the source tree
4566 * @inst: the XSLT 'call-template' instruction
4567 * @castedComp: the compiled information of the instruction
4568 *
4569 * Processes the XSLT call-template instruction on the source node.
4570 */
4571 void
4572 xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
4573 xmlNodePtr inst, xsltStylePreCompPtr castedComp)
4574 {
4575 #ifdef XSLT_REFACTORED
4576 xsltStyleItemCallTemplatePtr comp =
4577 (xsltStyleItemCallTemplatePtr) castedComp;
4578 #else
4579 xsltStylePreCompPtr comp = castedComp;
4580 #endif
4581 xsltStackElemPtr withParams = NULL;
4582
4583 if (ctxt->insert == NULL)
4584 return;
4585 if (comp == NULL) {
4586 xsltTransformError(ctxt, NULL, inst,
4587 "The XSLT 'call-template' instruction was not compiled.\n");
4588 return;
4589 }
4590
4591 /*
4592 * The template must have been precomputed
4593 */
4594 if (comp->templ == NULL) {
4595 comp->templ = xsltFindTemplate(ctxt, comp->name, comp->ns);
4596 if (comp->templ == NULL) {
4597 if (comp->ns != NULL) {
4598 xsltTransformError(ctxt, NULL, inst,
4599 "The called template '{%s}%s' was not found.\n",
4600 comp->ns, comp->name);
4601 } else {
4602 xsltTransformError(ctxt, NULL, inst,
4603 "The called template '%s' was not found.\n",
4604 comp->name);
4605 }
4606 return;
4607 }
4608 }
4609
4610 #ifdef WITH_XSLT_DEBUG_PROCESS
4611 if ((comp != NULL) && (comp->name != NULL))
4612 XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
4613 "call-template: name %s\n", comp->name));
4614 #endif
4615
4616 if (inst->children) {
4617 xmlNodePtr cur;
4618 xsltStackElemPtr param;
4619
4620 cur = inst->children;
4621 while (cur != NULL) {
4622 #ifdef WITH_DEBUGGER
4623 if (ctxt->debugStatus != XSLT_DEBUG_NONE)
4624 xslHandleDebugger(cur, node, comp->templ, ctxt);
4625 #endif
4626 if (ctxt->state == XSLT_STATE_STOPPED) break;
4627 /*
4628 * TODO: The "with-param"s could be part of the "call-template"
4629 * structure. Avoid to "search" for params dynamically
4630 * in the XML tree every time.
4631 */
4632 if (IS_XSLT_ELEM(cur)) {
4633 if (IS_XSLT_NAME(cur, "with-param")) {
4634 param = xsltParseStylesheetCallerParam(ctxt, cur);
4635 if (param != NULL) {
4636 param->next = withParams;
4637 withParams = param;
4638 }
4639 } else {
4640 xsltGenericError(xsltGenericErrorContext,
4641 "xsl:call-template: misplaced xsl:%s\n", cur->name);
4642 }
4643 } else {
4644 xsltGenericError(xsltGenericErrorContext,
4645 "xsl:call-template: misplaced %s element\n", cur->name);
4646 }
4647 cur = cur->next;
4648 }
4649 }
4650 /*
4651 * Create a new frame using the params first
4652 */
4653 xsltApplyXSLTTemplate(ctxt, node, comp->templ->content, comp->templ,
4654 withParams);
4655 if (withParams != NULL)
4656 xsltFreeStackElemList(withParams);
4657
4658 #ifdef WITH_XSLT_DEBUG_PROCESS
4659 if ((comp != NULL) && (comp->name != NULL))
4660 XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
4661 "call-template returned: name %s\n", comp->name));
4662 #endif
4663 }
4664
4665 /**
4666 * xsltApplyTemplates:
4667 * @ctxt: a XSLT transformation context
4668 * @node: the 'current node' in the source tree
4669 * @inst: the element node of an XSLT 'apply-templates' instruction
4670 * @castedComp: the compiled instruction
4671 *
4672 * Processes the XSLT 'apply-templates' instruction on the current node.
4673 */
4674 void
4675 xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node,
4676 xmlNodePtr inst, xsltStylePreCompPtr castedComp)
4677 {
4678 #ifdef XSLT_REFACTORED
4679 xsltStyleItemApplyTemplatesPtr comp =
4680 (xsltStyleItemApplyTemplatesPtr) castedComp;
4681 #else
4682 xsltStylePreCompPtr comp = castedComp;
4683 #endif
4684 int i;
4685 xmlNodePtr cur, delNode = NULL, oldContextNode;
4686 xmlNodeSetPtr list = NULL, oldList;
4687 xsltStackElemPtr withParams = NULL;
4688 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
4689 const xmlChar *oldMode, *oldModeURI;
4690 xmlDocPtr oldXPDoc;
4691 xsltDocumentPtr oldDocInfo;
4692 xmlXPathContextPtr xpctxt;
4693 xmlNsPtr *oldXPNamespaces;
4694
4695 if (comp == NULL) {
4696 xsltTransformError(ctxt, NULL, inst,
4697 "xsl:apply-templates : compilation failed\n");
4698 return;
4699 }
4700 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
4701 return;
4702
4703 #ifdef WITH_XSLT_DEBUG_PROCESS
4704 if ((node != NULL) && (node->name != NULL))
4705 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
4706 "xsltApplyTemplates: node: '%s'\n", node->name));
4707 #endif
4708
4709 xpctxt = ctxt->xpathCtxt;
4710 /*
4711 * Save context states.
4712 */
4713 oldContextNode = ctxt->node;
4714 oldMode = ctxt->mode;
4715 oldModeURI = ctxt->modeURI;
4716 oldDocInfo = ctxt->document;
4717 oldList = ctxt->nodeList;
4718
4719 /*
4720 * The xpath context size and proximity position, as
4721 * well as the xpath and context documents, may be changed
4722 * so we save their initial state and will restore on exit
4723 */
4724 oldXPContextSize = xpctxt->contextSize;
4725 oldXPProximityPosition = xpctxt->proximityPosition;
4726 oldXPDoc = xpctxt->doc;
4727 oldXPNsNr = xpctxt->nsNr;
4728 oldXPNamespaces = xpctxt->namespaces;
4729
4730 /*
4731 * Set up contexts.
4732 */
4733 ctxt->mode = comp->mode;
4734 ctxt->modeURI = comp->modeURI;
4735
4736 if (comp->select != NULL) {
4737 xmlXPathObjectPtr res = NULL;
4738
4739 if (comp->comp == NULL) {
4740 xsltTransformError(ctxt, NULL, inst,
4741 "xsl:apply-templates : compilation failed\n");
4742 goto error;
4743 }
4744 #ifdef WITH_XSLT_DEBUG_PROCESS
4745 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
4746 "xsltApplyTemplates: select %s\n", comp->select));
4747 #endif
4748
4749 /*
4750 * Set up XPath.
4751 */
4752 xpctxt->node = node; /* Set the "context node" */
4753 #ifdef XSLT_REFACTORED
4754 if (comp->inScopeNs != NULL) {
4755 xpctxt->namespaces = comp->inScopeNs->list;
4756 xpctxt->nsNr = comp->inScopeNs->xpathNumber;
4757 } else {
4758 xpctxt->namespaces = NULL;
4759 xpctxt->nsNr = 0;
4760 }
4761 #else
4762 xpctxt->namespaces = comp->nsList;
4763 xpctxt->nsNr = comp->nsNr;
4764 #endif
4765 res = xmlXPathCompiledEval(comp->comp, xpctxt);
4766
4767 xpctxt->contextSize = oldXPContextSize;
4768 xpctxt->proximityPosition = oldXPProximityPosition;
4769 if (res != NULL) {
4770 if (res->type == XPATH_NODESET) {
4771 list = res->nodesetval; /* consume the node set */
4772 res->nodesetval = NULL;
4773 } else {
4774 xsltTransformError(ctxt, NULL, inst,
4775 "The 'select' expression did not evaluate to a "
4776 "node set.\n");
4777 ctxt->state = XSLT_STATE_STOPPED;
4778 xmlXPathFreeObject(res);
4779 goto error;
4780 }
4781 xmlXPathFreeObject(res);
4782 /*
4783 * Note: An xsl:apply-templates with a 'select' attribute,
4784 * can change the current source doc.
4785 */
4786 } else {
4787 xsltTransformError(ctxt, NULL, inst,
4788 "Failed to evaluate the 'select' expression.\n");
4789 ctxt->state = XSLT_STATE_STOPPED;
4790 goto error;
4791 }
4792 if (list == NULL) {
4793 #ifdef WITH_XSLT_DEBUG_PROCESS
4794 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
4795 "xsltApplyTemplates: select didn't evaluate to a node list\n"));
4796 #endif
4797 goto exit;
4798 }
4799 /*
4800 *
4801 * NOTE: Previously a document info (xsltDocument) was
4802 * created and attached to the Result Tree Fragment.
4803 * But such a document info is created on demand in
4804 * xsltKeyFunction() (functions.c), so we need to create
4805 * it here beforehand.
4806 * In order to take care of potential keys we need to
4807 * do some extra work for the case when a Result Tree Fragment
4808 * is converted into a nodeset (e.g. exslt:node-set()) :
4809 * We attach a "pseudo-doc" (xsltDocument) to _private.
4810 * This xsltDocument, together with the keyset, will be freed
4811 * when the Result Tree Fragment is freed.
4812 *
4813 */
4814 #if 0
4815 if ((ctxt->nbKeys > 0) &&
4816 (list->nodeNr != 0) &&
4817 (list->nodeTab[0]->doc != NULL) &&
4818 XSLT_IS_RES_TREE_FRAG(list->nodeTab[0]->doc))
4819 {
4820 /*
4821 * NOTE that it's also OK if @effectiveDocInfo will be
4822 * set to NULL.
4823 */
4824 isRTF = 1;
4825 effectiveDocInfo = list->nodeTab[0]->doc->_private;
4826 }
4827 #endif
4828 } else {
4829 /*
4830 * Build an XPath node set with the children
4831 */
4832 list = xmlXPathNodeSetCreate(NULL);
4833 if (list == NULL)
4834 goto error;
4835 cur = node->children;
4836 while (cur != NULL) {
4837 switch (cur->type) {
4838 case XML_TEXT_NODE:
4839 if ((IS_BLANK_NODE(cur)) &&
4840 (cur->parent != NULL) &&
4841 (cur->parent->type == XML_ELEMENT_NODE) &&
4842 (ctxt->style->stripSpaces != NULL)) {
4843 const xmlChar *val;
4844
4845 if (cur->parent->ns != NULL) {
4846 val = (const xmlChar *)
4847 xmlHashLookup2(ctxt->style->stripSpaces,
4848 cur->parent->name,
4849 cur->parent->ns->href);
4850 if (val == NULL) {
4851 val = (const xmlChar *)
4852 xmlHashLookup2(ctxt->style->stripSpaces,
4853 BAD_CAST "*",
4854 cur->parent->ns->href);
4855 }
4856 } else {
4857 val = (const xmlChar *)
4858 xmlHashLookup2(ctxt->style->stripSpaces,
4859 cur->parent->name, NULL);
4860 }
4861 if ((val != NULL) &&
4862 (xmlStrEqual(val, (xmlChar *) "strip"))) {
4863 delNode = cur;
4864 break;
4865 }
4866 }
4867 /* no break on purpose */
4868 case XML_ELEMENT_NODE:
4869 case XML_DOCUMENT_NODE:
4870 case XML_HTML_DOCUMENT_NODE:
4871 case XML_CDATA_SECTION_NODE:
4872 case XML_PI_NODE:
4873 case XML_COMMENT_NODE:
4874 xmlXPathNodeSetAddUnique(list, cur);
4875 break;
4876 case XML_DTD_NODE:
4877 /* Unlink the DTD, it's still reachable
4878 * using doc->intSubset */
4879 if (cur->next != NULL)
4880 cur->next->prev = cur->prev;
4881 if (cur->prev != NULL)
4882 cur->prev->next = cur->next;
4883 break;
4884 default:
4885 #ifdef WITH_XSLT_DEBUG_PROCESS
4886 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
4887 "xsltApplyTemplates: skipping cur type %d\n",
4888 cur->type));
4889 #endif
4890 delNode = cur;
4891 }
4892 cur = cur->next;
4893 if (delNode != NULL) {
4894 #ifdef WITH_XSLT_DEBUG_PROCESS
4895 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
4896 "xsltApplyTemplates: removing ignorable blank cur\n"));
4897 #endif
4898 xmlUnlinkNode(delNode);
4899 xmlFreeNode(delNode);
4900 delNode = NULL;
4901 }
4902 }
4903 }
4904
4905 #ifdef WITH_XSLT_DEBUG_PROCESS
4906 if (list != NULL)
4907 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
4908 "xsltApplyTemplates: list of %d nodes\n", list->nodeNr));
4909 #endif
4910
4911 if ((list == NULL) || (list->nodeNr == 0))
4912 goto exit;
4913
4914 /*
4915 * Set the context's node set and size; this is also needed for
4916 * for xsltDoSortFunction().
4917 */
4918 ctxt->nodeList = list;
4919 /*
4920 * Process xsl:with-param and xsl:sort instructions.
4921 * (The code became so verbose just to avoid the
4922 * xmlNodePtr sorts[XSLT_MAX_SORT] if there's no xsl:sort)
4923 * BUG TODO: We are not using namespaced potentially defined on the
4924 * xsl:sort or xsl:with-param elements; XPath expression might fail.
4925 */
4926 if (inst->children) {
4927 xsltStackElemPtr param;
4928
4929 cur = inst->children;
4930 while (cur) {
4931
4932 #ifdef WITH_DEBUGGER
4933 if (ctxt->debugStatus != XSLT_DEBUG_NONE)
4934 xslHandleDebugger(cur, node, NULL, ctxt);
4935 #endif
4936 if (ctxt->state == XSLT_STATE_STOPPED)
4937 break;
4938 if (cur->type == XML_TEXT_NODE) {
4939 cur = cur->next;
4940 continue;
4941 }
4942 if (! IS_XSLT_ELEM(cur))
4943 break;
4944 if (IS_XSLT_NAME(cur, "with-param")) {
4945 param = xsltParseStylesheetCallerParam(ctxt, cur);
4946 if (param != NULL) {
4947 param->next = withParams;
4948 withParams = param;
4949 }
4950 }
4951 if (IS_XSLT_NAME(cur, "sort")) {
4952 xsltTemplatePtr oldCurTempRule =
4953 ctxt->currentTemplateRule;
4954 int nbsorts = 0;
4955 xmlNodePtr sorts[XSLT_MAX_SORT];
4956
4957 sorts[nbsorts++] = cur;
4958
4959 while (cur) {
4960
4961 #ifdef WITH_DEBUGGER
4962 if (ctxt->debugStatus != XSLT_DEBUG_NONE)
4963 xslHandleDebugger(cur, node, NULL, ctxt);
4964 #endif
4965 if (ctxt->state == XSLT_STATE_STOPPED)
4966 break;
4967
4968 if (cur->type == XML_TEXT_NODE) {
4969 cur = cur->next;
4970 continue;
4971 }
4972
4973 if (! IS_XSLT_ELEM(cur))
4974 break;
4975 if (IS_XSLT_NAME(cur, "with-param")) {
4976 param = xsltParseStylesheetCallerParam(ctxt, cur);
4977 if (param != NULL) {
4978 param->next = withParams;
4979 withParams = param;
4980 }
4981 }
4982 if (IS_XSLT_NAME(cur, "sort")) {
4983 if (nbsorts >= XSLT_MAX_SORT) {
4984 xsltTransformError(ctxt, NULL, cur,
4985 "The number (%d) of xsl:sort instructions exceeds the "
4986 "maximum allowed by this processor's settings.\n",
4987 nbsorts);
4988 ctxt->state = XSLT_STATE_STOPPED;
4989 break;
4990 } else {
4991 sorts[nbsorts++] = cur;
4992 }
4993 }
4994 cur = cur->next;
4995 }
4996 /*
4997 * The "current template rule" is cleared for xsl:sort.
4998 */
4999 ctxt->currentTemplateRule = NULL;
5000 /*
5001 * Sort.
5002 */
5003 xsltDoSortFunction(ctxt, sorts, nbsorts);
5004 ctxt->currentTemplateRule = oldCurTempRule;
5005 break;
5006 }
5007 cur = cur->next;
5008 }
5009 }
5010 xpctxt->contextSize = list->nodeNr;
5011 /*
5012 * Apply templates for all selected source nodes.
5013 */
5014 for (i = 0; i < list->nodeNr; i++) {
5015 cur = list->nodeTab[i];
5016 /*
5017 * The node becomes the "current node".
5018 */
5019 ctxt->node = cur;
5020 /*
5021 * An xsl:apply-templates can change the current context doc.
5022 * OPTIMIZE TODO: Get rid of the need to set the context doc.
5023 */
5024 if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL))
5025 xpctxt->doc = cur->doc;
5026
5027 xpctxt->proximityPosition = i + 1;
5028 /*
5029 * Find and apply a template for this node.
5030 */
5031 xsltProcessOneNode(ctxt, cur, withParams);
5032 }
5033
5034 exit:
5035 error:
5036 /*
5037 * Free the parameter list.
5038 */
5039 if (withParams != NULL)
5040 xsltFreeStackElemList(withParams);
5041 if (list != NULL)
5042 xmlXPathFreeNodeSet(list);
5043 /*
5044 * Restore context states.
5045 */
5046 xpctxt->nsNr = oldXPNsNr;
5047 xpctxt->namespaces = oldXPNamespaces;
5048 xpctxt->doc = oldXPDoc;
5049 xpctxt->contextSize = oldXPContextSize;
5050 xpctxt->proximityPosition = oldXPProximityPosition;
5051
5052 ctxt->document = oldDocInfo;
5053 ctxt->nodeList = oldList;
5054 ctxt->node = oldContextNode;
5055 ctxt->mode = oldMode;
5056 ctxt->modeURI = oldModeURI;
5057 }
5058
5059
5060 /**
5061 * xsltChoose:
5062 * @ctxt: a XSLT process context
5063 * @contextNode: the current node in the source tree
5064 * @inst: the xsl:choose instruction
5065 * @comp: compiled information of the instruction
5066 *
5067 * Processes the xsl:choose instruction on the source node.
5068 */
5069 void
5070 xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
5071 xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED)
5072 {
5073 xmlNodePtr cur;
5074
5075 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL))
5076 return;
5077
5078 /*
5079 * TODO: Content model checks should be done only at compilation
5080 * time.
5081 */
5082 cur = inst->children;
5083 if (cur == NULL) {
5084 xsltTransformError(ctxt, NULL, inst,
5085 "xsl:choose: The instruction has no content.\n");
5086 return;
5087 }
5088
5089 #ifdef XSLT_REFACTORED
5090 /*
5091 * We don't check the content model during transformation.
5092 */
5093 #else
5094 if ((! IS_XSLT_ELEM(cur)) || (! IS_XSLT_NAME(cur, "when"))) {
5095 xsltTransformError(ctxt, NULL, inst,
5096 "xsl:choose: xsl:when expected first\n");
5097 return;
5098 }
5099 #endif
5100
5101 {
5102 int testRes = 0, res = 0;
5103 xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
5104 xmlDocPtr oldXPContextDoc = xpctxt->doc;
5105 int oldXPProximityPosition = xpctxt->proximityPosition;
5106 int oldXPContextSize = xpctxt->contextSize;
5107 xmlNsPtr *oldXPNamespaces = xpctxt->namespaces;
5108 int oldXPNsNr = xpctxt->nsNr;
5109
5110 #ifdef XSLT_REFACTORED
5111 xsltStyleItemWhenPtr wcomp = NULL;
5112 #else
5113 xsltStylePreCompPtr wcomp = NULL;
5114 #endif
5115
5116 /*
5117 * Process xsl:when ---------------------------------------------------
5118 */
5119 while (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "when")) {
5120 wcomp = cur->psvi;
5121
5122 if ((wcomp == NULL) || (wcomp->test == NULL) ||
5123 (wcomp->comp == NULL))
5124 {
5125 xsltTransformError(ctxt, NULL, cur,
5126 "Internal error in xsltChoose(): "
5127 "The XSLT 'when' instruction was not compiled.\n");
5128 goto error;
5129 }
5130
5131
5132 #ifdef WITH_DEBUGGER
5133 if (xslDebugStatus != XSLT_DEBUG_NONE) {
5134 /*
5135 * TODO: Isn't comp->templ always NULL for xsl:choose?
5136 */
5137 xslHandleDebugger(cur, contextNode, NULL, ctxt);
5138 }
5139 #endif
5140 #ifdef WITH_XSLT_DEBUG_PROCESS
5141 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
5142 "xsltChoose: test %s\n", wcomp->test));
5143 #endif
5144
5145 xpctxt->node = contextNode;
5146 xpctxt->doc = oldXPContextDoc;
5147 xpctxt->proximityPosition = oldXPProximityPosition;
5148 xpctxt->contextSize = oldXPContextSize;
5149
5150 #ifdef XSLT_REFACTORED
5151 if (wcomp->inScopeNs != NULL) {
5152 xpctxt->namespaces = wcomp->inScopeNs->list;
5153 xpctxt->nsNr = wcomp->inScopeNs->xpathNumber;
5154 } else {
5155 xpctxt->namespaces = NULL;
5156 xpctxt->nsNr = 0;
5157 }
5158 #else
5159 xpctxt->namespaces = wcomp->nsList;
5160 xpctxt->nsNr = wcomp->nsNr;
5161 #endif
5162
5163
5164 #ifdef XSLT_FAST_IF
5165 res = xmlXPathCompiledEvalToBoolean(wcomp->comp, xpctxt);
5166
5167 if (res == -1) {
5168 ctxt->state = XSLT_STATE_STOPPED;
5169 goto error;
5170 }
5171 testRes = (res == 1) ? 1 : 0;
5172
5173 #else /* XSLT_FAST_IF */
5174
5175 res = xmlXPathCompiledEval(wcomp->comp, xpctxt);
5176
5177 if (res != NULL) {
5178 if (res->type != XPATH_BOOLEAN)
5179 res = xmlXPathConvertBoolean(res);
5180 if (res->type == XPATH_BOOLEAN)
5181 testRes = res->boolval;
5182 else {
5183 #ifdef WITH_XSLT_DEBUG_PROCESS
5184 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
5185 "xsltChoose: test didn't evaluate to a boolean\n"));
5186 #endif
5187 goto error;
5188 }
5189 xmlXPathFreeObject(res);
5190 res = NULL;
5191 } else {
5192 ctxt->state = XSLT_STATE_STOPPED;
5193 goto error;
5194 }
5195
5196 #endif /* else of XSLT_FAST_IF */
5197
5198 #ifdef WITH_XSLT_DEBUG_PROCESS
5199 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
5200 "xsltChoose: test evaluate to %d\n", testRes));
5201 #endif
5202 if (testRes)
5203 goto test_is_true;
5204
5205 cur = cur->next;
5206 }
5207
5208 /*
5209 * Process xsl:otherwise ----------------------------------------------
5210 */
5211 if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "otherwise")) {
5212
5213 #ifdef WITH_DEBUGGER
5214 if (xslDebugStatus != XSLT_DEBUG_NONE)
5215 xslHandleDebugger(cur, contextNode, NULL, ctxt);
5216 #endif
5217
5218 #ifdef WITH_XSLT_DEBUG_PROCESS
5219 XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
5220 "evaluating xsl:otherwise\n"));
5221 #endif
5222 goto test_is_true;
5223 }
5224 xpctxt->node = contextNode;
5225 xpctxt->doc = oldXPContextDoc;
5226 xpctxt->proximityPosition = oldXPProximityPosition;
5227 xpctxt->contextSize = oldXPContextSize;
5228 xpctxt->namespaces = oldXPNamespaces;
5229 xpctxt->nsNr = oldXPNsNr;
5230 goto exit;
5231
5232 test_is_true:
5233
5234 xpctxt->node = contextNode;
5235 xpctxt->doc = oldXPContextDoc;
5236 xpctxt->proximityPosition = oldXPProximityPosition;
5237 xpctxt->contextSize = oldXPContextSize;
5238 xpctxt->namespaces = oldXPNamespaces;
5239 xpctxt->nsNr = oldXPNsNr;
5240 goto process_sequence;
5241 }
5242
5243 process_sequence:
5244
5245 /*
5246 * Instantiate the sequence constructor.
5247 */
5248 xsltApplySequenceConstructor(ctxt, ctxt->node, cur->children,
5249 NULL);
5250
5251 exit:
5252 error:
5253 return;
5254 }
5255
5256 /**
5257 * xsltIf:
5258 * @ctxt: a XSLT process context
5259 * @contextNode: the current node in the source tree
5260 * @inst: the xsl:if instruction
5261 * @castedComp: compiled information of the instruction
5262 *
5263 * Processes the xsl:if instruction on the source node.
5264 */
5265 void
5266 xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
5267 xmlNodePtr inst, xsltStylePreCompPtr castedComp)
5268 {
5269 int res = 0;
5270
5271 #ifdef XSLT_REFACTORED
5272 xsltStyleItemIfPtr comp = (xsltStyleItemIfPtr) castedComp;
5273 #else
5274 xsltStylePreCompPtr comp = castedComp;
5275 #endif
5276
5277 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL))
5278 return;
5279 if ((comp == NULL) || (comp->test == NULL) || (comp->comp == NULL)) {
5280 xsltTransformError(ctxt, NULL, inst,
5281 "Internal error in xsltIf(): "
5282 "The XSLT 'if' instruction was not compiled.\n");
5283 return;
5284 }
5285
5286 #ifdef WITH_XSLT_DEBUG_PROCESS
5287 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext,
5288 "xsltIf: test %s\n", comp->test));
5289 #endif
5290
5291 #ifdef XSLT_FAST_IF
5292 {
5293 xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
5294 xmlDocPtr oldXPContextDoc = xpctxt->doc;
5295 xmlNsPtr *oldXPNamespaces = xpctxt->namespaces;
5296 xmlNodePtr oldXPContextNode = xpctxt->node;
5297 int oldXPProximityPosition = xpctxt->proximityPosition;
5298 int oldXPContextSize = xpctxt->contextSize;
5299 int oldXPNsNr = xpctxt->nsNr;
5300 xmlDocPtr oldLocalFragmentTop = ctxt->localRVT;
5301
5302 xpctxt->node = contextNode;
5303 if (comp != NULL) {
5304
5305 #ifdef XSLT_REFACTORED
5306 if (comp->inScopeNs != NULL) {
5307 xpctxt->namespaces = comp->inScopeNs->list;
5308 xpctxt->nsNr = comp->inScopeNs->xpathNumber;
5309 } else {
5310 xpctxt->namespaces = NULL;
5311 xpctxt->nsNr = 0;
5312 }
5313 #else
5314 xpctxt->namespaces = comp->nsList;
5315 xpctxt->nsNr = comp->nsNr;
5316 #endif
5317 } else {
5318 xpctxt->namespaces = NULL;
5319 xpctxt->nsNr = 0;
5320 }
5321 /*
5322 * This XPath function is optimized for boolean results.
5323 */
5324 res = xmlXPathCompiledEvalToBoolean(comp->comp, xpctxt);
5325
5326 /*
5327 * Cleanup fragments created during evaluation of the
5328 * "select" expression.
5329 */
5330 if (oldLocalFragmentTop != ctxt->localRVT)
5331 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
5332
5333 xpctxt->doc = oldXPContextDoc;
5334 xpctxt->node = oldXPContextNode;
5335 xpctxt->contextSize = oldXPContextSize;
5336 xpctxt->proximityPosition = oldXPProximityPosition;
5337 xpctxt->nsNr = oldXPNsNr;
5338 xpctxt->namespaces = oldXPNamespaces;
5339 }
5340
5341 #ifdef WITH_XSLT_DEBUG_PROCESS
5342 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext,
5343 "xsltIf: test evaluate to %d\n", res));
5344 #endif
5345
5346 if (res == -1) {
5347 ctxt->state = XSLT_STATE_STOPPED;
5348 goto error;
5349 }
5350 if (res == 1) {
5351 /*
5352 * Instantiate the sequence constructor of xsl:if.
5353 */
5354 xsltApplySequenceConstructor(ctxt,
5355 contextNode, inst->children, NULL);
5356 }
5357
5358 #else /* XSLT_FAST_IF */
5359 {
5360 xmlXPathObjectPtr xpobj = NULL;
5361 /*
5362 * OLD CODE:
5363 */
5364 {
5365 xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
5366 xmlDocPtr oldXPContextDoc = xpctxt->doc;
5367 xmlNsPtr *oldXPNamespaces = xpctxt->namespaces;
5368 xmlNodePtr oldXPContextNode = xpctxt->node;
5369 int oldXPProximityPosition = xpctxt->proximityPosition;
5370 int oldXPContextSize = xpctxt->contextSize;
5371 int oldXPNsNr = xpctxt->nsNr;
5372
5373 xpctxt->node = contextNode;
5374 if (comp != NULL) {
5375
5376 #ifdef XSLT_REFACTORED
5377 if (comp->inScopeNs != NULL) {
5378 xpctxt->namespaces = comp->inScopeNs->list;
5379 xpctxt->nsNr = comp->inScopeNs->xpathNumber;
5380 } else {
5381 xpctxt->namespaces = NULL;
5382 xpctxt->nsNr = 0;
5383 }
5384 #else
5385 xpctxt->namespaces = comp->nsList;
5386 xpctxt->nsNr = comp->nsNr;
5387 #endif
5388 } else {
5389 xpctxt->namespaces = NULL;
5390 xpctxt->nsNr = 0;
5391 }
5392
5393 /*
5394 * This XPath function is optimized for boolean results.
5395 */
5396 xpobj = xmlXPathCompiledEval(comp->comp, xpctxt);
5397
5398 xpctxt->doc = oldXPContextDoc;
5399 xpctxt->node = oldXPContextNode;
5400 xpctxt->contextSize = oldXPContextSize;
5401 xpctxt->proximityPosition = oldXPProximityPosition;
5402 xpctxt->nsNr = oldXPNsNr;
5403 xpctxt->namespaces = oldXPNamespaces;
5404 }
5405 if (xpobj != NULL) {
5406 if (xpobj->type != XPATH_BOOLEAN)
5407 xpobj = xmlXPathConvertBoolean(xpobj);
5408 if (xpobj->type == XPATH_BOOLEAN) {
5409 res = xpobj->boolval;
5410
5411 #ifdef WITH_XSLT_DEBUG_PROCESS
5412 XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext,
5413 "xsltIf: test evaluate to %d\n", res));
5414 #endif
5415 if (res) {
5416 xsltApplySequenceConstructor(ctxt,
5417 contextNode, inst->children, NULL);
5418 }
5419 } else {
5420
5421 #ifdef WITH_XSLT_DEBUG_PROCESS
5422 XSLT_TRACE(ctxt, XSLT_TRACE_IF,
5423 xsltGenericDebug(xsltGenericDebugContext,
5424 "xsltIf: test didn't evaluate to a boolean\n"));
5425 #endif
5426 ctxt->state = XSLT_STATE_STOPPED;
5427 }
5428 xmlXPathFreeObject(xpobj);
5429 } else {
5430 ctxt->state = XSLT_STATE_STOPPED;
5431 }
5432 }
5433 #endif /* else of XSLT_FAST_IF */
5434
5435 error:
5436 return;
5437 }
5438
5439 /**
5440 * xsltForEach:
5441 * @ctxt: an XSLT transformation context
5442 * @contextNode: the "current node" in the source tree
5443 * @inst: the element node of the xsl:for-each instruction
5444 * @castedComp: the compiled information of the instruction
5445 *
5446 * Process the xslt for-each node on the source node
5447 */
5448 void
5449 xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
5450 xmlNodePtr inst, xsltStylePreCompPtr castedComp)
5451 {
5452 #ifdef XSLT_REFACTORED
5453 xsltStyleItemForEachPtr comp = (xsltStyleItemForEachPtr) castedComp;
5454 #else
5455 xsltStylePreCompPtr comp = castedComp;
5456 #endif
5457 int i;
5458 xmlXPathObjectPtr res = NULL;
5459 xmlNodePtr cur, curInst;
5460 xmlNodeSetPtr list = NULL;
5461 xmlNodeSetPtr oldList;
5462 int oldXPProximityPosition, oldXPContextSize;
5463 xmlNodePtr oldContextNode;
5464 xsltTemplatePtr oldCurTemplRule;
5465 xmlDocPtr oldXPDoc;
5466 xsltDocumentPtr oldDocInfo;
5467 xmlXPathContextPtr xpctxt;
5468
5469 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) {
5470 xsltGenericError(xsltGenericErrorContext,
5471 "xsltForEach(): Bad arguments.\n");
5472 return;
5473 }
5474
5475 if (comp == NULL) {
5476 xsltTransformError(ctxt, NULL, inst,
5477 "Internal error in xsltForEach(): "
5478 "The XSLT 'for-each' instruction was not compiled.\n");
5479 return;
5480 }
5481 if ((comp->select == NULL) || (comp->comp == NULL)) {
5482 xsltTransformError(ctxt, NULL, inst,
5483 "Internal error in xsltForEach(): "
5484 "The selecting expression of the XSLT 'for-each' "
5485 "instruction was not compiled correctly.\n");
5486 return;
5487 }
5488 xpctxt = ctxt->xpathCtxt;
5489
5490 #ifdef WITH_XSLT_DEBUG_PROCESS
5491 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext,
5492 "xsltForEach: select %s\n", comp->select));
5493 #endif
5494
5495 /*
5496 * Save context states.
5497 */
5498 oldDocInfo = ctxt->document;
5499 oldList = ctxt->nodeList;
5500 oldContextNode = ctxt->node;
5501 /*
5502 * The "current template rule" is cleared for the instantiation of
5503 * xsl:for-each.
5504 */
5505 oldCurTemplRule = ctxt->currentTemplateRule;
5506 ctxt->currentTemplateRule = NULL;
5507
5508 oldXPDoc = xpctxt->doc;
5509 oldXPProximityPosition = xpctxt->proximityPosition;
5510 oldXPContextSize = xpctxt->contextSize;
5511 /*
5512 * Set up XPath.
5513 */
5514 xpctxt->node = contextNode;
5515 #ifdef XSLT_REFACTORED
5516 if (comp->inScopeNs != NULL) {
5517 xpctxt->namespaces = comp->inScopeNs->list;
5518 xpctxt->nsNr = comp->inScopeNs->xpathNumber;
5519 } else {
5520 xpctxt->namespaces = NULL;
5521 xpctxt->nsNr = 0;
5522 }
5523 #else
5524 xpctxt->namespaces = comp->nsList;
5525 xpctxt->nsNr = comp->nsNr;
5526 #endif
5527
5528 /*
5529 * Evaluate the 'select' expression.
5530 */
5531 res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
5532
5533 if (res != NULL) {
5534 if (res->type == XPATH_NODESET)
5535 list = res->nodesetval;
5536 else {
5537 xsltTransformError(ctxt, NULL, inst,
5538 "The 'select' expression does not evaluate to a node set.\n");
5539
5540 #ifdef WITH_XSLT_DEBUG_PROCESS
5541 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext,
5542 "xsltForEach: select didn't evaluate to a node list\n"));
5543 #endif
5544 goto error;
5545 }
5546 } else {
5547 xsltTransformError(ctxt, NULL, inst,
5548 "Failed to evaluate the 'select' expression.\n");
5549 ctxt->state = XSLT_STATE_STOPPED;
5550 goto error;
5551 }
5552
5553 if ((list == NULL) || (list->nodeNr <= 0))
5554 goto exit;
5555
5556 #ifdef WITH_XSLT_DEBUG_PROCESS
5557 XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext,
5558 "xsltForEach: select evaluates to %d nodes\n", list->nodeNr));
5559 #endif
5560
5561 /*
5562 * Restore XPath states for the "current node".
5563 */
5564 xpctxt->contextSize = oldXPContextSize;
5565 xpctxt->proximityPosition = oldXPProximityPosition;
5566 xpctxt->node = contextNode;
5567
5568 /*
5569 * Set the list; this has to be done already here for xsltDoSortFunction().
5570 */
5571 ctxt->nodeList = list;
5572 /*
5573 * Handle xsl:sort instructions and skip them for further processing.
5574 * BUG TODO: We are not using namespaced potentially defined on the
5575 * xsl:sort element; XPath expression might fail.
5576 */
5577 curInst = inst->children;
5578 if (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) {
5579 int nbsorts = 0;
5580 xmlNodePtr sorts[XSLT_MAX_SORT];
5581
5582 sorts[nbsorts++] = curInst;
5583
5584 #ifdef WITH_DEBUGGER
5585 if (xslDebugStatus != XSLT_DEBUG_NONE)
5586 xslHandleDebugger(curInst, contextNode, NULL, ctxt);
5587 #endif
5588
5589 curInst = curInst->next;
5590 while (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) {
5591 if (nbsorts >= XSLT_MAX_SORT) {
5592 xsltTransformError(ctxt, NULL, curInst,
5593 "The number of xsl:sort instructions exceeds the "
5594 "maximum (%d) allowed by this processor.\n",
5595 XSLT_MAX_SORT);
5596 goto error;
5597 } else {
5598 sorts[nbsorts++] = curInst;
5599 }
5600
5601 #ifdef WITH_DEBUGGER
5602 if (xslDebugStatus != XSLT_DEBUG_NONE)
5603 xslHandleDebugger(curInst, contextNode, NULL, ctxt);
5604 #endif
5605 curInst = curInst->next;
5606 }
5607 xsltDoSortFunction(ctxt, sorts, nbsorts);
5608 }
5609 xpctxt->contextSize = list->nodeNr;
5610 /*
5611 * Instantiate the sequence constructor for each selected node.
5612 */
5613 for (i = 0; i < list->nodeNr; i++) {
5614 cur = list->nodeTab[i];
5615 /*
5616 * The selected node becomes the "current node".
5617 */
5618 ctxt->node = cur;
5619 /*
5620 * An xsl:for-each can change the current context doc.
5621 * OPTIMIZE TODO: Get rid of the need to set the context doc.
5622 */
5623 if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL))
5624 xpctxt->doc = cur->doc;
5625
5626 xpctxt->proximityPosition = i + 1;
5627
5628 xsltApplySequenceConstructor(ctxt, cur, curInst, NULL);
5629 }
5630
5631 exit:
5632 error:
5633 if (res != NULL)
5634 xmlXPathFreeObject(res);
5635 /*
5636 * Restore old states.
5637 */
5638 ctxt->document = oldDocInfo;
5639 ctxt->nodeList = oldList;
5640 ctxt->node = oldContextNode;
5641 ctxt->currentTemplateRule = oldCurTemplRule;
5642
5643 xpctxt->doc = oldXPDoc;
5644 xpctxt->contextSize = oldXPContextSize;
5645 xpctxt->proximityPosition = oldXPProximityPosition;
5646 }
5647
5648 /************************************************************************
5649 * *
5650 * Generic interface *
5651 * *
5652 ************************************************************************/
5653
5654 #ifdef XSLT_GENERATE_HTML_DOCTYPE
5655 typedef struct xsltHTMLVersion {
5656 const char *version;
5657 const char *public;
5658 const char *system;
5659 } xsltHTMLVersion;
5660
5661 static xsltHTMLVersion xsltHTMLVersions[] = {
5662 { "4.01frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
5663 "http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd"},
5664 { "4.01strict", "-//W3C//DTD HTML 4.01//EN",
5665 "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"},
5666 { "4.01trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
5667 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
5668 { "4.01", "-//W3C//DTD HTML 4.01 Transitional//EN",
5669 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
5670 { "4.0strict", "-//W3C//DTD HTML 4.01//EN",
5671 "http://www.w3.org/TR/html4/strict.dtd"},
5672 { "4.0trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
5673 "http://www.w3.org/TR/html4/loose.dtd"},
5674 { "4.0frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
5675 "http://www.w3.org/TR/html4/frameset.dtd"},
5676 { "4.0", "-//W3C//DTD HTML 4.01 Transitional//EN",
5677 "http://www.w3.org/TR/html4/loose.dtd"},
5678 { "3.2", "-//W3C//DTD HTML 3.2//EN", NULL }
5679 };
5680
5681 /**
5682 * xsltGetHTMLIDs:
5683 * @version: the version string
5684 * @publicID: used to return the public ID
5685 * @systemID: used to return the system ID
5686 *
5687 * Returns -1 if not found, 0 otherwise and the system and public
5688 * Identifier for this given verion of HTML
5689 */
5690 static int
5691 xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID,
5692 const xmlChar **systemID) {
5693 unsigned int i;
5694 if (version == NULL)
5695 return(-1);
5696 for (i = 0;i < (sizeof(xsltHTMLVersions)/sizeof(xsltHTMLVersions[1]));
5697 i++) {
5698 if (!xmlStrcasecmp(version,
5699 (const xmlChar *) xsltHTMLVersions[i].version)) {
5700 if (publicID != NULL)
5701 *publicID = (const xmlChar *) xsltHTMLVersions[i].public;
5702 if (systemID != NULL)
5703 *systemID = (const xmlChar *) xsltHTMLVersions[i].system;
5704 return(0);
5705 }
5706 }
5707 return(-1);
5708 }
5709 #endif
5710
5711 /**
5712 * xsltApplyStripSpaces:
5713 * @ctxt: a XSLT process context
5714 * @node: the root of the XML tree
5715 *
5716 * Strip the unwanted ignorable spaces from the input tree
5717 */
5718 void
5719 xsltApplyStripSpaces(xsltTransformContextPtr ctxt, xmlNodePtr node) {
5720 xmlNodePtr current;
5721 #ifdef WITH_XSLT_DEBUG_PROCESS
5722 int nb = 0;
5723 #endif
5724
5725
5726 current = node;
5727 while (current != NULL) {
5728 /*
5729 * Cleanup children empty nodes if asked for
5730 */
5731 if ((IS_XSLT_REAL_NODE(current)) &&
5732 (current->children != NULL) &&
5733 (xsltFindElemSpaceHandling(ctxt, current))) {
5734 xmlNodePtr delete = NULL, cur = current->children;
5735
5736 while (cur != NULL) {
5737 if (IS_BLANK_NODE(cur))
5738 delete = cur;
5739
5740 cur = cur->next;
5741 if (delete != NULL) {
5742 xmlUnlinkNode(delete);
5743 xmlFreeNode(delete);
5744 delete = NULL;
5745 #ifdef WITH_XSLT_DEBUG_PROCESS
5746 nb++;
5747 #endif
5748 }
5749 }
5750 }
5751
5752 /*
5753 * Skip to next node in document order.
5754 */
5755 if (node->type == XML_ENTITY_REF_NODE) {
5756 /* process deep in entities */
5757 xsltApplyStripSpaces(ctxt, node->children);
5758 }
5759 if ((current->children != NULL) &&
5760 (current->type != XML_ENTITY_REF_NODE)) {
5761 current = current->children;
5762 } else if (current->next != NULL) {
5763 current = current->next;
5764 } else {
5765 do {
5766 current = current->parent;
5767 if (current == NULL)
5768 break;
5769 if (current == node)
5770 goto done;
5771 if (current->next != NULL) {
5772 current = current->next;
5773 break;
5774 }
5775 } while (current != NULL);
5776 }
5777 }
5778
5779 done:
5780 #ifdef WITH_XSLT_DEBUG_PROCESS
5781 XSLT_TRACE(ctxt,XSLT_TRACE_STRIP_SPACES,xsltGenericDebug(xsltGenericDebugContext,
5782 "xsltApplyStripSpaces: removed %d ignorable blank node\n", nb));
5783 #endif
5784 return;
5785 }
5786
5787 static int
5788 xsltCountKeys(xsltTransformContextPtr ctxt)
5789 {
5790 xsltStylesheetPtr style;
5791 xsltKeyDefPtr keyd;
5792
5793 if (ctxt == NULL)
5794 return(-1);
5795
5796 /*
5797 * Do we have those nastly templates with a key() in the match pattern?
5798 */
5799 ctxt->hasTemplKeyPatterns = 0;
5800 style = ctxt->style;
5801 while (style != NULL) {
5802 if (style->keyMatch != NULL) {
5803 ctxt->hasTemplKeyPatterns = 1;
5804 break;
5805 }
5806 style = xsltNextImport(style);
5807 }
5808 /*
5809 * Count number of key declarations.
5810 */
5811 ctxt->nbKeys = 0;
5812 style = ctxt->style;
5813 while (style != NULL) {
5814 keyd = style->keys;
5815 while (keyd) {
5816 ctxt->nbKeys++;
5817 keyd = keyd->next;
5818 }
5819 style = xsltNextImport(style);
5820 }
5821 return(ctxt->nbKeys);
5822 }
5823
5824 /**
5825 * xsltApplyStylesheetInternal:
5826 * @style: a parsed XSLT stylesheet
5827 * @doc: a parsed XML document
5828 * @params: a NULL terminated array of parameters names/values tuples
5829 * @output: the targetted output
5830 * @profile: profile FILE * output or NULL
5831 * @user: user provided parameter
5832 *
5833 * Apply the stylesheet to the document
5834 * NOTE: This may lead to a non-wellformed output XML wise !
5835 *
5836 * Returns the result document or NULL in case of error
5837 */
5838 static xmlDocPtr
5839 xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc,
5840 const char **params, const char *output,
5841 FILE * profile, xsltTransformContextPtr userCtxt)
5842 {
5843 xmlDocPtr res = NULL;
5844 xsltTransformContextPtr ctxt = NULL;
5845 xmlNodePtr root, node;
5846 const xmlChar *method;
5847 const xmlChar *doctypePublic;
5848 const xmlChar *doctypeSystem;
5849 const xmlChar *version;
5850 const xmlChar *encoding;
5851 xsltStackElemPtr variables;
5852 xsltStackElemPtr vptr;
5853
5854 xsltInitGlobals();
5855
5856 if ((style == NULL) || (doc == NULL))
5857 return (NULL);
5858
5859 if (style->internalized == 0) {
5860 #ifdef WITH_XSLT_DEBUG
5861 xsltGenericDebug(xsltGenericDebugContext,
5862 "Stylesheet was not fully internalized !\n");
5863 #endif
5864 }
5865 if (doc->intSubset != NULL) {
5866 /*
5867 * Avoid hitting the DTD when scanning nodes
5868 * but keep it linked as doc->intSubset
5869 */
5870 xmlNodePtr cur = (xmlNodePtr) doc->intSubset;
5871 if (cur->next != NULL)
5872 cur->next->prev = cur->prev;
5873 if (cur->prev != NULL)
5874 cur->prev->next = cur->next;
5875 if (doc->children == cur)
5876 doc->children = cur->next;
5877 if (doc->last == cur)
5878 doc->last = cur->prev;
5879 cur->prev = cur->next = NULL;
5880 }
5881
5882 /*
5883 * Check for XPath document order availability
5884 */
5885 root = xmlDocGetRootElement(doc);
5886 if (root != NULL) {
5887 if (((long) root->content) >= 0 && (xslDebugStatus == XSLT_DEBUG_NONE))
5888 xmlXPathOrderDocElems(doc);
5889 }
5890
5891 if (userCtxt != NULL)
5892 ctxt = userCtxt;
5893 else
5894 ctxt = xsltNewTransformContext(style, doc);
5895
5896 if (ctxt == NULL)
5897 return (NULL);
5898
5899 ctxt->initialContextDoc = doc;
5900 ctxt->initialContextNode = (xmlNodePtr) doc;
5901
5902 if (profile != NULL)
5903 ctxt->profile = 1;
5904
5905 if (output != NULL)
5906 ctxt->outputFile = output;
5907 else
5908 ctxt->outputFile = NULL;
5909
5910 /*
5911 * internalize the modes if needed
5912 */
5913 if (ctxt->dict != NULL) {
5914 if (ctxt->mode != NULL)
5915 ctxt->mode = xmlDictLookup(ctxt->dict, ctxt->mode, -1);
5916 if (ctxt->modeURI != NULL)
5917 ctxt->modeURI = xmlDictLookup(ctxt->dict, ctxt->modeURI, -1);
5918 }
5919
5920 XSLT_GET_IMPORT_PTR(method, style, method)
5921 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
5922 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
5923 XSLT_GET_IMPORT_PTR(version, style, version)
5924 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
5925
5926 if ((method != NULL) &&
5927 (!xmlStrEqual(method, (const xmlChar *) "xml")))
5928 {
5929 if (xmlStrEqual(method, (const xmlChar *) "html")) {
5930 ctxt->type = XSLT_OUTPUT_HTML;
5931 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
5932 res = htmlNewDoc(doctypeSystem, doctypePublic);
5933 } else {
5934 if (version == NULL) {
5935 xmlDtdPtr dtd;
5936
5937 res = htmlNewDoc(NULL, NULL);
5938 /*
5939 * Make sure no DTD node is generated in this case
5940 */
5941 if (res != NULL) {
5942 dtd = xmlGetIntSubset(res);
5943 if (dtd != NULL) {
5944 xmlUnlinkNode((xmlNodePtr) dtd);
5945 xmlFreeDtd(dtd);
5946 }
5947 res->intSubset = NULL;
5948 res->extSubset = NULL;
5949 }
5950 } else {
5951
5952 #ifdef XSLT_GENERATE_HTML_DOCTYPE
5953 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
5954 #endif
5955 res = htmlNewDoc(doctypeSystem, doctypePublic);
5956 }
5957 }
5958 if (res == NULL)
5959 goto error;
5960 res->dict = ctxt->dict;
5961 xmlDictReference(res->dict);
5962
5963 #ifdef WITH_XSLT_DEBUG
5964 xsltGenericDebug(xsltGenericDebugContext,
5965 "reusing transformation dict for output\n");
5966 #endif
5967 } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
5968 xsltTransformError(ctxt, NULL, (xmlNodePtr) doc,
5969 "xsltApplyStylesheetInternal: unsupported method xhtml, using html\n",
5970 style->method);
5971 ctxt->type = XSLT_OUTPUT_HTML;
5972 res = htmlNewDoc(doctypeSystem, doctypePublic);
5973 if (res == NULL)
5974 goto error;
5975 res->dict = ctxt->dict;
5976 xmlDictReference(res->dict);
5977
5978 #ifdef WITH_XSLT_DEBUG
5979 xsltGenericDebug(xsltGenericDebugContext,
5980 "reusing transformation dict for output\n");
5981 #endif
5982 } else if (xmlStrEqual(method, (const xmlChar *) "text")) {
5983 ctxt->type = XSLT_OUTPUT_TEXT;
5984 res = xmlNewDoc(style->version);
5985 if (res == NULL)
5986 goto error;
5987 res->dict = ctxt->dict;
5988 xmlDictReference(res->dict);
5989
5990 #ifdef WITH_XSLT_DEBUG
5991 xsltGenericDebug(xsltGenericDebugContext,
5992 "reusing transformation dict for output\n");
5993 #endif
5994 } else {
5995 xsltTransformError(ctxt, NULL, (xmlNodePtr) doc,
5996 "xsltApplyStylesheetInternal: unsupported method %s\n",
5997 style->method);
5998 goto error;
5999 }
6000 } else {
6001 ctxt->type = XSLT_OUTPUT_XML;
6002 res = xmlNewDoc(style->version);
6003 if (res == NULL)
6004 goto error;
6005 res->dict = ctxt->dict;
6006 xmlDictReference(ctxt->dict);
6007 #ifdef WITH_XSLT_DEBUG
6008 xsltGenericDebug(xsltGenericDebugContext,
6009 "reusing transformation dict for output\n");
6010 #endif
6011 }
6012 res->charset = XML_CHAR_ENCODING_UTF8;
6013 if (encoding != NULL)
6014 res->encoding = xmlStrdup(encoding);
6015 variables = style->variables;
6016
6017 /*
6018 * Start the evaluation, evaluate the params, the stylesheets globals
6019 * and start by processing the top node.
6020 */
6021 if (xsltNeedElemSpaceHandling(ctxt))
6022 xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc));
6023 /*
6024 * Evaluate global params and user-provided params.
6025 */
6026 ctxt->node = (xmlNodePtr) doc;
6027 if (ctxt->globalVars == NULL)
6028 ctxt->globalVars = xmlHashCreate(20);
6029 if (params != NULL) {
6030 xsltEvalUserParams(ctxt, params);
6031 }
6032
6033 /* need to be called before evaluating global variables */
6034 xsltCountKeys(ctxt);
6035
6036 xsltEvalGlobalVariables(ctxt);
6037
6038 ctxt->node = (xmlNodePtr) doc;
6039 ctxt->output = res;
6040 ctxt->insert = (xmlNodePtr) res;
6041 ctxt->varsBase = ctxt->varsNr - 1;
6042
6043 ctxt->xpathCtxt->contextSize = 1;
6044 ctxt->xpathCtxt->proximityPosition = 1;
6045 ctxt->xpathCtxt->node = NULL; /* TODO: Set the context node here? */
6046 /*
6047 * Start processing the source tree -----------------------------------
6048 */
6049 xsltProcessOneNode(ctxt, ctxt->node, NULL);
6050 /*
6051 * Remove all remaining vars from the stack.
6052 */
6053 xsltLocalVariablePop(ctxt, 0, -2);
6054 xsltShutdownCtxtExts(ctxt);
6055
6056 xsltCleanupTemplates(style); /* TODO: <- style should be read only */
6057
6058 /*
6059 * Now cleanup our variables so stylesheet can be re-used
6060 *
6061 * TODO: this is not needed anymore global variables are copied
6062 * and not evaluated directly anymore, keep this as a check
6063 */
6064 if (style->variables != variables) {
6065 vptr = style->variables;
6066 while (vptr->next != variables)
6067 vptr = vptr->next;
6068 vptr->next = NULL;
6069 xsltFreeStackElemList(style->variables);
6070 style->variables = variables;
6071 }
6072 vptr = style->variables;
6073 while (vptr != NULL) {
6074 if (vptr->computed) {
6075 if (vptr->value != NULL) {
6076 xmlXPathFreeObject(vptr->value);
6077 vptr->value = NULL;
6078 vptr->computed = 0;
6079 }
6080 }
6081 vptr = vptr->next;
6082 }
6083 #if 0
6084 /*
6085 * code disabled by wmb; awaiting kb's review
6086 * problem is that global variable(s) may contain xpath objects
6087 * from doc associated with RVT, so can't be freed at this point.
6088 * xsltFreeTransformContext includes a call to xsltFreeRVTs, so
6089 * I assume this shouldn't be required at this point.
6090 */
6091 /*
6092 * Free all remaining tree fragments.
6093 */
6094 xsltFreeRVTs(ctxt);
6095 #endif
6096 /*
6097 * Do some post processing work depending on the generated output
6098 */
6099 root = xmlDocGetRootElement(res);
6100 if (root != NULL) {
6101 const xmlChar *doctype = NULL;
6102
6103 if ((root->ns != NULL) && (root->ns->prefix != NULL))
6104 doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name);
6105 if (doctype == NULL)
6106 doctype = root->name;
6107
6108 /*
6109 * Apply the default selection of the method
6110 */
6111 if ((method == NULL) &&
6112 (root->ns == NULL) &&
6113 (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) {
6114 xmlNodePtr tmp;
6115
6116 tmp = res->children;
6117 while ((tmp != NULL) && (tmp != root)) {
6118 if (tmp->type == XML_ELEMENT_NODE)
6119 break;
6120 if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp)))
6121 break;
6122 tmp = tmp->next;
6123 }
6124 if (tmp == root) {
6125 ctxt->type = XSLT_OUTPUT_HTML;
6126 /*
6127 * REVISIT TODO: XML_HTML_DOCUMENT_NODE is set after the
6128 * transformation on the doc, but functions like
6129 */
6130 res->type = XML_HTML_DOCUMENT_NODE;
6131 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
6132 res->intSubset = xmlCreateIntSubset(res, doctype,
6133 doctypePublic,
6134 doctypeSystem);
6135 #ifdef XSLT_GENERATE_HTML_DOCTYPE
6136 } else if (version != NULL) {
6137 xsltGetHTMLIDs(version, &doctypePublic,
6138 &doctypeSystem);
6139 if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
6140 res->intSubset =
6141 xmlCreateIntSubset(res, doctype,
6142 doctypePublic,
6143 doctypeSystem);
6144 #endif
6145 }
6146 }
6147
6148 }
6149 if (ctxt->type == XSLT_OUTPUT_XML) {
6150 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
6151 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
6152 if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
6153 xmlNodePtr last;
6154 /* Need a small "hack" here to assure DTD comes before
6155 possible comment nodes */
6156 node = res->children;
6157 last = res->last;
6158 res->children = NULL;
6159 res->last = NULL;
6160 res->intSubset = xmlCreateIntSubset(res, doctype,
6161 doctypePublic,
6162 doctypeSystem);
6163 if (res->children != NULL) {
6164 res->children->next = node;
6165 node->prev = res->children;
6166 res->last = last;
6167 } else {
6168 res->children = node;
6169 res->last = last;
6170 }
6171 }
6172 }
6173 }
6174 xmlXPathFreeNodeSet(ctxt->nodeList);
6175 if (profile != NULL) {
6176 xsltSaveProfiling(ctxt, profile);
6177 }
6178
6179 /*
6180 * Be pedantic.
6181 */
6182 if ((ctxt != NULL) && (ctxt->state == XSLT_STATE_ERROR)) {
6183 xmlFreeDoc(res);
6184 res = NULL;
6185 }
6186 if ((res != NULL) && (ctxt != NULL) && (output != NULL)) {
6187 int ret;
6188
6189 ret = xsltCheckWrite(ctxt->sec, ctxt, (const xmlChar *) output);
6190 if (ret == 0) {
6191 xsltTransformError(ctxt, NULL, NULL,
6192 "xsltApplyStylesheet: forbidden to save to %s\n",
6193 output);
6194 } else if (ret < 0) {
6195 xsltTransformError(ctxt, NULL, NULL,
6196 "xsltApplyStylesheet: saving to %s may not be possible\n",
6197 output);
6198 }
6199 }
6200
6201 #ifdef XSLT_DEBUG_PROFILE_CACHE
6202 printf("# Cache:\n");
6203 printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs);
6204 printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars);
6205 #endif
6206
6207 if ((ctxt != NULL) && (userCtxt == NULL))
6208 xsltFreeTransformContext(ctxt);
6209
6210 return (res);
6211
6212 error:
6213 if (res != NULL)
6214 xmlFreeDoc(res);
6215
6216 #ifdef XSLT_DEBUG_PROFILE_CACHE
6217 printf("# Cache:\n");
6218 printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs);
6219 printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars);
6220 #endif
6221
6222 if ((ctxt != NULL) && (userCtxt == NULL))
6223 xsltFreeTransformContext(ctxt);
6224 return (NULL);
6225 }
6226
6227 /**
6228 * xsltApplyStylesheet:
6229 * @style: a parsed XSLT stylesheet
6230 * @doc: a parsed XML document
6231 * @params: a NULL terminated arry of parameters names/values tuples
6232 *
6233 * Apply the stylesheet to the document
6234 * NOTE: This may lead to a non-wellformed output XML wise !
6235 *
6236 * Returns the result document or NULL in case of error
6237 */
6238 xmlDocPtr
6239 xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
6240 const char **params)
6241 {
6242 return (xsltApplyStylesheetInternal(style, doc, params, NULL, NULL, NULL));
6243 }
6244
6245 /**
6246 * xsltProfileStylesheet:
6247 * @style: a parsed XSLT stylesheet
6248 * @doc: a parsed XML document
6249 * @params: a NULL terminated arry of parameters names/values tuples
6250 * @output: a FILE * for the profiling output
6251 *
6252 * Apply the stylesheet to the document and dump the profiling to
6253 * the given output.
6254 *
6255 * Returns the result document or NULL in case of error
6256 */
6257 xmlDocPtr
6258 xsltProfileStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
6259 const char **params, FILE * output)
6260 {
6261 xmlDocPtr res;
6262
6263 res = xsltApplyStylesheetInternal(style, doc, params, NULL, output, NULL);
6264 return (res);
6265 }
6266
6267 /**
6268 * xsltApplyStylesheetUser:
6269 * @style: a parsed XSLT stylesheet
6270 * @doc: a parsed XML document
6271 * @params: a NULL terminated array of parameters names/values tuples
6272 * @output: the targetted output
6273 * @profile: profile FILE * output or NULL
6274 * @userCtxt: user provided transform context
6275 *
6276 * Apply the stylesheet to the document and allow the user to provide
6277 * its own transformation context.
6278 *
6279 * Returns the result document or NULL in case of error
6280 */
6281 xmlDocPtr
6282 xsltApplyStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc,
6283 const char **params, const char *output,
6284 FILE * profile, xsltTransformContextPtr userCtxt)
6285 {
6286 xmlDocPtr res;
6287
6288 res = xsltApplyStylesheetInternal(style, doc, params, output,
6289 profile, userCtxt);
6290 return (res);
6291 }
6292
6293 /**
6294 * xsltRunStylesheetUser:
6295 * @style: a parsed XSLT stylesheet
6296 * @doc: a parsed XML document
6297 * @params: a NULL terminated array of parameters names/values tuples
6298 * @output: the URL/filename ot the generated resource if available
6299 * @SAX: a SAX handler for progressive callback output (not implemented yet)
6300 * @IObuf: an output buffer for progressive output (not implemented yet)
6301 * @profile: profile FILE * output or NULL
6302 * @userCtxt: user provided transform context
6303 *
6304 * Apply the stylesheet to the document and generate the output according
6305 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf.
6306 *
6307 * NOTE: This may lead to a non-wellformed output XML wise !
6308 * NOTE: This may also result in multiple files being generated
6309 * NOTE: using IObuf, the result encoding used will be the one used for
6310 * creating the output buffer, use the following macro to read it
6311 * from the stylesheet
6312 * XSLT_GET_IMPORT_PTR(encoding, style, encoding)
6313 * NOTE: using SAX, any encoding specified in the stylesheet will be lost
6314 * since the interface uses only UTF8
6315 *
6316 * Returns the number of by written to the main resource or -1 in case of
6317 * error.
6318 */
6319 int
6320 xsltRunStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc,
6321 const char **params, const char *output,
6322 xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf,
6323 FILE * profile, xsltTransformContextPtr userCtxt)
6324 {
6325 xmlDocPtr tmp;
6326 int ret;
6327
6328 if ((output == NULL) && (SAX == NULL) && (IObuf == NULL))
6329 return (-1);
6330 if ((SAX != NULL) && (IObuf != NULL))
6331 return (-1);
6332
6333 /* unsupported yet */
6334 if (SAX != NULL) {
6335 XSLT_TODO /* xsltRunStylesheet xmlSAXHandlerPtr SAX */
6336 return (-1);
6337 }
6338
6339 tmp = xsltApplyStylesheetInternal(style, doc, params, output, profile,
6340 userCtxt);
6341 if (tmp == NULL) {
6342 xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
6343 "xsltRunStylesheet : run failed\n");
6344 return (-1);
6345 }
6346 if (IObuf != NULL) {
6347 /* TODO: incomplete, IObuf output not progressive */
6348 ret = xsltSaveResultTo(IObuf, tmp, style);
6349 } else {
6350 ret = xsltSaveResultToFilename(output, tmp, style, 0);
6351 }
6352 xmlFreeDoc(tmp);
6353 return (ret);
6354 }
6355
6356 /**
6357 * xsltRunStylesheet:
6358 * @style: a parsed XSLT stylesheet
6359 * @doc: a parsed XML document
6360 * @params: a NULL terminated array of parameters names/values tuples
6361 * @output: the URL/filename ot the generated resource if available
6362 * @SAX: a SAX handler for progressive callback output (not implemented yet)
6363 * @IObuf: an output buffer for progressive output (not implemented yet)
6364 *
6365 * Apply the stylesheet to the document and generate the output according
6366 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf.
6367 *
6368 * NOTE: This may lead to a non-wellformed output XML wise !
6369 * NOTE: This may also result in multiple files being generated
6370 * NOTE: using IObuf, the result encoding used will be the one used for
6371 * creating the output buffer, use the following macro to read it
6372 * from the stylesheet
6373 * XSLT_GET_IMPORT_PTR(encoding, style, encoding)
6374 * NOTE: using SAX, any encoding specified in the stylesheet will be lost
6375 * since the interface uses only UTF8
6376 *
6377 * Returns the number of bytes written to the main resource or -1 in case of
6378 * error.
6379 */
6380 int
6381 xsltRunStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
6382 const char **params, const char *output,
6383 xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf)
6384 {
6385 return(xsltRunStylesheetUser(style, doc, params, output, SAX, IObuf,
6386 NULL, NULL));
6387 }
6388
6389 /**
6390 * xsltRegisterAllElement:
6391 * @ctxt: the XPath context
6392 *
6393 * Registers all default XSLT elements in this context
6394 */
6395 void
6396 xsltRegisterAllElement(xsltTransformContextPtr ctxt)
6397 {
6398 xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-templates",
6399 XSLT_NAMESPACE,
6400 (xsltTransformFunction) xsltApplyTemplates);
6401 xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-imports",
6402 XSLT_NAMESPACE,
6403 (xsltTransformFunction) xsltApplyImports);
6404 xsltRegisterExtElement(ctxt, (const xmlChar *) "call-template",
6405 XSLT_NAMESPACE,
6406 (xsltTransformFunction) xsltCallTemplate);
6407 xsltRegisterExtElement(ctxt, (const xmlChar *) "element",
6408 XSLT_NAMESPACE,
6409 (xsltTransformFunction) xsltElement);
6410 xsltRegisterExtElement(ctxt, (const xmlChar *) "attribute",
6411 XSLT_NAMESPACE,
6412 (xsltTransformFunction) xsltAttribute);
6413 xsltRegisterExtElement(ctxt, (const xmlChar *) "text",
6414 XSLT_NAMESPACE,
6415 (xsltTransformFunction) xsltText);
6416 xsltRegisterExtElement(ctxt, (const xmlChar *) "processing-instruction",
6417 XSLT_NAMESPACE,
6418 (xsltTransformFunction) xsltProcessingInstruction);
6419 xsltRegisterExtElement(ctxt, (const xmlChar *) "comment",
6420 XSLT_NAMESPACE,
6421 (xsltTransformFunction) xsltComment);
6422 xsltRegisterExtElement(ctxt, (const xmlChar *) "copy",
6423 XSLT_NAMESPACE,
6424 (xsltTransformFunction) xsltCopy);
6425 xsltRegisterExtElement(ctxt, (const xmlChar *) "value-of",
6426 XSLT_NAMESPACE,
6427 (xsltTransformFunction) xsltValueOf);
6428 xsltRegisterExtElement(ctxt, (const xmlChar *) "number",
6429 XSLT_NAMESPACE,
6430 (xsltTransformFunction) xsltNumber);
6431 xsltRegisterExtElement(ctxt, (const xmlChar *) "for-each",
6432 XSLT_NAMESPACE,
6433 (xsltTransformFunction) xsltForEach);
6434 xsltRegisterExtElement(ctxt, (const xmlChar *) "if",
6435 XSLT_NAMESPACE,
6436 (xsltTransformFunction) xsltIf);
6437 xsltRegisterExtElement(ctxt, (const xmlChar *) "choose",
6438 XSLT_NAMESPACE,
6439 (xsltTransformFunction) xsltChoose);
6440 xsltRegisterExtElement(ctxt, (const xmlChar *) "sort",
6441 XSLT_NAMESPACE,
6442 (xsltTransformFunction) xsltSort);
6443 xsltRegisterExtElement(ctxt, (const xmlChar *) "copy-of",
6444 XSLT_NAMESPACE,
6445 (xsltTransformFunction) xsltCopyOf);
6446 xsltRegisterExtElement(ctxt, (const xmlChar *) "message",
6447 XSLT_NAMESPACE,
6448 (xsltTransformFunction) xsltMessage);
6449
6450 /*
6451 * Those don't have callable entry points but are registered anyway
6452 */
6453 xsltRegisterExtElement(ctxt, (const xmlChar *) "variable",
6454 XSLT_NAMESPACE,
6455 (xsltTransformFunction) xsltDebug);
6456 xsltRegisterExtElement(ctxt, (const xmlChar *) "param",
6457 XSLT_NAMESPACE,
6458 (xsltTransformFunction) xsltDebug);
6459 xsltRegisterExtElement(ctxt, (const xmlChar *) "with-param",
6460 XSLT_NAMESPACE,
6461 (xsltTransformFunction) xsltDebug);
6462 xsltRegisterExtElement(ctxt, (const xmlChar *) "decimal-format",
6463 XSLT_NAMESPACE,
6464 (xsltTransformFunction) xsltDebug);
6465 xsltRegisterExtElement(ctxt, (const xmlChar *) "when",
6466 XSLT_NAMESPACE,
6467 (xsltTransformFunction) xsltDebug);
6468 xsltRegisterExtElement(ctxt, (const xmlChar *) "otherwise",
6469 XSLT_NAMESPACE,
6470 (xsltTransformFunction) xsltDebug);
6471 xsltRegisterExtElement(ctxt, (const xmlChar *) "fallback",
6472 XSLT_NAMESPACE,
6473 (xsltTransformFunction) xsltDebug);
6474
6475 }