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