45ceaf16762f89d4d6c7c1e1db297f2b5e1b49ea
[reactos.git] / dll / 3rdparty / libxslt / transform.c
1 /*
2 * transform.c: Implementation of the XSL Transformation 1.0 engine
3 * transform part, i.e. applying a Stylesheet to a document
4 *
5 * References:
6 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 *
8 * Michael Kay "XSLT Programmer's Reference" pp 637-643
9 * Writing Multiple Output Files
10 *
11 * XSLT-1.1 Working Draft
12 * http://www.w3.org/TR/xslt11#multiple-output
13 *
14 * See Copyright for the status of this software.
15 *
16 * daniel@veillard.com
17 */
18
19 #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 * xsltPreCompEval:
310 * @ctxt: transform context
311 * @node: context node
312 * @comp: precompiled expression
313 *
314 * Evaluate a precompiled XPath expression.
315 */
316 static xmlXPathObjectPtr
317 xsltPreCompEval(xsltTransformContextPtr ctxt, xmlNodePtr node,
318 xsltStylePreCompPtr comp) {
319 xmlXPathObjectPtr res;
320 xmlXPathContextPtr xpctxt;
321 xmlNodePtr oldXPContextNode;
322 xmlNsPtr *oldXPNamespaces;
323 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
324
325 xpctxt = ctxt->xpathCtxt;
326 oldXPContextNode = xpctxt->node;
327 oldXPProximityPosition = xpctxt->proximityPosition;
328 oldXPContextSize = xpctxt->contextSize;
329 oldXPNsNr = xpctxt->nsNr;
330 oldXPNamespaces = xpctxt->namespaces;
331
332 xpctxt->node = node;
333 #ifdef XSLT_REFACTORED
334 if (comp->inScopeNs != NULL) {
335 xpctxt->namespaces = comp->inScopeNs->list;
336 xpctxt->nsNr = comp->inScopeNs->xpathNumber;
337 } else {
338 xpctxt->namespaces = NULL;
339 xpctxt->nsNr = 0;
340 }
341 #else
342 xpctxt->namespaces = comp->nsList;
343 xpctxt->nsNr = comp->nsNr;
344 #endif
345
346 res = xmlXPathCompiledEval(comp->comp, xpctxt);
347
348 xpctxt->node = oldXPContextNode;
349 xpctxt->proximityPosition = oldXPProximityPosition;
350 xpctxt->contextSize = oldXPContextSize;
351 xpctxt->nsNr = oldXPNsNr;
352 xpctxt->namespaces = oldXPNamespaces;
353
354 return(res);
355 }
356
357 /**
358 * xsltPreCompEvalToBoolean:
359 * @ctxt: transform context
360 * @node: context node
361 * @comp: precompiled expression
362 *
363 * Evaluate a precompiled XPath expression as boolean.
364 */
365 static int
366 xsltPreCompEvalToBoolean(xsltTransformContextPtr ctxt, xmlNodePtr node,
367 xsltStylePreCompPtr comp) {
368 int res;
369 xmlXPathContextPtr xpctxt;
370 xmlNodePtr oldXPContextNode;
371 xmlNsPtr *oldXPNamespaces;
372 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
373
374 xpctxt = ctxt->xpathCtxt;
375 oldXPContextNode = xpctxt->node;
376 oldXPProximityPosition = xpctxt->proximityPosition;
377 oldXPContextSize = xpctxt->contextSize;
378 oldXPNsNr = xpctxt->nsNr;
379 oldXPNamespaces = xpctxt->namespaces;
380
381 xpctxt->node = node;
382 #ifdef XSLT_REFACTORED
383 if (comp->inScopeNs != NULL) {
384 xpctxt->namespaces = comp->inScopeNs->list;
385 xpctxt->nsNr = comp->inScopeNs->xpathNumber;
386 } else {
387 xpctxt->namespaces = NULL;
388 xpctxt->nsNr = 0;
389 }
390 #else
391 xpctxt->namespaces = comp->nsList;
392 xpctxt->nsNr = comp->nsNr;
393 #endif
394
395 res = xmlXPathCompiledEvalToBoolean(comp->comp, xpctxt);
396
397 xpctxt->node = oldXPContextNode;
398 xpctxt->proximityPosition = oldXPProximityPosition;
399 xpctxt->contextSize = oldXPContextSize;
400 xpctxt->nsNr = oldXPNsNr;
401 xpctxt->namespaces = oldXPNamespaces;
402
403 return(res);
404 }
405
406 /************************************************************************
407 * *
408 * XInclude default settings *
409 * *
410 ************************************************************************/
411
412 static int xsltDoXIncludeDefault = 0;
413
414 /**
415 * xsltSetXIncludeDefault:
416 * @xinclude: whether to do XInclude processing
417 *
418 * Set whether XInclude should be processed on document being loaded by default
419 */
420 void
421 xsltSetXIncludeDefault(int xinclude) {
422 xsltDoXIncludeDefault = (xinclude != 0);
423 }
424
425 /**
426 * xsltGetXIncludeDefault:
427 *
428 * Provides the default state for XInclude processing
429 *
430 * Returns 0 if there is no processing 1 otherwise
431 */
432 int
433 xsltGetXIncludeDefault(void) {
434 return(xsltDoXIncludeDefault);
435 }
436
437 unsigned long xsltDefaultTrace = (unsigned long) XSLT_TRACE_ALL;
438
439 /**
440 * xsltDebugSetDefaultTrace:
441 * @val: tracing level mask
442 *
443 * Set the default debug tracing level mask
444 */
445 void xsltDebugSetDefaultTrace(xsltDebugTraceCodes val) {
446 xsltDefaultTrace = val;
447 }
448
449 /**
450 * xsltDebugGetDefaultTrace:
451 *
452 * Get the current default debug tracing level mask
453 *
454 * Returns the current default debug tracing level mask
455 */
456 xsltDebugTraceCodes xsltDebugGetDefaultTrace() {
457 return xsltDefaultTrace;
458 }
459
460 /************************************************************************
461 * *
462 * Handling of Transformation Contexts *
463 * *
464 ************************************************************************/
465
466 static xsltTransformCachePtr
467 xsltTransformCacheCreate(void)
468 {
469 xsltTransformCachePtr ret;
470
471 ret = (xsltTransformCachePtr) xmlMalloc(sizeof(xsltTransformCache));
472 if (ret == NULL) {
473 xsltTransformError(NULL, NULL, NULL,
474 "xsltTransformCacheCreate : malloc failed\n");
475 return(NULL);
476 }
477 memset(ret, 0, sizeof(xsltTransformCache));
478 return(ret);
479 }
480
481 static void
482 xsltTransformCacheFree(xsltTransformCachePtr cache)
483 {
484 if (cache == NULL)
485 return;
486 /*
487 * Free tree fragments.
488 */
489 if (cache->RVT) {
490 xmlDocPtr tmp, cur = cache->RVT;
491 while (cur) {
492 tmp = cur;
493 cur = (xmlDocPtr) cur->next;
494 if (tmp->_private != NULL) {
495 /*
496 * Tree the document info.
497 */
498 xsltFreeDocumentKeys((xsltDocumentPtr) tmp->_private);
499 xmlFree(tmp->_private);
500 }
501 xmlFreeDoc(tmp);
502 }
503 }
504 /*
505 * Free vars/params.
506 */
507 if (cache->stackItems) {
508 xsltStackElemPtr tmp, cur = cache->stackItems;
509 while (cur) {
510 tmp = cur;
511 cur = cur->next;
512 /*
513 * REVISIT TODO: Should be call a destruction-function
514 * instead?
515 */
516 xmlFree(tmp);
517 }
518 }
519 xmlFree(cache);
520 }
521
522 /**
523 * xsltNewTransformContext:
524 * @style: a parsed XSLT stylesheet
525 * @doc: the input document
526 *
527 * Create a new XSLT TransformContext
528 *
529 * Returns the newly allocated xsltTransformContextPtr or NULL in case of error
530 */
531 xsltTransformContextPtr
532 xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) {
533 xsltTransformContextPtr cur;
534 xsltDocumentPtr docu;
535 int i;
536
537 xsltInitGlobals();
538
539 cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext));
540 if (cur == NULL) {
541 xsltTransformError(NULL, NULL, (xmlNodePtr)doc,
542 "xsltNewTransformContext : malloc failed\n");
543 return(NULL);
544 }
545 memset(cur, 0, sizeof(xsltTransformContext));
546
547 cur->cache = xsltTransformCacheCreate();
548 if (cur->cache == NULL)
549 goto internal_err;
550 /*
551 * setup of the dictionary must be done early as some of the
552 * processing later like key handling may need it.
553 */
554 cur->dict = xmlDictCreateSub(style->dict);
555 cur->internalized = ((style->internalized) && (cur->dict != NULL));
556 #ifdef WITH_XSLT_DEBUG
557 xsltGenericDebug(xsltGenericDebugContext,
558 "Creating sub-dictionary from stylesheet for transformation\n");
559 #endif
560
561 /*
562 * initialize the template stack
563 */
564 cur->templTab = (xsltTemplatePtr *)
565 xmlMalloc(10 * sizeof(xsltTemplatePtr));
566 if (cur->templTab == NULL) {
567 xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
568 "xsltNewTransformContext: out of memory\n");
569 goto internal_err;
570 }
571 cur->templNr = 0;
572 cur->templMax = 5;
573 cur->templ = NULL;
574 cur->maxTemplateDepth = xsltMaxDepth;
575
576 /*
577 * initialize the variables stack
578 */
579 cur->varsTab = (xsltStackElemPtr *)
580 xmlMalloc(10 * sizeof(xsltStackElemPtr));
581 if (cur->varsTab == NULL) {
582 xmlGenericError(xmlGenericErrorContext,
583 "xsltNewTransformContext: out of memory\n");
584 goto internal_err;
585 }
586 cur->varsNr = 0;
587 cur->varsMax = 10;
588 cur->vars = NULL;
589 cur->varsBase = 0;
590 cur->maxTemplateVars = xsltMaxVars;
591
592 /*
593 * the profiling stack is not initialized by default
594 */
595 cur->profTab = NULL;
596 cur->profNr = 0;
597 cur->profMax = 0;
598 cur->prof = 0;
599
600 cur->style = style;
601 xmlXPathInit();
602 cur->xpathCtxt = xmlXPathNewContext(doc);
603 if (cur->xpathCtxt == NULL) {
604 xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
605 "xsltNewTransformContext : xmlXPathNewContext failed\n");
606 goto internal_err;
607 }
608 /*
609 * Create an XPath cache.
610 */
611 if (xmlXPathContextSetCache(cur->xpathCtxt, 1, -1, 0) == -1)
612 goto internal_err;
613 /*
614 * Initialize the extras array
615 */
616 if (style->extrasNr != 0) {
617 cur->extrasMax = style->extrasNr + 20;
618 cur->extras = (xsltRuntimeExtraPtr)
619 xmlMalloc(cur->extrasMax * sizeof(xsltRuntimeExtra));
620 if (cur->extras == NULL) {
621 xmlGenericError(xmlGenericErrorContext,
622 "xsltNewTransformContext: out of memory\n");
623 goto internal_err;
624 }
625 cur->extrasNr = style->extrasNr;
626 for (i = 0;i < cur->extrasMax;i++) {
627 cur->extras[i].info = NULL;
628 cur->extras[i].deallocate = NULL;
629 cur->extras[i].val.ptr = NULL;
630 }
631 } else {
632 cur->extras = NULL;
633 cur->extrasNr = 0;
634 cur->extrasMax = 0;
635 }
636
637 XSLT_REGISTER_VARIABLE_LOOKUP(cur);
638 XSLT_REGISTER_FUNCTION_LOOKUP(cur);
639 cur->xpathCtxt->nsHash = style->nsHash;
640 /*
641 * Initialize the registered external modules
642 */
643 xsltInitCtxtExts(cur);
644 /*
645 * Setup document element ordering for later efficiencies
646 * (bug 133289)
647 */
648 if (xslDebugStatus == XSLT_DEBUG_NONE)
649 xmlXPathOrderDocElems(doc);
650 /*
651 * Must set parserOptions before calling xsltNewDocument
652 * (bug 164530)
653 */
654 cur->parserOptions = XSLT_PARSE_OPTIONS;
655 docu = xsltNewDocument(cur, doc);
656 if (docu == NULL) {
657 xsltTransformError(cur, NULL, (xmlNodePtr)doc,
658 "xsltNewTransformContext : xsltNewDocument failed\n");
659 goto internal_err;
660 }
661 docu->main = 1;
662 cur->document = docu;
663 cur->inst = NULL;
664 cur->outputFile = NULL;
665 cur->sec = xsltGetDefaultSecurityPrefs();
666 cur->debugStatus = xslDebugStatus;
667 cur->traceCode = (unsigned long*) &xsltDefaultTrace;
668 cur->xinclude = xsltGetXIncludeDefault();
669 cur->keyInitLevel = 0;
670
671 return(cur);
672
673 internal_err:
674 if (cur != NULL)
675 xsltFreeTransformContext(cur);
676 return(NULL);
677 }
678
679 /**
680 * xsltFreeTransformContext:
681 * @ctxt: an XSLT parser context
682 *
683 * Free up the memory allocated by @ctxt
684 */
685 void
686 xsltFreeTransformContext(xsltTransformContextPtr ctxt) {
687 if (ctxt == NULL)
688 return;
689
690 /*
691 * Shutdown the extension modules associated to the stylesheet
692 * used if needed.
693 */
694 xsltShutdownCtxtExts(ctxt);
695
696 if (ctxt->xpathCtxt != NULL) {
697 ctxt->xpathCtxt->nsHash = NULL;
698 xmlXPathFreeContext(ctxt->xpathCtxt);
699 }
700 if (ctxt->templTab != NULL)
701 xmlFree(ctxt->templTab);
702 if (ctxt->varsTab != NULL)
703 xmlFree(ctxt->varsTab);
704 if (ctxt->profTab != NULL)
705 xmlFree(ctxt->profTab);
706 if ((ctxt->extrasNr > 0) && (ctxt->extras != NULL)) {
707 int i;
708
709 for (i = 0;i < ctxt->extrasNr;i++) {
710 if ((ctxt->extras[i].deallocate != NULL) &&
711 (ctxt->extras[i].info != NULL))
712 ctxt->extras[i].deallocate(ctxt->extras[i].info);
713 }
714 xmlFree(ctxt->extras);
715 }
716 xsltFreeGlobalVariables(ctxt);
717 xsltFreeDocuments(ctxt);
718 xsltFreeCtxtExts(ctxt);
719 xsltFreeRVTs(ctxt);
720 xsltTransformCacheFree(ctxt->cache);
721 xmlDictFree(ctxt->dict);
722 #ifdef WITH_XSLT_DEBUG
723 xsltGenericDebug(xsltGenericDebugContext,
724 "freeing transformation dictionary\n");
725 #endif
726 memset(ctxt, -1, sizeof(xsltTransformContext));
727 xmlFree(ctxt);
728 }
729
730 /************************************************************************
731 * *
732 * Copy of Nodes in an XSLT fashion *
733 * *
734 ************************************************************************/
735
736 xmlNodePtr xsltCopyTree(xsltTransformContextPtr ctxt,
737 xmlNodePtr node, xmlNodePtr insert, int literal);
738
739 /**
740 * xsltAddChild:
741 * @parent: the parent node
742 * @cur: the child node
743 *
744 * Wrapper version of xmlAddChild with a more consistent behaviour on
745 * error. One expect the use to be child = xsltAddChild(parent, child);
746 * and the routine will take care of not leaking on errors or node merge
747 *
748 * Returns the child is successfully attached or NULL if merged or freed
749 */
750 static xmlNodePtr
751 xsltAddChild(xmlNodePtr parent, xmlNodePtr cur) {
752 xmlNodePtr ret;
753
754 if ((cur == NULL) || (parent == NULL))
755 return(NULL);
756 if (parent == NULL) {
757 xmlFreeNode(cur);
758 return(NULL);
759 }
760 ret = xmlAddChild(parent, cur);
761
762 return(ret);
763 }
764
765 /**
766 * xsltAddTextString:
767 * @ctxt: a XSLT process context
768 * @target: the text node where the text will be attached
769 * @string: the text string
770 * @len: the string length in byte
771 *
772 * Extend the current text node with the new string, it handles coalescing
773 *
774 * Returns: the text node
775 */
776 static xmlNodePtr
777 xsltAddTextString(xsltTransformContextPtr ctxt, xmlNodePtr target,
778 const xmlChar *string, int len) {
779 /*
780 * optimization
781 */
782 if ((len <= 0) || (string == NULL) || (target == NULL))
783 return(target);
784
785 if (ctxt->lasttext == target->content) {
786
787 if (ctxt->lasttuse + len >= ctxt->lasttsize) {
788 xmlChar *newbuf;
789 int size;
790
791 size = ctxt->lasttsize + len + 100;
792 size *= 2;
793 newbuf = (xmlChar *) xmlRealloc(target->content,size);
794 if (newbuf == NULL) {
795 xsltTransformError(ctxt, NULL, target,
796 "xsltCopyText: text allocation failed\n");
797 return(NULL);
798 }
799 ctxt->lasttsize = size;
800 ctxt->lasttext = newbuf;
801 target->content = newbuf;
802 }
803 memcpy(&(target->content[ctxt->lasttuse]), string, len);
804 ctxt->lasttuse += len;
805 target->content[ctxt->lasttuse] = 0;
806 } else {
807 xmlNodeAddContent(target, string);
808 ctxt->lasttext = target->content;
809 len = xmlStrlen(target->content);
810 ctxt->lasttsize = len;
811 ctxt->lasttuse = len;
812 }
813 return(target);
814 }
815
816 /**
817 * xsltCopyTextString:
818 * @ctxt: a XSLT process context
819 * @target: the element where the text will be attached
820 * @string: the text string
821 * @noescape: should disable-escaping be activated for this text node.
822 *
823 * Adds @string to a newly created or an existent text node child of
824 * @target.
825 *
826 * Returns: the text node, where the text content of @cur is copied to.
827 * NULL in case of API or internal errors.
828 */
829 xmlNodePtr
830 xsltCopyTextString(xsltTransformContextPtr ctxt, xmlNodePtr target,
831 const xmlChar *string, int noescape)
832 {
833 xmlNodePtr copy;
834 int len;
835
836 if (string == NULL)
837 return(NULL);
838
839 #ifdef WITH_XSLT_DEBUG_PROCESS
840 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
841 "xsltCopyTextString: copy text %s\n",
842 string));
843 #endif
844
845 /*
846 * Play safe and reset the merging mechanism for every new
847 * target node.
848 */
849 if ((target == NULL) || (target->children == NULL)) {
850 ctxt->lasttext = NULL;
851 }
852
853 /* handle coalescing of text nodes here */
854 len = xmlStrlen(string);
855 if ((ctxt->type == XSLT_OUTPUT_XML) &&
856 (ctxt->style->cdataSection != NULL) &&
857 (target != NULL) &&
858 (target->type == XML_ELEMENT_NODE) &&
859 (((target->ns == NULL) &&
860 (xmlHashLookup2(ctxt->style->cdataSection,
861 target->name, NULL) != NULL)) ||
862 ((target->ns != NULL) &&
863 (xmlHashLookup2(ctxt->style->cdataSection,
864 target->name, target->ns->href) != NULL))))
865 {
866 /*
867 * Process "cdata-section-elements".
868 */
869 if ((target->last != NULL) &&
870 (target->last->type == XML_CDATA_SECTION_NODE))
871 {
872 return(xsltAddTextString(ctxt, target->last, string, len));
873 }
874 copy = xmlNewCDataBlock(ctxt->output, string, len);
875 } else if (noescape) {
876 /*
877 * Process "disable-output-escaping".
878 */
879 if ((target != NULL) && (target->last != NULL) &&
880 (target->last->type == XML_TEXT_NODE) &&
881 (target->last->name == xmlStringTextNoenc))
882 {
883 return(xsltAddTextString(ctxt, target->last, string, len));
884 }
885 copy = xmlNewTextLen(string, len);
886 if (copy != NULL)
887 copy->name = xmlStringTextNoenc;
888 } else {
889 /*
890 * Default processing.
891 */
892 if ((target != NULL) && (target->last != NULL) &&
893 (target->last->type == XML_TEXT_NODE) &&
894 (target->last->name == xmlStringText)) {
895 return(xsltAddTextString(ctxt, target->last, string, len));
896 }
897 copy = xmlNewTextLen(string, len);
898 }
899 if (copy != NULL && target != NULL)
900 copy = xsltAddChild(target, copy);
901 if (copy != NULL) {
902 ctxt->lasttext = copy->content;
903 ctxt->lasttsize = len;
904 ctxt->lasttuse = len;
905 } else {
906 xsltTransformError(ctxt, NULL, target,
907 "xsltCopyTextString: text copy failed\n");
908 ctxt->lasttext = NULL;
909 }
910 return(copy);
911 }
912
913 /**
914 * xsltCopyText:
915 * @ctxt: a XSLT process context
916 * @target: the element where the text will be attached
917 * @cur: the text or CDATA node
918 * @interned: the string is in the target doc dictionary
919 *
920 * Copy the text content of @cur and append it to @target's children.
921 *
922 * Returns: the text node, where the text content of @cur is copied to.
923 * NULL in case of API or internal errors.
924 */
925 static xmlNodePtr
926 xsltCopyText(xsltTransformContextPtr ctxt, xmlNodePtr target,
927 xmlNodePtr cur, int interned)
928 {
929 xmlNodePtr copy;
930
931 if ((cur->type != XML_TEXT_NODE) &&
932 (cur->type != XML_CDATA_SECTION_NODE))
933 return(NULL);
934 if (cur->content == NULL)
935 return(NULL);
936
937 #ifdef WITH_XSLT_DEBUG_PROCESS
938 if (cur->type == XML_CDATA_SECTION_NODE) {
939 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
940 "xsltCopyText: copy CDATA text %s\n",
941 cur->content));
942 } else if (cur->name == xmlStringTextNoenc) {
943 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
944 "xsltCopyText: copy unescaped text %s\n",
945 cur->content));
946 } else {
947 XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
948 "xsltCopyText: copy text %s\n",
949 cur->content));
950 }
951 #endif
952
953 /*
954 * Play save and reset the merging mechanism for every new
955 * target node.
956 */
957 if ((target == NULL) || (target->children == NULL)) {
958 ctxt->lasttext = NULL;
959 }
960
961 if ((ctxt->style->cdataSection != NULL) &&
962 (ctxt->type == XSLT_OUTPUT_XML) &&
963 (target != NULL) &&
964 (target->type == XML_ELEMENT_NODE) &&
965 (((target->ns == NULL) &&
966 (xmlHashLookup2(ctxt->style->cdataSection,
967 target->name, NULL) != NULL)) ||
968 ((target->ns != NULL) &&
969 (xmlHashLookup2(ctxt->style->cdataSection,
970 target->name, target->ns->href) != NULL))))
971 {
972 /*
973 * Process "cdata-section-elements".
974 */
975 /*
976 * OPTIMIZE TODO: xsltCopyText() is also used for attribute content.
977 */
978 /*
979 * TODO: Since this doesn't merge adjacent CDATA-section nodes,
980 * we'll get: <![CDATA[x]]><!CDATA[y]]>.
981 * TODO: Reported in #321505.
982 */
983 if ((target->last != NULL) &&
984 (target->last->type == XML_CDATA_SECTION_NODE))
985 {
986 /*
987 * Append to existing CDATA-section node.
988 */
989 copy = xsltAddTextString(ctxt, target->last, cur->content,
990 xmlStrlen(cur->content));
991 goto exit;
992 } else {
993 unsigned int len;
994
995 len = xmlStrlen(cur->content);
996 copy = xmlNewCDataBlock(ctxt->output, cur->content, len);
997 if (copy == NULL)
998 goto exit;
999 ctxt->lasttext = copy->content;
1000 ctxt->lasttsize = len;
1001 ctxt->lasttuse = len;
1002 }
1003 } else if ((target != NULL) &&
1004 (target->last != NULL) &&
1005 /* both escaped or both non-escaped text-nodes */
1006 (((target->last->type == XML_TEXT_NODE) &&
1007 (target->last->name == cur->name)) ||
1008 /* non-escaped text nodes and CDATA-section nodes */
1009 (((target->last->type == XML_CDATA_SECTION_NODE) &&
1010 (cur->name == xmlStringTextNoenc)))))
1011 {
1012 /*
1013 * we are appending to an existing text node
1014 */
1015 copy = xsltAddTextString(ctxt, target->last, cur->content,
1016 xmlStrlen(cur->content));
1017 goto exit;
1018 } else if ((interned) && (target != NULL) &&
1019 (target->doc != NULL) &&
1020 (target->doc->dict == ctxt->dict))
1021 {
1022 /*
1023 * TODO: DO we want to use this also for "text" output?
1024 */
1025 copy = xmlNewTextLen(NULL, 0);
1026 if (copy == NULL)
1027 goto exit;
1028 if (cur->name == xmlStringTextNoenc)
1029 copy->name = xmlStringTextNoenc;
1030
1031 /*
1032 * Must confirm that content is in dict (bug 302821)
1033 * TODO: This check should be not needed for text coming
1034 * from the stylesheets
1035 */
1036 if (xmlDictOwns(ctxt->dict, cur->content))
1037 copy->content = cur->content;
1038 else {
1039 if ((copy->content = xmlStrdup(cur->content)) == NULL)
1040 return NULL;
1041 }
1042 } else {
1043 /*
1044 * normal processing. keep counters to extend the text node
1045 * in xsltAddTextString if needed.
1046 */
1047 unsigned int len;
1048
1049 len = xmlStrlen(cur->content);
1050 copy = xmlNewTextLen(cur->content, len);
1051 if (copy == NULL)
1052 goto exit;
1053 if (cur->name == xmlStringTextNoenc)
1054 copy->name = xmlStringTextNoenc;
1055 ctxt->lasttext = copy->content;
1056 ctxt->lasttsize = len;
1057 ctxt->lasttuse = len;
1058 }
1059 if (copy != NULL) {
1060 if (target != NULL) {
1061 copy->doc = target->doc;
1062 /*
1063 * MAYBE TODO: Maybe we should reset the ctxt->lasttext here
1064 * to ensure that the optimized text-merging mechanism
1065 * won't interfere with normal node-merging in any case.
1066 */
1067 copy = xsltAddChild(target, copy);
1068 }
1069 } else {
1070 xsltTransformError(ctxt, NULL, target,
1071 "xsltCopyText: text copy failed\n");
1072 }
1073
1074 exit:
1075 if ((copy == NULL) || (copy->content == NULL)) {
1076 xsltTransformError(ctxt, NULL, target,
1077 "Internal error in xsltCopyText(): "
1078 "Failed to copy the string.\n");
1079 ctxt->state = XSLT_STATE_STOPPED;
1080 }
1081 return(copy);
1082 }
1083
1084 /**
1085 * xsltShallowCopyAttr:
1086 * @ctxt: a XSLT process context
1087 * @invocNode: responsible node in the stylesheet; used for error reports
1088 * @target: the element where the attribute will be grafted
1089 * @attr: the attribute to be copied
1090 *
1091 * Do a copy of an attribute.
1092 * Called by:
1093 * - xsltCopyTreeInternal()
1094 * - xsltCopyOf()
1095 * - xsltCopy()
1096 *
1097 * Returns: a new xmlAttrPtr, or NULL in case of error.
1098 */
1099 static xmlAttrPtr
1100 xsltShallowCopyAttr(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
1101 xmlNodePtr target, xmlAttrPtr attr)
1102 {
1103 xmlAttrPtr copy;
1104 xmlChar *value;
1105
1106 if (attr == NULL)
1107 return(NULL);
1108
1109 if (target->type != XML_ELEMENT_NODE) {
1110 xsltTransformError(ctxt, NULL, invocNode,
1111 "Cannot add an attribute node to a non-element node.\n");
1112 return(NULL);
1113 }
1114
1115 if (target->children != NULL) {
1116 xsltTransformError(ctxt, NULL, invocNode,
1117 "Attribute nodes must be added before "
1118 "any child nodes to an element.\n");
1119 return(NULL);
1120 }
1121
1122 value = xmlNodeListGetString(attr->doc, attr->children, 1);
1123 if (attr->ns != NULL) {
1124 xmlNsPtr ns;
1125
1126 ns = xsltGetSpecialNamespace(ctxt, invocNode,
1127 attr->ns->href, attr->ns->prefix, target);
1128 if (ns == NULL) {
1129 xsltTransformError(ctxt, NULL, invocNode,
1130 "Namespace fixup error: Failed to acquire an in-scope "
1131 "namespace binding of the copied attribute '{%s}%s'.\n",
1132 attr->ns->href, attr->name);
1133 /*
1134 * TODO: Should we just stop here?
1135 */
1136 }
1137 /*
1138 * Note that xmlSetNsProp() will take care of duplicates
1139 * and assigns the new namespace even to a duplicate.
1140 */
1141 copy = xmlSetNsProp(target, ns, attr->name, value);
1142 } else {
1143 copy = xmlSetNsProp(target, NULL, attr->name, value);
1144 }
1145 if (value != NULL)
1146 xmlFree(value);
1147
1148 if (copy == NULL)
1149 return(NULL);
1150
1151 #if 0
1152 /*
1153 * NOTE: This was optimized according to bug #342695.
1154 * TODO: Can this further be optimized, if source and target
1155 * share the same dict and attr->children is just 1 text node
1156 * which is in the dict? How probable is such a case?
1157 */
1158 /*
1159 * TODO: Do we need to create an empty text node if the value
1160 * is the empty string?
1161 */
1162 value = xmlNodeListGetString(attr->doc, attr->children, 1);
1163 if (value != NULL) {
1164 txtNode = xmlNewDocText(target->doc, NULL);
1165 if (txtNode == NULL)
1166 return(NULL);
1167 if ((target->doc != NULL) &&
1168 (target->doc->dict != NULL))
1169 {
1170 txtNode->content =
1171 (xmlChar *) xmlDictLookup(target->doc->dict,
1172 BAD_CAST value, -1);
1173 xmlFree(value);
1174 } else
1175 txtNode->content = value;
1176 copy->children = txtNode;
1177 }
1178 #endif
1179
1180 return(copy);
1181 }
1182
1183 /**
1184 * xsltCopyAttrListNoOverwrite:
1185 * @ctxt: a XSLT process context
1186 * @invocNode: responsible node in the stylesheet; used for error reports
1187 * @target: the element where the new attributes will be grafted
1188 * @attr: the first attribute in the list to be copied
1189 *
1190 * Copies a list of attribute nodes, starting with @attr, over to the
1191 * @target element node.
1192 *
1193 * Called by:
1194 * - xsltCopyTreeInternal()
1195 *
1196 * Returns 0 on success and -1 on errors and internal errors.
1197 */
1198 static int
1199 xsltCopyAttrListNoOverwrite(xsltTransformContextPtr ctxt,
1200 xmlNodePtr invocNode,
1201 xmlNodePtr target, xmlAttrPtr attr)
1202 {
1203 xmlAttrPtr copy;
1204 xmlNsPtr origNs = NULL, copyNs = NULL;
1205 xmlChar *value;
1206
1207 /*
1208 * Don't use xmlCopyProp() here, since it will try to
1209 * reconciliate namespaces.
1210 */
1211 while (attr != NULL) {
1212 /*
1213 * Find a namespace node in the tree of @target.
1214 * Avoid searching for the same ns.
1215 */
1216 if (attr->ns != origNs) {
1217 origNs = attr->ns;
1218 if (attr->ns != NULL) {
1219 copyNs = xsltGetSpecialNamespace(ctxt, invocNode,
1220 attr->ns->href, attr->ns->prefix, target);
1221 if (copyNs == NULL)
1222 return(-1);
1223 } else
1224 copyNs = NULL;
1225 }
1226 /*
1227 * If attribute has a value, we need to copy it (watching out
1228 * for possible entities)
1229 */
1230 if ((attr->children) && (attr->children->type == XML_TEXT_NODE) &&
1231 (attr->children->next == NULL)) {
1232 copy = xmlNewNsProp(target, copyNs, attr->name,
1233 attr->children->content);
1234 } else if (attr->children != NULL) {
1235 value = xmlNodeListGetString(attr->doc, attr->children, 1);
1236 copy = xmlNewNsProp(target, copyNs, attr->name, BAD_CAST value);
1237 xmlFree(value);
1238 } else {
1239 copy = xmlNewNsProp(target, copyNs, attr->name, NULL);
1240 }
1241
1242 if (copy == NULL)
1243 return(-1);
1244
1245 attr = attr->next;
1246 }
1247 return(0);
1248 }
1249
1250 /**
1251 * xsltShallowCopyElem:
1252 * @ctxt: the XSLT process context
1253 * @node: the element node in the source tree
1254 * or the Literal Result Element
1255 * @insert: the parent in the result tree
1256 * @isLRE: if @node is a Literal Result Element
1257 *
1258 * Make a copy of the element node @node
1259 * and insert it as last child of @insert.
1260 *
1261 * URGENT TODO: The problem with this one (for the non-refactored code)
1262 * is that it is used for both, Literal Result Elements *and*
1263 * copying input nodes.
1264 *
1265 * BIG NOTE: This is only called for XML_ELEMENT_NODEs.
1266 *
1267 * Called from:
1268 * xsltApplySequenceConstructor()
1269 * (for Literal Result Elements - which is a problem)
1270 * xsltCopy() (for shallow-copying elements via xsl:copy)
1271 *
1272 * Returns a pointer to the new node, or NULL in case of error
1273 */
1274 static xmlNodePtr
1275 xsltShallowCopyElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
1276 xmlNodePtr insert, int isLRE)
1277 {
1278 xmlNodePtr copy;
1279
1280 if ((node->type == XML_DTD_NODE) || (insert == NULL))
1281 return(NULL);
1282 if ((node->type == XML_TEXT_NODE) ||
1283 (node->type == XML_CDATA_SECTION_NODE))
1284 return(xsltCopyText(ctxt, insert, node, 0));
1285
1286 copy = xmlDocCopyNode(node, insert->doc, 0);
1287 if (copy != NULL) {
1288 copy->doc = ctxt->output;
1289 copy = xsltAddChild(insert, copy);
1290 if (copy == NULL) {
1291 xsltTransformError(ctxt, NULL, node,
1292 "xsltShallowCopyElem: copy failed\n");
1293 return (copy);
1294 }
1295
1296 if (node->type == XML_ELEMENT_NODE) {
1297 /*
1298 * Add namespaces as they are needed
1299 */
1300 if (node->nsDef != NULL) {
1301 /*
1302 * TODO: Remove the LRE case in the refactored code
1303 * gets enabled.
1304 */
1305 if (isLRE)
1306 xsltCopyNamespaceList(ctxt, copy, node->nsDef);
1307 else
1308 xsltCopyNamespaceListInternal(copy, node->nsDef);
1309 }
1310
1311 /*
1312 * URGENT TODO: The problem with this is that it does not
1313 * copy over all namespace nodes in scope.
1314 * The damn thing about this is, that we would need to
1315 * use the xmlGetNsList(), for every single node; this is
1316 * also done in xsltCopyTreeInternal(), but only for the top node.
1317 */
1318 if (node->ns != NULL) {
1319 if (isLRE) {
1320 /*
1321 * REVISIT TODO: Since the non-refactored code still does
1322 * ns-aliasing, we need to call xsltGetNamespace() here.
1323 * Remove this when ready.
1324 */
1325 copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy);
1326 } else {
1327 copy->ns = xsltGetSpecialNamespace(ctxt,
1328 node, node->ns->href, node->ns->prefix, copy);
1329
1330 }
1331 } else if ((insert->type == XML_ELEMENT_NODE) &&
1332 (insert->ns != NULL))
1333 {
1334 /*
1335 * "Undeclare" the default namespace.
1336 */
1337 xsltGetSpecialNamespace(ctxt, node, NULL, NULL, copy);
1338 }
1339 }
1340 } else {
1341 xsltTransformError(ctxt, NULL, node,
1342 "xsltShallowCopyElem: copy %s failed\n", node->name);
1343 }
1344 return(copy);
1345 }
1346
1347 /**
1348 * xsltCopyTreeList:
1349 * @ctxt: a XSLT process context
1350 * @invocNode: responsible node in the stylesheet; used for error reports
1351 * @list: the list of element nodes in the source tree.
1352 * @insert: the parent in the result tree.
1353 * @isLRE: is this a literal result element list
1354 * @topElemVisited: indicates if a top-most element was already processed
1355 *
1356 * Make a copy of the full list of tree @list
1357 * and insert it as last children of @insert
1358 *
1359 * NOTE: Not to be used for Literal Result Elements.
1360 *
1361 * Used by:
1362 * - xsltCopyOf()
1363 *
1364 * Returns a pointer to the new list, or NULL in case of error
1365 */
1366 static xmlNodePtr
1367 xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
1368 xmlNodePtr list,
1369 xmlNodePtr insert, int isLRE, int topElemVisited)
1370 {
1371 xmlNodePtr copy, ret = NULL;
1372
1373 while (list != NULL) {
1374 copy = xsltCopyTreeInternal(ctxt, invocNode,
1375 list, insert, isLRE, topElemVisited);
1376 if (copy != NULL) {
1377 if (ret == NULL) {
1378 ret = copy;
1379 }
1380 }
1381 list = list->next;
1382 }
1383 return(ret);
1384 }
1385
1386 /**
1387 * xsltCopyNamespaceListInternal:
1388 * @node: the target node
1389 * @cur: the first namespace
1390 *
1391 * Do a copy of a namespace list. If @node is non-NULL the
1392 * new namespaces are added automatically.
1393 * Called by:
1394 * xsltCopyTreeInternal()
1395 *
1396 * QUESTION: What is the exact difference between this function
1397 * and xsltCopyNamespaceList() in "namespaces.c"?
1398 * ANSWER: xsltCopyNamespaceList() tries to apply ns-aliases.
1399 *
1400 * Returns: a new xmlNsPtr, or NULL in case of error.
1401 */
1402 static xmlNsPtr
1403 xsltCopyNamespaceListInternal(xmlNodePtr elem, xmlNsPtr ns) {
1404 xmlNsPtr ret = NULL;
1405 xmlNsPtr p = NULL, q, luNs;
1406
1407 if (ns == NULL)
1408 return(NULL);
1409 /*
1410 * One can add namespaces only on element nodes
1411 */
1412 if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE))
1413 elem = NULL;
1414
1415 do {
1416 if (ns->type != XML_NAMESPACE_DECL)
1417 break;
1418 /*
1419 * Avoid duplicating namespace declarations on the tree.
1420 */
1421 if (elem != NULL) {
1422 if ((elem->ns != NULL) &&
1423 xmlStrEqual(elem->ns->prefix, ns->prefix) &&
1424 xmlStrEqual(elem->ns->href, ns->href))
1425 {
1426 ns = ns->next;
1427 continue;
1428 }
1429 luNs = xmlSearchNs(elem->doc, elem, ns->prefix);
1430 if ((luNs != NULL) && (xmlStrEqual(luNs->href, ns->href)))
1431 {
1432 ns = ns->next;
1433 continue;
1434 }
1435 }
1436 q = xmlNewNs(elem, ns->href, ns->prefix);
1437 if (p == NULL) {
1438 ret = p = q;
1439 } else if (q != NULL) {
1440 p->next = q;
1441 p = q;
1442 }
1443 ns = ns->next;
1444 } while (ns != NULL);
1445 return(ret);
1446 }
1447
1448 /**
1449 * xsltShallowCopyNsNode:
1450 * @ctxt: the XSLT transformation context
1451 * @invocNode: responsible node in the stylesheet; used for error reports
1452 * @insert: the target element node in the result tree
1453 * @ns: the namespace node
1454 *
1455 * This is used for copying ns-nodes with xsl:copy-of and xsl:copy.
1456 *
1457 * Returns a new/existing ns-node, or NULL.
1458 */
1459 static xmlNsPtr
1460 xsltShallowCopyNsNode(xsltTransformContextPtr ctxt,
1461 xmlNodePtr invocNode,
1462 xmlNodePtr insert,
1463 xmlNsPtr ns)
1464 {
1465 /*
1466 * TODO: Contrary to header comments, this is declared as int.
1467 * be modified to return a node pointer, or NULL if any error
1468 */
1469 xmlNsPtr tmpns;
1470
1471 if ((insert == NULL) || (insert->type != XML_ELEMENT_NODE))
1472 return(NULL);
1473
1474 if (insert->children != NULL) {
1475 xsltTransformError(ctxt, NULL, invocNode,
1476 "Namespace nodes must be added before "
1477 "any child nodes are added to an element.\n");
1478 return(NULL);
1479 }
1480 /*
1481 * BIG NOTE: Xalan-J simply overwrites any ns-decls with
1482 * an equal prefix. We definitively won't do that.
1483 *
1484 * MSXML 4.0 and the .NET ignores ns-decls for which an
1485 * equal prefix is already in use.
1486 *
1487 * Saxon raises an error like:
1488 * "net.sf.saxon.xpath.DynamicError: Cannot create two namespace
1489 * nodes with the same name".
1490 *
1491 * NOTE: We'll currently follow MSXML here.
1492 * REVISIT TODO: Check if it's better to follow Saxon here.
1493 */
1494 if (ns->prefix == NULL) {
1495 /*
1496 * If we are adding ns-nodes to an element using e.g.
1497 * <xsl:copy-of select="/foo/namespace::*">, then we need
1498 * to ensure that we don't incorrectly declare a default
1499 * namespace on an element in no namespace, which otherwise
1500 * would move the element incorrectly into a namespace, if
1501 * the node tree is serialized.
1502 */
1503 if (insert->ns == NULL)
1504 goto occupied;
1505 } else if ((ns->prefix[0] == 'x') &&
1506 xmlStrEqual(ns->prefix, BAD_CAST "xml"))
1507 {
1508 /*
1509 * The XML namespace is built in.
1510 */
1511 return(NULL);
1512 }
1513
1514 if (insert->nsDef != NULL) {
1515 tmpns = insert->nsDef;
1516 do {
1517 if ((tmpns->prefix == NULL) == (ns->prefix == NULL)) {
1518 if ((tmpns->prefix == ns->prefix) ||
1519 xmlStrEqual(tmpns->prefix, ns->prefix))
1520 {
1521 /*
1522 * Same prefix.
1523 */
1524 if (xmlStrEqual(tmpns->href, ns->href))
1525 return(NULL);
1526 goto occupied;
1527 }
1528 }
1529 tmpns = tmpns->next;
1530 } while (tmpns != NULL);
1531 }
1532 tmpns = xmlSearchNs(insert->doc, insert, ns->prefix);
1533 if ((tmpns != NULL) && xmlStrEqual(tmpns->href, ns->href))
1534 return(NULL);
1535 /*
1536 * Declare a new namespace.
1537 * TODO: The problem (wrt efficiency) with this xmlNewNs() is
1538 * that it will again search the already declared namespaces
1539 * for a duplicate :-/
1540 */
1541 return(xmlNewNs(insert, ns->href, ns->prefix));
1542
1543 occupied:
1544 /*
1545 * TODO: We could as well raise an error here (like Saxon does),
1546 * or at least generate a warning.
1547 */
1548 return(NULL);
1549 }
1550
1551 /**
1552 * xsltCopyTreeInternal:
1553 * @ctxt: the XSLT transformation context
1554 * @invocNode: responsible node in the stylesheet; used for error reports
1555 * @node: the element node in the source tree
1556 * @insert: the parent in the result tree
1557 * @isLRE: indicates if @node is a Literal Result Element
1558 * @topElemVisited: indicates if a top-most element was already processed
1559 *
1560 * Make a copy of the full tree under the element node @node
1561 * and insert it as last child of @insert
1562 *
1563 * NOTE: Not to be used for Literal Result Elements.
1564 *
1565 * Used by:
1566 * - xsltCopyOf()
1567 *
1568 * Returns a pointer to the new tree, or NULL in case of error
1569 */
1570 static xmlNodePtr
1571 xsltCopyTreeInternal(xsltTransformContextPtr ctxt,
1572 xmlNodePtr invocNode,
1573 xmlNodePtr node,
1574 xmlNodePtr insert, int isLRE, int topElemVisited)
1575 {
1576 xmlNodePtr copy;
1577
1578 if (node == NULL)
1579 return(NULL);
1580 switch (node->type) {
1581 case XML_ELEMENT_NODE:
1582 case XML_ENTITY_REF_NODE:
1583 case XML_ENTITY_NODE:
1584 case XML_PI_NODE:
1585 case XML_COMMENT_NODE:
1586 case XML_DOCUMENT_NODE:
1587 case XML_HTML_DOCUMENT_NODE:
1588 #ifdef LIBXML_DOCB_ENABLED
1589 case XML_DOCB_DOCUMENT_NODE:
1590 #endif
1591 break;
1592 case XML_TEXT_NODE: {
1593 int noenc = (node->name == xmlStringTextNoenc);
1594 return(xsltCopyTextString(ctxt, insert, node->content, noenc));
1595 }
1596 case XML_CDATA_SECTION_NODE:
1597 return(xsltCopyTextString(ctxt, insert, node->content, 0));
1598 case XML_ATTRIBUTE_NODE:
1599 return((xmlNodePtr)
1600 xsltShallowCopyAttr(ctxt, invocNode, insert, (xmlAttrPtr) node));
1601 case XML_NAMESPACE_DECL:
1602 return((xmlNodePtr) xsltShallowCopyNsNode(ctxt, invocNode,
1603 insert, (xmlNsPtr) node));
1604
1605 case XML_DOCUMENT_TYPE_NODE:
1606 case XML_DOCUMENT_FRAG_NODE:
1607 case XML_NOTATION_NODE:
1608 case XML_DTD_NODE:
1609 case XML_ELEMENT_DECL:
1610 case XML_ATTRIBUTE_DECL:
1611 case XML_ENTITY_DECL:
1612 case XML_XINCLUDE_START:
1613 case XML_XINCLUDE_END:
1614 return(NULL);
1615 }
1616 if (XSLT_IS_RES_TREE_FRAG(node)) {
1617 if (node->children != NULL)
1618 copy = xsltCopyTreeList(ctxt, invocNode,
1619 node->children, insert, 0, 0);
1620 else
1621 copy = NULL;
1622 return(copy);
1623 }
1624 copy = xmlDocCopyNode(node, insert->doc, 0);
1625 if (copy != NULL) {
1626 copy->doc = ctxt->output;
1627 copy = xsltAddChild(insert, copy);
1628 if (copy == NULL) {
1629 xsltTransformError(ctxt, NULL, invocNode,
1630 "xsltCopyTreeInternal: Copying of '%s' failed.\n", node->name);
1631 return (copy);
1632 }
1633 /*
1634 * The node may have been coalesced into another text node.
1635 */
1636 if (insert->last != copy)
1637 return(insert->last);
1638 copy->next = NULL;
1639
1640 if (node->type == XML_ELEMENT_NODE) {
1641 /*
1642 * Copy in-scope namespace nodes.
1643 *
1644 * REVISIT: Since we try to reuse existing in-scope ns-decls by
1645 * using xmlSearchNsByHref(), this will eventually change
1646 * the prefix of an original ns-binding; thus it might
1647 * break QNames in element/attribute content.
1648 * OPTIMIZE TODO: If we had a xmlNsPtr * on the transformation
1649 * context, plus a ns-lookup function, which writes directly
1650 * to a given list, then we wouldn't need to create/free the
1651 * nsList every time.
1652 */
1653 if ((topElemVisited == 0) &&
1654 (node->parent != NULL) &&
1655 (node->parent->type != XML_DOCUMENT_NODE) &&
1656 (node->parent->type != XML_HTML_DOCUMENT_NODE))
1657 {
1658 xmlNsPtr *nsList, *curns, ns;
1659
1660 /*
1661 * If this is a top-most element in a tree to be
1662 * copied, then we need to ensure that all in-scope
1663 * namespaces are copied over. For nodes deeper in the
1664 * tree, it is sufficient to reconcile only the ns-decls
1665 * (node->nsDef entries).
1666 */
1667
1668 nsList = xmlGetNsList(node->doc, node);
1669 if (nsList != NULL) {
1670 curns = nsList;
1671 do {
1672 /*
1673 * Search by prefix first in order to break as less
1674 * QNames in element/attribute content as possible.
1675 */
1676 ns = xmlSearchNs(insert->doc, insert,
1677 (*curns)->prefix);
1678
1679 if ((ns == NULL) ||
1680 (! xmlStrEqual(ns->href, (*curns)->href)))
1681 {
1682 ns = NULL;
1683 /*
1684 * Search by namespace name.
1685 * REVISIT TODO: Currently disabled.
1686 */
1687 #if 0
1688 ns = xmlSearchNsByHref(insert->doc,
1689 insert, (*curns)->href);
1690 #endif
1691 }
1692 if (ns == NULL) {
1693 /*
1694 * Declare a new namespace on the copied element.
1695 */
1696 ns = xmlNewNs(copy, (*curns)->href,
1697 (*curns)->prefix);
1698 /* TODO: Handle errors */
1699 }
1700 if (node->ns == *curns) {
1701 /*
1702 * If this was the original's namespace then set
1703 * the generated counterpart on the copy.
1704 */
1705 copy->ns = ns;
1706 }
1707 curns++;
1708 } while (*curns != NULL);
1709 xmlFree(nsList);
1710 }
1711 } else if (node->nsDef != NULL) {
1712 /*
1713 * Copy over all namespace declaration attributes.
1714 */
1715 if (node->nsDef != NULL) {
1716 if (isLRE)
1717 xsltCopyNamespaceList(ctxt, copy, node->nsDef);
1718 else
1719 xsltCopyNamespaceListInternal(copy, node->nsDef);
1720 }
1721 }
1722 /*
1723 * Set the namespace.
1724 */
1725 if (node->ns != NULL) {
1726 if (copy->ns == NULL) {
1727 /*
1728 * This will map copy->ns to one of the newly created
1729 * in-scope ns-decls, OR create a new ns-decl on @copy.
1730 */
1731 copy->ns = xsltGetSpecialNamespace(ctxt, invocNode,
1732 node->ns->href, node->ns->prefix, copy);
1733 }
1734 } else if ((insert->type == XML_ELEMENT_NODE) &&
1735 (insert->ns != NULL))
1736 {
1737 /*
1738 * "Undeclare" the default namespace on @copy with xmlns="".
1739 */
1740 xsltGetSpecialNamespace(ctxt, invocNode, NULL, NULL, copy);
1741 }
1742 /*
1743 * Copy attribute nodes.
1744 */
1745 if (node->properties != NULL) {
1746 xsltCopyAttrListNoOverwrite(ctxt, invocNode,
1747 copy, node->properties);
1748 }
1749 if (topElemVisited == 0)
1750 topElemVisited = 1;
1751 }
1752 /*
1753 * Copy the subtree.
1754 */
1755 if (node->children != NULL) {
1756 xsltCopyTreeList(ctxt, invocNode,
1757 node->children, copy, isLRE, topElemVisited);
1758 }
1759 } else {
1760 xsltTransformError(ctxt, NULL, invocNode,
1761 "xsltCopyTreeInternal: Copying of '%s' failed.\n", node->name);
1762 }
1763 return(copy);
1764 }
1765
1766 /**
1767 * xsltCopyTree:
1768 * @ctxt: the XSLT transformation context
1769 * @node: the element node in the source tree
1770 * @insert: the parent in the result tree
1771 * @literal: indicates if @node is a Literal Result Element
1772 *
1773 * Make a copy of the full tree under the element node @node
1774 * and insert it as last child of @insert
1775 * For literal result element, some of the namespaces may not be copied
1776 * over according to section 7.1.
1777 * TODO: Why is this a public function?
1778 *
1779 * Returns a pointer to the new tree, or NULL in case of error
1780 */
1781 xmlNodePtr
1782 xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
1783 xmlNodePtr insert, int literal)
1784 {
1785 return(xsltCopyTreeInternal(ctxt, node, node, insert, literal, 0));
1786
1787 }
1788
1789 /************************************************************************
1790 * *
1791 * Error/fallback processing *
1792 * *
1793 ************************************************************************/
1794
1795 /**
1796 * xsltApplyFallbacks:
1797 * @ctxt: a XSLT process context
1798 * @node: the node in the source tree.
1799 * @inst: the node generating the error
1800 *
1801 * Process possible xsl:fallback nodes present under @inst
1802 *
1803 * Returns the number of xsl:fallback element found and processed
1804 */
1805 static int
1806 xsltApplyFallbacks(xsltTransformContextPtr ctxt, xmlNodePtr node,
1807 xmlNodePtr inst) {
1808
1809 xmlNodePtr child;
1810 int ret = 0;
1811
1812 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) ||
1813 (inst->children == NULL))
1814 return(0);
1815
1816 child = inst->children;
1817 while (child != NULL) {
1818 if ((IS_XSLT_ELEM(child)) &&
1819 (xmlStrEqual(child->name, BAD_CAST "fallback"))) {
1820 #ifdef WITH_XSLT_DEBUG_PARSING
1821 xsltGenericDebug(xsltGenericDebugContext,
1822 "applying xsl:fallback\n");
1823 #endif
1824 ret++;
1825 xsltApplySequenceConstructor(ctxt, node, child->children,
1826 NULL);
1827 }
1828 child = child->next;
1829 }
1830 return(ret);
1831 }
1832
1833 /************************************************************************
1834 * *
1835 * Default processing *
1836 * *
1837 ************************************************************************/
1838
1839 /**
1840 * xsltDefaultProcessOneNode:
1841 * @ctxt: a XSLT process context
1842 * @node: the node in the source tree.
1843 * @params: extra parameters passed to the template if any
1844 *
1845 * Process the source node with the default built-in template rule:
1846 * <xsl:template match="*|/">
1847 * <xsl:apply-templates/>
1848 * </xsl:template>
1849 *
1850 * and
1851 *
1852 * <xsl:template match="text()|@*">
1853 * <xsl:value-of select="."/>
1854 * </xsl:template>
1855 *
1856 * Note also that namespace declarations are copied directly:
1857 *
1858 * the built-in template rule is the only template rule that is applied
1859 * for namespace nodes.
1860 */
1861 static void
1862 xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
1863 xsltStackElemPtr params) {
1864 xmlNodePtr copy;
1865 xmlNodePtr delete = NULL, cur;
1866 int nbchild = 0, oldSize;
1867 int childno = 0, oldPos;
1868 xsltTemplatePtr template;
1869
1870 CHECK_STOPPED;
1871 /*
1872 * Handling of leaves
1873 */
1874 switch (node->type) {
1875 case XML_DOCUMENT_NODE:
1876 case XML_HTML_DOCUMENT_NODE:
1877 case XML_ELEMENT_NODE:
1878 break;
1879 case XML_CDATA_SECTION_NODE:
1880 #ifdef WITH_XSLT_DEBUG_PROCESS
1881 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1882 "xsltDefaultProcessOneNode: copy CDATA %s\n",
1883 node->content));
1884 #endif
1885 copy = xsltCopyText(ctxt, ctxt->insert, node, 0);
1886 if (copy == NULL) {
1887 xsltTransformError(ctxt, NULL, node,
1888 "xsltDefaultProcessOneNode: cdata copy failed\n");
1889 }
1890 return;
1891 case XML_TEXT_NODE:
1892 #ifdef WITH_XSLT_DEBUG_PROCESS
1893 if (node->content == NULL) {
1894 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1895 "xsltDefaultProcessOneNode: copy empty text\n"));
1896 return;
1897 } else {
1898 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1899 "xsltDefaultProcessOneNode: copy text %s\n",
1900 node->content));
1901 }
1902 #endif
1903 copy = xsltCopyText(ctxt, ctxt->insert, node, 0);
1904 if (copy == NULL) {
1905 xsltTransformError(ctxt, NULL, node,
1906 "xsltDefaultProcessOneNode: text copy failed\n");
1907 }
1908 return;
1909 case XML_ATTRIBUTE_NODE:
1910 cur = node->children;
1911 while ((cur != NULL) && (cur->type != XML_TEXT_NODE))
1912 cur = cur->next;
1913 if (cur == NULL) {
1914 xsltTransformError(ctxt, NULL, node,
1915 "xsltDefaultProcessOneNode: no text for attribute\n");
1916 } else {
1917 #ifdef WITH_XSLT_DEBUG_PROCESS
1918 if (cur->content == NULL) {
1919 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1920 "xsltDefaultProcessOneNode: copy empty text\n"));
1921 } else {
1922 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1923 "xsltDefaultProcessOneNode: copy text %s\n",
1924 cur->content));
1925 }
1926 #endif
1927 copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
1928 if (copy == NULL) {
1929 xsltTransformError(ctxt, NULL, node,
1930 "xsltDefaultProcessOneNode: text copy failed\n");
1931 }
1932 }
1933 return;
1934 default:
1935 return;
1936 }
1937 /*
1938 * Handling of Elements: first pass, cleanup and counting
1939 */
1940 cur = node->children;
1941 while (cur != NULL) {
1942 switch (cur->type) {
1943 case XML_TEXT_NODE:
1944 case XML_CDATA_SECTION_NODE:
1945 case XML_DOCUMENT_NODE:
1946 case XML_HTML_DOCUMENT_NODE:
1947 case XML_ELEMENT_NODE:
1948 case XML_PI_NODE:
1949 case XML_COMMENT_NODE:
1950 nbchild++;
1951 break;
1952 case XML_DTD_NODE:
1953 /* Unlink the DTD, it's still reachable using doc->intSubset */
1954 if (cur->next != NULL)
1955 cur->next->prev = cur->prev;
1956 if (cur->prev != NULL)
1957 cur->prev->next = cur->next;
1958 break;
1959 default:
1960 #ifdef WITH_XSLT_DEBUG_PROCESS
1961 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1962 "xsltDefaultProcessOneNode: skipping node type %d\n",
1963 cur->type));
1964 #endif
1965 delete = cur;
1966 }
1967 cur = cur->next;
1968 if (delete != NULL) {
1969 #ifdef WITH_XSLT_DEBUG_PROCESS
1970 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1971 "xsltDefaultProcessOneNode: removing ignorable blank node\n"));
1972 #endif
1973 xmlUnlinkNode(delete);
1974 xmlFreeNode(delete);
1975 delete = NULL;
1976 }
1977 }
1978 if (delete != NULL) {
1979 #ifdef WITH_XSLT_DEBUG_PROCESS
1980 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
1981 "xsltDefaultProcessOneNode: removing ignorable blank node\n"));
1982 #endif
1983 xmlUnlinkNode(delete);
1984 xmlFreeNode(delete);
1985 delete = NULL;
1986 }
1987
1988 /*
1989 * Handling of Elements: second pass, actual processing
1990 */
1991 oldSize = ctxt->xpathCtxt->contextSize;
1992 oldPos = ctxt->xpathCtxt->proximityPosition;
1993 cur = node->children;
1994 while (cur != NULL) {
1995 childno++;
1996 switch (cur->type) {
1997 case XML_DOCUMENT_NODE:
1998 case XML_HTML_DOCUMENT_NODE:
1999 case XML_ELEMENT_NODE:
2000 ctxt->xpathCtxt->contextSize = nbchild;
2001 ctxt->xpathCtxt->proximityPosition = childno;
2002 xsltProcessOneNode(ctxt, cur, params);
2003 break;
2004 case XML_CDATA_SECTION_NODE:
2005 template = xsltGetTemplate(ctxt, cur, NULL);
2006 if (template) {
2007 #ifdef WITH_XSLT_DEBUG_PROCESS
2008 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2009 "xsltDefaultProcessOneNode: applying template for CDATA %s\n",
2010 cur->content));
2011 #endif
2012 /*
2013 * Instantiate the xsl:template.
2014 */
2015 xsltApplyXSLTTemplate(ctxt, cur, template->content,
2016 template, params);
2017 } else /* if (ctxt->mode == NULL) */ {
2018 #ifdef WITH_XSLT_DEBUG_PROCESS
2019 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2020 "xsltDefaultProcessOneNode: copy CDATA %s\n",
2021 cur->content));
2022 #endif
2023 copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
2024 if (copy == NULL) {
2025 xsltTransformError(ctxt, NULL, cur,
2026 "xsltDefaultProcessOneNode: cdata copy failed\n");
2027 }
2028 }
2029 break;
2030 case XML_TEXT_NODE:
2031 template = xsltGetTemplate(ctxt, cur, NULL);
2032 if (template) {
2033 #ifdef WITH_XSLT_DEBUG_PROCESS
2034 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2035 "xsltDefaultProcessOneNode: applying template for text %s\n",
2036 cur->content));
2037 #endif
2038 ctxt->xpathCtxt->contextSize = nbchild;
2039 ctxt->xpathCtxt->proximityPosition = childno;
2040 /*
2041 * Instantiate the xsl:template.
2042 */
2043 xsltApplyXSLTTemplate(ctxt, cur, template->content,
2044 template, params);
2045 } else /* if (ctxt->mode == NULL) */ {
2046 #ifdef WITH_XSLT_DEBUG_PROCESS
2047 if (cur->content == NULL) {
2048 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2049 "xsltDefaultProcessOneNode: copy empty text\n"));
2050 } else {
2051 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2052 "xsltDefaultProcessOneNode: copy text %s\n",
2053 cur->content));
2054 }
2055 #endif
2056 copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
2057 if (copy == NULL) {
2058 xsltTransformError(ctxt, NULL, cur,
2059 "xsltDefaultProcessOneNode: text copy failed\n");
2060 }
2061 }
2062 break;
2063 case XML_PI_NODE:
2064 case XML_COMMENT_NODE:
2065 template = xsltGetTemplate(ctxt, cur, NULL);
2066 if (template) {
2067 #ifdef WITH_XSLT_DEBUG_PROCESS
2068 if (cur->type == XML_PI_NODE) {
2069 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2070 "xsltDefaultProcessOneNode: template found for PI %s\n",
2071 cur->name));
2072 } else if (cur->type == XML_COMMENT_NODE) {
2073 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2074 "xsltDefaultProcessOneNode: template found for comment\n"));
2075 }
2076 #endif
2077 ctxt->xpathCtxt->contextSize = nbchild;
2078 ctxt->xpathCtxt->proximityPosition = childno;
2079 /*
2080 * Instantiate the xsl:template.
2081 */
2082 xsltApplyXSLTTemplate(ctxt, cur, template->content,
2083 template, params);
2084 }
2085 break;
2086 default:
2087 break;
2088 }
2089 cur = cur->next;
2090 }
2091 ctxt->xpathCtxt->contextSize = oldSize;
2092 ctxt->xpathCtxt->proximityPosition = oldPos;
2093 }
2094
2095 /**
2096 * xsltProcessOneNode:
2097 * @ctxt: a XSLT process context
2098 * @contextNode: the "current node" in the source tree
2099 * @withParams: extra parameters (e.g. xsl:with-param) passed to the
2100 * template if any
2101 *
2102 * Process the source node.
2103 */
2104 void
2105 xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
2106 xsltStackElemPtr withParams)
2107 {
2108 xsltTemplatePtr templ;
2109 xmlNodePtr oldNode;
2110
2111 templ = xsltGetTemplate(ctxt, contextNode, NULL);
2112 /*
2113 * If no template is found, apply the default rule.
2114 */
2115 if (templ == NULL) {
2116 #ifdef WITH_XSLT_DEBUG_PROCESS
2117 if (contextNode->type == XML_DOCUMENT_NODE) {
2118 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2119 "xsltProcessOneNode: no template found for /\n"));
2120 } else if (contextNode->type == XML_CDATA_SECTION_NODE) {
2121 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2122 "xsltProcessOneNode: no template found for CDATA\n"));
2123 } else if (contextNode->type == XML_ATTRIBUTE_NODE) {
2124 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2125 "xsltProcessOneNode: no template found for attribute %s\n",
2126 ((xmlAttrPtr) contextNode)->name));
2127 } else {
2128 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2129 "xsltProcessOneNode: no template found for %s\n", contextNode->name));
2130 }
2131 #endif
2132 oldNode = ctxt->node;
2133 ctxt->node = contextNode;
2134 xsltDefaultProcessOneNode(ctxt, contextNode, withParams);
2135 ctxt->node = oldNode;
2136 return;
2137 }
2138
2139 if (contextNode->type == XML_ATTRIBUTE_NODE) {
2140 xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule;
2141 /*
2142 * Set the "current template rule".
2143 */
2144 ctxt->currentTemplateRule = templ;
2145
2146 #ifdef WITH_XSLT_DEBUG_PROCESS
2147 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2148 "xsltProcessOneNode: applying template '%s' for attribute %s\n",
2149 templ->match, contextNode->name));
2150 #endif
2151 xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams);
2152
2153 ctxt->currentTemplateRule = oldCurTempRule;
2154 } else {
2155 xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule;
2156 /*
2157 * Set the "current template rule".
2158 */
2159 ctxt->currentTemplateRule = templ;
2160
2161 #ifdef WITH_XSLT_DEBUG_PROCESS
2162 if (contextNode->type == XML_DOCUMENT_NODE) {
2163 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2164 "xsltProcessOneNode: applying template '%s' for /\n",
2165 templ->match));
2166 } else {
2167 XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
2168 "xsltProcessOneNode: applying template '%s' for %s\n",
2169 templ->match, contextNode->name));
2170 }
2171 #endif
2172 xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams);
2173
2174 ctxt->currentTemplateRule = oldCurTempRule;
2175 }
2176 }
2177
2178 static xmlNodePtr
2179 xsltDebuggerStartSequenceConstructor(xsltTransformContextPtr ctxt,
2180 xmlNodePtr contextNode,
2181 xmlNodePtr list,
2182 xsltTemplatePtr templ,
2183 int *addCallResult)
2184 {
2185 xmlNodePtr debugedNode = NULL;
2186
2187 if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
2188 if (templ) {
2189 *addCallResult = xslAddCall(templ, templ->elem);
2190 } else {
2191 *addCallResult = xslAddCall(NULL, list);
2192 }
2193 switch (ctxt->debugStatus) {
2194 case XSLT_DEBUG_RUN_RESTART:
2195 case XSLT_DEBUG_QUIT:
2196 if (*addCallResult)
2197 xslDropCall();
2198 return(NULL);
2199 }
2200 if (templ) {
2201 xslHandleDebugger(templ->elem, contextNode, templ, ctxt);
2202 debugedNode = templ->elem;
2203 } else if (list) {
2204 xslHandleDebugger(list, contextNode, templ, ctxt);
2205 debugedNode = list;
2206 } else if (ctxt->inst) {
2207 xslHandleDebugger(ctxt->inst, contextNode, templ, ctxt);
2208 debugedNode = ctxt->inst;
2209 }
2210 }
2211 return(debugedNode);
2212 }
2213
2214 /**
2215 * xsltLocalVariablePush:
2216 * @ctxt: the transformation context
2217 * @variable: variable to be pushed to the variable stack
2218 * @level: new value for variable's level
2219 *
2220 * Places the variable onto the local variable stack
2221 *
2222 * Returns: 0 for success, -1 for any error
2223 * **NOTE:**
2224 * This is an internal routine and should not be called by users!
2225 */
2226 int
2227 xsltLocalVariablePush(xsltTransformContextPtr ctxt,
2228 xsltStackElemPtr variable,
2229 int level)
2230 {
2231 if (ctxt->varsMax == 0) {
2232 ctxt->varsMax = 10;
2233 ctxt->varsTab =
2234 (xsltStackElemPtr *) xmlMalloc(ctxt->varsMax *
2235 sizeof(ctxt->varsTab[0]));
2236 if (ctxt->varsTab == NULL) {
2237 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
2238 return (-1);
2239 }
2240 }
2241 if (ctxt->varsNr >= ctxt->varsMax) {
2242 ctxt->varsMax *= 2;
2243 ctxt->varsTab =
2244 (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab,
2245 ctxt->varsMax *
2246 sizeof(ctxt->varsTab[0]));
2247 if (ctxt->varsTab == NULL) {
2248 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
2249 return (-1);
2250 }
2251 }
2252 ctxt->varsTab[ctxt->varsNr++] = variable;
2253 ctxt->vars = variable;
2254 variable->level = level;
2255 return(0);
2256 }
2257
2258 /**
2259 * xsltReleaseLocalRVTs:
2260 *
2261 * Fragments which are results of extension instructions
2262 * are preserved; all other fragments are freed/cached.
2263 */
2264 static void
2265 xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt, xmlDocPtr base)
2266 {
2267 xmlDocPtr cur = ctxt->localRVT, tmp;
2268
2269 while ((cur != NULL) && (cur != base)) {
2270 if (cur->psvi == (void *) ((long) 1)) {
2271 cur = (xmlDocPtr) cur->next;
2272 } else {
2273 tmp = cur;
2274 cur = (xmlDocPtr) cur->next;
2275
2276 if (tmp == ctxt->localRVT)
2277 ctxt->localRVT = cur;
2278
2279 /*
2280 * We need ctxt->localRVTBase for extension instructions
2281 * which return values (like EXSLT's function).
2282 */
2283 if (tmp == ctxt->localRVTBase)
2284 ctxt->localRVTBase = cur;
2285
2286 if (tmp->prev)
2287 tmp->prev->next = (xmlNodePtr) cur;
2288 if (cur)
2289 cur->prev = tmp->prev;
2290 xsltReleaseRVT(ctxt, tmp);
2291 }
2292 }
2293 }
2294
2295 /**
2296 * xsltApplySequenceConstructor:
2297 * @ctxt: a XSLT process context
2298 * @contextNode: the "current node" in the source tree
2299 * @list: the nodes of a sequence constructor;
2300 * (plus leading xsl:param elements)
2301 * @templ: the compiled xsl:template (optional)
2302 *
2303 * Processes a sequence constructor.
2304 *
2305 * NOTE: ctxt->currentTemplateRule was introduced to reflect the
2306 * semantics of "current template rule". I.e. the field ctxt->templ
2307 * is not intended to reflect this, thus always pushed onto the
2308 * template stack.
2309 */
2310 static void
2311 xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
2312 xmlNodePtr contextNode, xmlNodePtr list,
2313 xsltTemplatePtr templ)
2314 {
2315 xmlNodePtr oldInsert, oldInst, oldCurInst, oldContextNode;
2316 xmlNodePtr cur, insert, copy = NULL;
2317 int level = 0, oldVarsNr;
2318 xmlDocPtr oldLocalFragmentTop, oldLocalFragmentBase;
2319
2320 #ifdef XSLT_REFACTORED
2321 xsltStylePreCompPtr info;
2322 #endif
2323
2324 #ifdef WITH_DEBUGGER
2325 int addCallResult = 0;
2326 xmlNodePtr debuggedNode = NULL;
2327 #endif
2328
2329 if (ctxt == NULL)
2330 return;
2331
2332 #ifdef WITH_DEBUGGER
2333 if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
2334 debuggedNode =
2335 xsltDebuggerStartSequenceConstructor(ctxt, contextNode,
2336 list, templ, &addCallResult);
2337 if (debuggedNode == NULL)
2338 return;
2339 }
2340 #endif
2341
2342 if (list == NULL)
2343 return;
2344 CHECK_STOPPED;
2345
2346 oldLocalFragmentTop = ctxt->localRVT;
2347 oldInsert = insert = ctxt->insert;
2348 oldInst = oldCurInst = ctxt->inst;
2349 oldContextNode = ctxt->node;
2350 /*
2351 * Save current number of variables on the stack; new vars are popped when
2352 * exiting.
2353 */
2354 oldVarsNr = ctxt->varsNr;
2355 /*
2356 * Process the sequence constructor.
2357 */
2358 cur = list;
2359 while (cur != NULL) {
2360 ctxt->inst = cur;
2361
2362 #ifdef WITH_DEBUGGER
2363 switch (ctxt->debugStatus) {
2364 case XSLT_DEBUG_RUN_RESTART:
2365 case XSLT_DEBUG_QUIT:
2366 break;
2367
2368 }
2369 #endif
2370 /*
2371 * Test; we must have a valid insertion point.
2372 */
2373 if (insert == NULL) {
2374
2375 #ifdef WITH_XSLT_DEBUG_PROCESS
2376 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2377 "xsltApplySequenceConstructor: insert == NULL !\n"));
2378 #endif
2379 goto error;
2380 }
2381
2382 #ifdef WITH_DEBUGGER
2383 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (debuggedNode != cur))
2384 xslHandleDebugger(cur, contextNode, templ, ctxt);
2385 #endif
2386
2387 #ifdef XSLT_REFACTORED
2388 if (cur->type == XML_ELEMENT_NODE) {
2389 info = (xsltStylePreCompPtr) cur->psvi;
2390 /*
2391 * We expect a compiled representation on:
2392 * 1) XSLT instructions of this XSLT version (1.0)
2393 * (with a few exceptions)
2394 * 2) Literal result elements
2395 * 3) Extension instructions
2396 * 4) XSLT instructions of future XSLT versions
2397 * (forwards-compatible mode).
2398 */
2399 if (info == NULL) {
2400 /*
2401 * Handle the rare cases where we don't expect a compiled
2402 * representation on an XSLT element.
2403 */
2404 if (IS_XSLT_ELEM_FAST(cur) && IS_XSLT_NAME(cur, "message")) {
2405 xsltMessage(ctxt, contextNode, cur);
2406 goto skip_children;
2407 }
2408 /*
2409 * Something really went wrong:
2410 */
2411 xsltTransformError(ctxt, NULL, cur,
2412 "Internal error in xsltApplySequenceConstructor(): "
2413 "The element '%s' in the stylesheet has no compiled "
2414 "representation.\n",
2415 cur->name);
2416 goto skip_children;
2417 }
2418
2419 if (info->type == XSLT_FUNC_LITERAL_RESULT_ELEMENT) {
2420 xsltStyleItemLRElementInfoPtr lrInfo =
2421 (xsltStyleItemLRElementInfoPtr) info;
2422 /*
2423 * Literal result elements
2424 * --------------------------------------------------------
2425 */
2426 #ifdef WITH_XSLT_DEBUG_PROCESS
2427 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
2428 xsltGenericDebug(xsltGenericDebugContext,
2429 "xsltApplySequenceConstructor: copy literal result "
2430 "element '%s'\n", cur->name));
2431 #endif
2432 /*
2433 * Copy the raw element-node.
2434 * OLD: if ((copy = xsltShallowCopyElem(ctxt, cur, insert))
2435 * == NULL)
2436 * goto error;
2437 */
2438 copy = xmlDocCopyNode(cur, insert->doc, 0);
2439 if (copy == NULL) {
2440 xsltTransformError(ctxt, NULL, cur,
2441 "Internal error in xsltApplySequenceConstructor(): "
2442 "Failed to copy literal result element '%s'.\n",
2443 cur->name);
2444 goto error;
2445 } else {
2446 /*
2447 * Add the element-node to the result tree.
2448 */
2449 copy->doc = ctxt->output;
2450 copy = xsltAddChild(insert, copy);
2451 /*
2452 * Create effective namespaces declarations.
2453 * OLD: xsltCopyNamespaceList(ctxt, copy, cur->nsDef);
2454 */
2455 if (lrInfo->effectiveNs != NULL) {
2456 xsltEffectiveNsPtr effNs = lrInfo->effectiveNs;
2457 xmlNsPtr ns, lastns = NULL;
2458
2459 while (effNs != NULL) {
2460 /*
2461 * Avoid generating redundant namespace
2462 * declarations; thus lookup if there is already
2463 * such a ns-decl in the result.
2464 */
2465 ns = xmlSearchNs(copy->doc, copy, effNs->prefix);
2466 if ((ns != NULL) &&
2467 (xmlStrEqual(ns->href, effNs->nsName)))
2468 {
2469 effNs = effNs->next;
2470 continue;
2471 }
2472 ns = xmlNewNs(copy, effNs->nsName, effNs->prefix);
2473 if (ns == NULL) {
2474 xsltTransformError(ctxt, NULL, cur,
2475 "Internal error in "
2476 "xsltApplySequenceConstructor(): "
2477 "Failed to copy a namespace "
2478 "declaration.\n");
2479 goto error;
2480 }
2481
2482 if (lastns == NULL)
2483 copy->nsDef = ns;
2484 else
2485 lastns->next =ns;
2486 lastns = ns;
2487
2488 effNs = effNs->next;
2489 }
2490
2491 }
2492 /*
2493 * NOTE that we don't need to apply ns-alising: this was
2494 * already done at compile-time.
2495 */
2496 if (cur->ns != NULL) {
2497 /*
2498 * If there's no such ns-decl in the result tree,
2499 * then xsltGetSpecialNamespace() will
2500 * create a ns-decl on the copied node.
2501 */
2502 copy->ns = xsltGetSpecialNamespace(ctxt, cur,
2503 cur->ns->href, cur->ns->prefix, copy);
2504 } else {
2505 /*
2506 * Undeclare the default namespace if needed.
2507 * This can be skipped, if the result element has
2508 * no ns-decls, in which case the result element
2509 * obviously does not declare a default namespace;
2510 * AND there's either no parent, or the parent
2511 * element is in no namespace; this means there's no
2512 * default namespace is scope to care about.
2513 *
2514 * REVISIT: This might result in massive
2515 * generation of ns-decls if nodes in a default
2516 * namespaces are mixed with nodes in no namespace.
2517 *
2518 */
2519 if (copy->nsDef ||
2520 ((insert != NULL) &&
2521 (insert->type == XML_ELEMENT_NODE) &&
2522 (insert->ns != NULL)))
2523 {
2524 xsltGetSpecialNamespace(ctxt, cur,
2525 NULL, NULL, copy);
2526 }
2527 }
2528 }
2529 /*
2530 * SPEC XSLT 2.0 "Each attribute of the literal result
2531 * element, other than an attribute in the XSLT namespace,
2532 * is processed to produce an attribute for the element in
2533 * the result tree."
2534 * NOTE: See bug #341325.
2535 */
2536 if (cur->properties != NULL) {
2537 xsltAttrListTemplateProcess(ctxt, copy, cur->properties);
2538 }
2539 } else if (IS_XSLT_ELEM_FAST(cur)) {
2540 /*
2541 * XSLT instructions
2542 * --------------------------------------------------------
2543 */
2544 if (info->type == XSLT_FUNC_UNKOWN_FORWARDS_COMPAT) {
2545 /*
2546 * We hit an unknown XSLT element.
2547 * Try to apply one of the fallback cases.
2548 */
2549 ctxt->insert = insert;
2550 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
2551 xsltTransformError(ctxt, NULL, cur,
2552 "The is no fallback behaviour defined for "
2553 "the unknown XSLT element '%s'.\n",
2554 cur->name);
2555 }
2556 ctxt->insert = oldInsert;
2557 } else if (info->func != NULL) {
2558 /*
2559 * Execute the XSLT instruction.
2560 */
2561 ctxt->insert = insert;
2562
2563 info->func(ctxt, contextNode, cur,
2564 (xsltElemPreCompPtr) info);
2565
2566 /*
2567 * Cleanup temporary tree fragments.
2568 */
2569 if (oldLocalFragmentTop != ctxt->localRVT)
2570 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
2571
2572 ctxt->insert = oldInsert;
2573 } else if (info->type == XSLT_FUNC_VARIABLE) {
2574 xsltStackElemPtr tmpvar = ctxt->vars;
2575
2576 xsltParseStylesheetVariable(ctxt, cur);
2577
2578 if (tmpvar != ctxt->vars) {
2579 /*
2580 * TODO: Using a @tmpvar is an annoying workaround, but
2581 * the current mechanisms do not provide any other way
2582 * of knowing if the var was really pushed onto the
2583 * stack.
2584 */
2585 ctxt->vars->level = level;
2586 }
2587 } else if (info->type == XSLT_FUNC_MESSAGE) {
2588 /*
2589 * TODO: Won't be hit, since we don't compile xsl:message.
2590 */
2591 xsltMessage(ctxt, contextNode, cur);
2592 } else {
2593 xsltTransformError(ctxt, NULL, cur,
2594 "Unexpected XSLT element '%s'.\n", cur->name);
2595 }
2596 goto skip_children;
2597
2598 } else {
2599 xsltTransformFunction func;
2600 /*
2601 * Extension intructions (elements)
2602 * --------------------------------------------------------
2603 */
2604 if (cur->psvi == xsltExtMarker) {
2605 /*
2606 * The xsltExtMarker was set during the compilation
2607 * of extension instructions if there was no registered
2608 * handler for this specific extension function at
2609 * compile-time.
2610 * Libxslt will now lookup if a handler is
2611 * registered in the context of this transformation.
2612 */
2613 func = (xsltTransformFunction)
2614 xsltExtElementLookup(ctxt, cur->name, cur->ns->href);
2615 } else
2616 func = ((xsltElemPreCompPtr) cur->psvi)->func;
2617
2618 if (func == NULL) {
2619 /*
2620 * No handler available.
2621 * Try to execute fallback behaviour via xsl:fallback.
2622 */
2623 #ifdef WITH_XSLT_DEBUG_PROCESS
2624 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
2625 xsltGenericDebug(xsltGenericDebugContext,
2626 "xsltApplySequenceConstructor: unknown extension %s\n",
2627 cur->name));
2628 #endif
2629 ctxt->insert = insert;
2630 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
2631 xsltTransformError(ctxt, NULL, cur,
2632 "Unknown extension instruction '{%s}%s'.\n",
2633 cur->ns->href, cur->name);
2634 }
2635 ctxt->insert = oldInsert;
2636 } else {
2637 /*
2638 * Execute the handler-callback.
2639 */
2640 #ifdef WITH_XSLT_DEBUG_PROCESS
2641 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2642 "xsltApplySequenceConstructor: extension construct %s\n",
2643 cur->name));
2644 #endif
2645 ctxt->insert = insert;
2646 /*
2647 * We need the fragment base for extension instructions
2648 * which return values (like EXSLT's function).
2649 */
2650 oldLocalFragmentBase = ctxt->localRVTBase;
2651 ctxt->localRVTBase = NULL;
2652
2653 func(ctxt, contextNode, cur, cur->psvi);
2654
2655 ctxt->localRVTBase = oldLocalFragmentBase;
2656 /*
2657 * Cleanup temporary tree fragments.
2658 */
2659 if (oldLocalFragmentTop != ctxt->localRVT)
2660 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
2661
2662 ctxt->insert = oldInsert;
2663 }
2664 goto skip_children;
2665 }
2666
2667 } else if (XSLT_IS_TEXT_NODE(cur)) {
2668 /*
2669 * Text
2670 * ------------------------------------------------------------
2671 */
2672 #ifdef WITH_XSLT_DEBUG_PROCESS
2673 if (cur->name == xmlStringTextNoenc) {
2674 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
2675 xsltGenericDebug(xsltGenericDebugContext,
2676 "xsltApplySequenceConstructor: copy unescaped text '%s'\n",
2677 cur->content));
2678 } else {
2679 XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
2680 xsltGenericDebug(xsltGenericDebugContext,
2681 "xsltApplySequenceConstructor: copy text '%s'\n",
2682 cur->content));
2683 }
2684 #endif
2685 if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL)
2686 goto error;
2687 }
2688
2689 #else /* XSLT_REFACTORED */
2690
2691 if (IS_XSLT_ELEM(cur)) {
2692 /*
2693 * This is an XSLT node
2694 */
2695 xsltStylePreCompPtr info = (xsltStylePreCompPtr) cur->psvi;
2696
2697 if (info == NULL) {
2698 if (IS_XSLT_NAME(cur, "message")) {
2699 xsltMessage(ctxt, contextNode, cur);
2700 } else {
2701 /*
2702 * That's an error try to apply one of the fallback cases
2703 */
2704 ctxt->insert = insert;
2705 if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
2706 xsltGenericError(xsltGenericErrorContext,
2707 "xsltApplySequenceConstructor: %s was not compiled\n",
2708 cur->name);
2709 }
2710 ctxt->insert = oldInsert;
2711 }
2712 goto skip_children;
2713 }
2714
2715 if (info->func != NULL) {
2716 oldCurInst = ctxt->inst;
2717 ctxt->inst = cur;
2718 ctxt->insert = insert;
2719 oldLocalFragmentBase = ctxt->localRVTBase;
2720 ctxt->localRVTBase = NULL;
2721
2722 info->func(ctxt, contextNode, cur, (xsltElemPreCompPtr) info);
2723
2724 ctxt->localRVTBase = oldLocalFragmentBase;
2725 /*
2726 * Cleanup temporary tree fragments.
2727 */
2728 if (oldLocalFragmentTop != ctxt->localRVT)
2729 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
2730
2731 ctxt->insert = oldInsert;
2732 ctxt->inst = oldCurInst;
2733 goto skip_children;
2734 }
2735
2736 if (IS_XSLT_NAME(cur, "variable")) {
2737 xsltStackElemPtr tmpvar = ctxt->vars;
2738
2739 oldCurInst = ctxt->inst;
2740 ctxt->inst = cur;
2741
2742 xsltParseStylesheetVariable(ctxt, cur);
2743
2744 ctxt->inst = oldCurInst;
2745
2746 if (tmpvar != ctxt->vars) {
2747 /*
2748 * TODO: Using a @tmpvar is an annoying workaround, but
2749 * the current mechanisms do not provide any other way
2750 * of knowing if the var was really pushed onto the
2751 * stack.
2752 */
2753 ctxt->vars->level = level;
2754 }
2755 } else if (IS_XSLT_NAME(cur, "message")) {
2756 xsltMessage(ctxt, contextNode, cur);
2757 } else {
2758 xsltTransformError(ctxt, NULL, cur,
2759 "Unexpected XSLT element '%s'.\n", cur->name);
2760 }
2761 goto skip_children;
2762 } else if ((cur->type == XML_TEXT_NODE) ||
2763 (cur->type == XML_CDATA_SECTION_NODE)) {
2764
2765 /*
2766 * This text comes from the stylesheet
2767 * For stylesheets, the set of whitespace-preserving
2768 * element names consists of just xsl:text.
2769 */
2770 #ifdef WITH_XSLT_DEBUG_PROCESS
2771 if (cur->type == XML_CDATA_SECTION_NODE) {
2772 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2773 "xsltApplySequenceConstructor: copy CDATA text %s\n",
2774 cur->content));
2775 } else if (cur->name == xmlStringTextNoenc) {
2776 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2777 "xsltApplySequenceConstructor: copy unescaped text %s\n",
2778 cur->content));
2779 } else {
2780 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2781 "xsltApplySequenceConstructor: copy text %s\n",
2782 cur->content));
2783 }
2784 #endif
2785 if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL)
2786 goto error;
2787 } else if ((cur->type == XML_ELEMENT_NODE) &&
2788 (cur->ns != NULL) && (cur->psvi != NULL)) {
2789 xsltTransformFunction function;
2790
2791 oldCurInst = ctxt->inst;
2792 ctxt->inst = cur;
2793 /*
2794 * Flagged as an extension element
2795 */
2796 if (cur->psvi == xsltExtMarker)
2797 function = (xsltTransformFunction)
2798 xsltExtElementLookup(ctxt, cur->name, cur->ns->href);
2799 else
2800 function = ((xsltElemPreCompPtr) cur->psvi)->func;
2801
2802 if (function == NULL) {
2803 xmlNodePtr child;
2804 int found = 0;
2805
2806 #ifdef WITH_XSLT_DEBUG_PROCESS
2807 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2808 "xsltApplySequenceConstructor: unknown extension %s\n",
2809 cur->name));
2810 #endif
2811 /*
2812 * Search if there are fallbacks
2813 */
2814 child = cur->children;
2815 while (child != NULL) {
2816 if ((IS_XSLT_ELEM(child)) &&
2817 (IS_XSLT_NAME(child, "fallback")))
2818 {
2819 found = 1;
2820 xsltApplySequenceConstructor(ctxt, contextNode,
2821 child->children, NULL);
2822 }
2823 child = child->next;
2824 }
2825
2826 if (!found) {
2827 xsltTransformError(ctxt, NULL, cur,
2828 "xsltApplySequenceConstructor: failed to find extension %s\n",
2829 cur->name);
2830 }
2831 } else {
2832 #ifdef WITH_XSLT_DEBUG_PROCESS
2833 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2834 "xsltApplySequenceConstructor: extension construct %s\n",
2835 cur->name));
2836 #endif
2837
2838 ctxt->insert = insert;
2839 /*
2840 * We need the fragment base for extension instructions
2841 * which return values (like EXSLT's function).
2842 */
2843 oldLocalFragmentBase = ctxt->localRVTBase;
2844 ctxt->localRVTBase = NULL;
2845
2846 function(ctxt, contextNode, cur, cur->psvi);
2847 /*
2848 * Cleanup temporary tree fragments.
2849 */
2850 if (oldLocalFragmentTop != ctxt->localRVT)
2851 xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
2852
2853 ctxt->localRVTBase = oldLocalFragmentBase;
2854 ctxt->insert = oldInsert;
2855
2856 }
2857 ctxt->inst = oldCurInst;
2858 goto skip_children;
2859 } else if (cur->type == XML_ELEMENT_NODE) {
2860 #ifdef WITH_XSLT_DEBUG_PROCESS
2861 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
2862 "xsltApplySequenceConstructor: copy node %s\n",
2863 cur->name));
2864 #endif
2865 oldCurInst = ctxt->inst;
2866 ctxt->inst = cur;
2867
2868 if ((copy = xsltShallowCopyElem(ctxt, cur, insert, 1)) == NULL)
2869 goto error;
2870 /*
2871 * Add extra namespaces inherited from the current template
2872 * if we are in the first level children and this is a
2873 * "real" template.
2874 */
2875 if ((templ != NULL) && (oldInsert == insert) &&
2876 (ctxt->templ != NULL) && (ctxt->templ->inheritedNs != NULL)) {
2877 int i;
2878 xmlNsPtr ns, ret;
2879
2880 for (i = 0; i < ctxt->templ->inheritedNsNr; i++) {
2881 const xmlChar *URI = NULL;
2882 xsltStylesheetPtr style;
2883 ns = ctxt->templ->inheritedNs[i];
2884
2885 /* Note that the XSLT namespace was already excluded
2886 * in xsltGetInheritedNsList().
2887 */
2888 #if 0
2889 if (xmlStrEqual(ns->href, XSLT_NAMESPACE))
2890 continue;
2891 #endif
2892 style = ctxt->style;
2893 while (style != NULL) {
2894 if (style->nsAliases != NULL)
2895 URI = (const xmlChar *)
2896 xmlHashLookup(style->nsAliases, ns->href);
2897 if (URI != NULL)
2898 break;
2899
2900 style = xsltNextImport(style);
2901 }
2902 if (URI == UNDEFINED_DEFAULT_NS)
2903 continue;
2904 if (URI == NULL)
2905 URI = ns->href;
2906 /*
2907 * TODO: The following will still be buggy for the
2908 * non-refactored code.
2909 */
2910 ret = xmlSearchNs(copy->doc, copy, ns->prefix);
2911 if ((ret == NULL) || (!xmlStrEqual(ret->href, URI)))
2912 {
2913 xmlNewNs(copy, URI, ns->prefix);
2914 }
2915 }
2916 if (copy->ns != NULL) {
2917 /*
2918 * Fix the node namespace if needed
2919 */
2920 copy->ns = xsltGetNamespace(ctxt, cur, copy->ns, copy);
2921 }
2922 }
2923 /*
2924 * all the attributes are directly inherited
2925 */
2926 if (cur->properties != NULL) {
2927 xsltAttrListTemplateProcess(ctxt, copy, cur->properties);
2928 }
2929 ctxt->inst = oldCurInst;
2930 }
2931 #endif /* else of XSLT_REFACTORED */
2932
2933 /*
2934 * Descend into content in document order.
2935 */
2936 if (cur->children != NULL) {
2937 if (cur->children->type != XML_ENTITY_DECL) {
2938 cur = cur->children;
2939 level++;
2940 if (copy != NULL)
2941 insert = copy;
2942 continue;
2943 }
2944 }
2945
2946 skip_children:
2947 /*
2948 * If xslt:message was just processed, we might have hit a
2949 * terminate='yes'; if so, then break the loop and clean up.
2950 * TODO: Do we need to check this also before trying to descend
2951 * into the content?
2952 */
2953 if (ctxt->state == XSLT_STATE_STOPPED)
2954 break;
2955 if (cur->next != NULL) {
2956 cur = cur->next;
2957 continue;
2958 }
2959
2960 do {
2961 cur = cur->parent;
2962 level--;
2963 /*
2964 * Pop variables/params (xsl:variable and xsl:param).
2965 */
2966 if ((ctxt->varsNr > oldVarsNr) && (ctxt->vars->level > level)) {
2967 xsltLocalVariablePop(ctxt, oldVarsNr, level);
2968 }
2969
2970 insert = insert->parent;
2971 if (cur == NULL)
2972 break;
2973 if (cur == list->parent) {
2974 cur = NULL;
2975 break;
2976 }
2977 if (cur->next != NULL) {
2978 cur = cur->next;
2979 break;
2980 }
2981 } while (cur != NULL);
2982 }
2983
2984 error:
2985 /*
2986 * In case of errors: pop remaining variables.
2987 */
2988 if (ctxt->varsNr > oldVarsNr)
2989 xsltLocalVariablePop(ctxt, oldVarsNr, -1);
2990
2991 ctxt->node = oldContextNode;
2992 ctxt->inst = oldInst;
2993 ctxt->insert = oldInsert;
2994
2995 #ifdef WITH_DEBUGGER
2996 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) {
2997 xslDropCall();
2998 }
2999 #endif
3000 }
3001
3002 /*
3003 * xsltApplyXSLTTemplate:
3004 * @ctxt: a XSLT transformation context
3005 * @contextNode: the node in the source tree.
3006 * @list: the nodes of a sequence constructor;
3007 * (plus leading xsl:param elements)
3008 * @templ: the compiled xsl:template declaration;
3009 * NULL if a sequence constructor
3010 * @withParams: a set of caller-parameters (xsl:with-param) or NULL
3011 *
3012 * Called by:
3013 * - xsltApplyImports()
3014 * - xsltCallTemplate()
3015 * - xsltDefaultProcessOneNode()
3016 * - xsltProcessOneNode()
3017 */
3018 static void
3019 xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt,
3020 xmlNodePtr contextNode,
3021 xmlNodePtr list,
3022 xsltTemplatePtr templ,
3023 xsltStackElemPtr withParams)
3024 {
3025 int oldVarsBase = 0;
3026 long start = 0;
3027 xmlNodePtr cur;
3028 xsltStackElemPtr tmpParam = NULL;
3029 xmlDocPtr oldUserFragmentTop, oldLocalFragmentTop;
3030
3031 #ifdef XSLT_REFACTORED
3032 xsltStyleItemParamPtr iparam;
3033 #else
3034 xsltStylePreCompPtr iparam;
3035 #endif
3036
3037 #ifdef WITH_DEBUGGER
3038 int addCallResult = 0;
3039 #endif
3040
3041 if (ctxt == NULL)
3042 return;
3043 if (templ == NULL) {
3044 xsltTransformError(ctxt, NULL, list,
3045 "xsltApplyXSLTTemplate: Bad arguments; @templ is mandatory.\n");
3046 return;
3047 }
3048
3049 #ifdef WITH_DEBUGGER
3050 if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
3051 if (xsltDebuggerStartSequenceConstructor(ctxt, contextNode,
3052 list, templ, &addCallResult) == NULL)
3053 return;
3054 }
3055 #endif
3056
3057 if (list == NULL)
3058 return;
3059 CHECK_STOPPED;
3060
3061 /*
3062 * Check for infinite recursion: stop if the maximum of nested templates
3063 * is excceeded. Adjust xsltMaxDepth if you need more.
3064 */
3065 if (ctxt->templNr >= ctxt->maxTemplateDepth)
3066 {
3067 xsltTransformError(ctxt, NULL, list,
3068 "xsltApplyXSLTTemplate: A potential infinite template recursion "
3069 "was detected.\n"
3070 "You can adjust xsltMaxDepth (--maxdepth) in order to "
3071 "raise the maximum number of nested template calls and "
3072 "variables/params (currently set to %d).\n",
3073 ctxt->maxTemplateDepth);
3074 xsltDebug(ctxt, contextNode, list, NULL);
3075 return;
3076 }
3077
3078 if (ctxt->varsNr >= ctxt->maxTemplateVars)
3079 {
3080 xsltTransformError(ctxt, NULL, list,
3081 "xsltApplyXSLTTemplate: A potential infinite template recursion "
3082 "was detected.\n"
3083 "You can adjust maxTemplateVars (--maxvars) in order to "
3084 "raise the maximum number of variables/params (currently set to %d).\n",
3085 ctxt->maxTemplateVars);
3086 xsltDebug(ctxt, contextNode, list, NULL);
3087 return;
3088 }
3089
3090 oldUserFragmentTop = ctxt->tmpRVT;
3091 ctxt->tmpRVT = NULL;
3092 oldLocalFragmentTop = ctxt->localRVT;
3093
3094 /*
3095 * Initiate a distinct scope of local params/variables.
3096 */
3097 oldVarsBase = ctxt->varsBase;
3098 ctxt->varsBase = ctxt->varsNr;
3099
3100 ctxt->node = contextNode;
3101 if (ctxt->profile) {
3102 templ->nbCalls++;
3103 start = xsltTimestamp();
3104 profPush(ctxt, 0);
3105 profCallgraphAdd(templ, ctxt->templ);
3106 }
3107 /*
3108 * Push the xsl:template declaration onto the stack.
3109 */
3110 templPush(ctxt, templ);
3111
3112 #ifdef WITH_XSLT_DEBUG_PROCESS
3113 if (templ->name != NULL)
3114 XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
3115 "applying xsl:template '%s'\n", templ->name));
3116 #endif
3117 /*
3118 * Process xsl:param instructions and skip those elements for
3119 * further processing.
3120 */
3121 cur = list;
3122 do {
3123 if (cur->type == XML_TEXT_NODE) {
3124 cur = cur->next;
3125 continue;
3126 }
3127 if ((cur->type != XML_ELEMENT_NODE) ||
3128 (cur->name[0] != 'p') ||
3129 (cur->psvi == NULL) ||
3130 (! xmlStrEqual(cur->name, BAD_CAST "param")) ||
3131 (! IS_XSLT_ELEM(cur)))
3132 {
3133 break;
3134 }
3135
3136 list = cur->next;
3137
3138 #ifdef XSLT_REFACTORED
3139 iparam = (xsltStyleItemParamPtr) cur->psvi;
3140 #else
3141 iparam = (xsltStylePreCompPtr) cur->psvi;
3142 #endif
3143
3144 /*
3145 * Substitute xsl:param for a given xsl:with-param.
3146 * Since the XPath expression will reference the params/vars
3147 * by index, we need to slot the xsl:with-params in the
3148 * order of encountered xsl:params to keep the sequence of
3149 * params/variables in the stack exactly as it was at
3150 * compile time,
3151 */
3152 tmpParam = NULL;
3153 if (withParams) {
3154 tmpParam = withParams;
3155 do {
3156 if ((tmpParam->name == (iparam->name)) &&
3157 (tmpParam->nameURI == (iparam->ns)))
3158 {
3159 /*
3160 * Push the caller-parameter.
3161 */
3162 xsltLocalVariablePush(ctxt, tmpParam, -1);
3163 break;
3164 }
3165 tmpParam = tmpParam->next;
3166 } while (tmpParam != NULL);
3167 }
3168 /*
3169 * Push the xsl:param.
3170 */
3171 if (tmpParam == NULL) {
3172 /*
3173 * Note that we must assume that the added parameter
3174 * has a @depth of 0.
3175 */
3176 xsltParseStylesheetParam(ctxt, cur);
3177 }
3178 cur = cur->next;
3179 } while (cur != NULL);
3180 /*
3181 * Process the sequence constructor.
3182 */
3183 xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
3184
3185 /*
3186 * Remove remaining xsl:param and xsl:with-param items from
3187 * the stack. Don't free xsl:with-param items.
3188 */
3189 if (ctxt->varsNr > ctxt->varsBase)
3190 xsltTemplateParamsCleanup(ctxt);
3191 ctxt->varsBase = oldVarsBase;
3192
3193 /*
3194 * Clean up remaining local tree fragments.
3195 * This also frees fragments which are the result of
3196 * extension instructions. Should normally not be hit; but
3197 * just for the case xsltExtensionInstructionResultFinalize()
3198 * was not called by the extension author.
3199 */
3200 if (oldLocalFragmentTop != ctxt->localRVT) {
3201 xmlDocPtr curdoc = ctxt->localRVT, tmp;
3202
3203 do {
3204 tmp = curdoc;
3205 curdoc = (xmlDocPtr) curdoc->next;
3206 /* Need to housekeep localRVTBase */
3207 if (tmp == ctxt->localRVTBase)
3208 ctxt->localRVTBase = curdoc;
3209 if (tmp->prev)
3210 tmp->prev->next = (xmlNodePtr) curdoc;
3211 if (curdoc)
3212 curdoc->prev = tmp->prev;
3213 xsltReleaseRVT(ctxt, tmp);
3214 } while (curdoc != oldLocalFragmentTop);
3215 }
3216 ctxt->localRVT = oldLocalFragmentTop;
3217
3218 /*
3219 * Release user-created fragments stored in the scope
3220 * of xsl:template. Note that this mechanism is deprecated:
3221 * user code should now use xsltRegisterLocalRVT() instead
3222 * of the obsolete xsltRegisterTmpRVT().
3223 */
3224 if (ctxt->tmpRVT) {
3225 xmlDocPtr curdoc = ctxt->tmpRVT, tmp;
3226
3227 while (curdoc != NULL) {
3228 tmp = curdoc;
3229 curdoc = (xmlDocPtr) curdoc->next;
3230 xsltReleaseRVT(ctxt, tmp);
3231 }
3232 }
3233 ctxt->tmpRVT = oldUserFragmentTop;
3234
3235 /*
3236 * Pop the xsl:template declaration from the stack.
3237 */
3238 templPop(ctxt);
3239 if (ctxt->profile) {
3240 long spent, child, total, end;
3241
3242 end = xsltTimestamp();
3243 child = profPop(ctxt);
3244 total = end - start;
3245 spent = total - child;
3246 if (spent <= 0) {
3247 /*
3248 * Not possible unless the original calibration failed
3249 * we can try to correct it on the fly.
3250 */
3251 xsltCalibrateAdjust(spent);
3252 spent = 0;
3253 }
3254
3255 templ->time += spent;
3256 if (ctxt->profNr > 0)
3257 ctxt->profTab[ctxt->profNr - 1] += total;
3258 }
3259
3260 #ifdef WITH_DEBUGGER
3261 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) {
3262 xslDropCall();
3263 }
3264 #endif
3265 }
3266
3267
3268 /**
3269 * xsltApplyOneTemplate:
3270 * @ctxt: a XSLT process context
3271 * @contextNode: the node in the source tree.
3272 * @list: the nodes of a sequence constructor
3273 * @templ: not used
3274 * @params: a set of parameters (xsl:param) or NULL
3275 *
3276 * Processes a sequence constructor on the current node in the source tree.
3277 *
3278 * @params are the already computed variable stack items; this function
3279 * pushes them on the variable stack, and pops them before exiting; it's
3280 * left to the caller to free or reuse @params afterwards. The initial
3281 * states of the variable stack will always be restored before this
3282 * function exits.
3283 * NOTE that this does *not* initiate a new distinct variable scope; i.e.
3284 * variables already on the stack are visible to the process. The caller's
3285 * side needs to start a new variable scope if needed (e.g. in exsl:function).
3286 *
3287 * @templ is obsolete and not used anymore (e.g. <exslt:function> does not
3288 * provide a @templ); a non-NULL @templ might raise an error in the future.
3289 *
3290 * BIG NOTE: This function is not intended to process the content of an
3291 * xsl:template; it does not expect xsl:param instructions in @list and
3292 * will report errors if found.
3293 *
3294 * Called by:
3295 * - xsltEvalVariable() (variables.c)
3296 * - exsltFuncFunctionFunction() (libexsl/functions.c)
3297 */
3298 void
3299 xsltApplyOneTemplate(xsltTransformContextPtr ctxt,
3300 xmlNodePtr contextNode,
3301 xmlNodePtr list,
3302 xsltTemplatePtr templ ATTRIBUTE_UNUSED,
3303 xsltStackElemPtr params)
3304 {
3305 if ((ctxt == NULL) || (list == NULL))
3306 return;
3307 CHECK_STOPPED;
3308
3309 if (params) {
3310 /*
3311 * This code should be obsolete - was previously used
3312 * by libexslt/functions.c, but due to bug 381319 the
3313 * logic there was changed.
3314 */
3315 int oldVarsNr = ctxt->varsNr;
3316
3317 /*
3318 * Push the given xsl:param(s) onto the variable stack.
3319 */
3320 while (params != NULL) {
3321 xsltLocalVariablePush(ctxt, params, -1);
3322 params = params->next;
3323 }
3324 xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
3325 /*
3326 * Pop the given xsl:param(s) from the stack but don't free them.
3327 */
3328 xsltLocalVariablePop(ctxt, oldVarsNr, -2);
3329 } else
3330 xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
3331 }
3332
3333 /************************************************************************
3334 * *
3335 * XSLT-1.1 extensions *
3336 * *
3337 ************************************************************************/
3338
3339 /**
3340 * xsltDocumentElem:
3341 * @ctxt: an XSLT processing context
3342 * @node: The current node
3343 * @inst: the instruction in the stylesheet
3344 * @castedComp: precomputed information
3345 *
3346 * Process an EXSLT/XSLT-1.1 document element
3347 */
3348 void
3349 xsltDocumentElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
3350 xmlNodePtr inst, xsltStylePreCompPtr castedComp)
3351 {
3352 #ifdef XSLT_REFACTORED
3353 xsltStyleItemDocumentPtr comp = (xsltStyleItemDocumentPtr) castedComp;
3354 #else
3355 xsltStylePreCompPtr comp = castedComp;
3356 #endif
3357 xsltStylesheetPtr style = NULL;
3358 int ret;
3359 xmlChar *filename = NULL, *prop, *elements;
3360 xmlChar *element, *end;
3361 xmlDocPtr res = NULL;
3362 xmlDocPtr oldOutput;
3363 xmlNodePtr oldInsert, root;
3364 const char *oldOutputFile;
3365 xsltOutputType oldType;
3366 xmlChar *URL = NULL;
3367 const xmlChar *method;
3368 const xmlChar *doctypePublic;
3369 const xmlChar *doctypeSystem;
3370 const xmlChar *version;
3371 const xmlChar *encoding;
3372 int redirect_write_append = 0;
3373
3374 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
3375 return;
3376
3377 if (comp->filename == NULL) {
3378
3379 if (xmlStrEqual(inst->name, (const xmlChar *) "output")) {
3380 /*
3381 * The element "output" is in the namespace XSLT_SAXON_NAMESPACE
3382 * (http://icl.com/saxon)
3383 * The @file is in no namespace.
3384 */
3385 #ifdef WITH_XSLT_DEBUG_EXTRA
3386 xsltGenericDebug(xsltGenericDebugContext,
3387 "Found saxon:output extension\n");
3388 #endif
3389 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3390 (const xmlChar *) "file",
3391 XSLT_SAXON_NAMESPACE);
3392
3393 if (URL == NULL)
3394 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3395 (const xmlChar *) "href",
3396 XSLT_SAXON_NAMESPACE);
3397 } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) {
3398 #ifdef WITH_XSLT_DEBUG_EXTRA
3399 xsltGenericDebug(xsltGenericDebugContext,
3400 "Found xalan:write extension\n");
3401 #endif
3402 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3403 (const xmlChar *)
3404 "select",
3405 XSLT_XALAN_NAMESPACE);
3406 if (URL != NULL) {
3407 xmlXPathCompExprPtr cmp;
3408 xmlChar *val;
3409
3410 /*
3411 * Trying to handle bug #59212
3412 * The value of the "select" attribute is an
3413 * XPath expression.
3414 * (see http://xml.apache.org/xalan-j/extensionslib.html#redirect)
3415 */
3416 cmp = xmlXPathCompile(URL);
3417 val = xsltEvalXPathString(ctxt, cmp);
3418 xmlXPathFreeCompExpr(cmp);
3419 xmlFree(URL);
3420 URL = val;
3421 }
3422 if (URL == NULL)
3423 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3424 (const xmlChar *)
3425 "file",
3426 XSLT_XALAN_NAMESPACE);
3427 if (URL == NULL)
3428 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3429 (const xmlChar *)
3430 "href",
3431 XSLT_XALAN_NAMESPACE);
3432 } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) {
3433 URL = xsltEvalAttrValueTemplate(ctxt, inst,
3434 (const xmlChar *) "href",
3435 NULL);
3436 }
3437
3438 } else {
3439 URL = xmlStrdup(comp->filename);
3440 }
3441
3442 if (URL == NULL) {
3443 xsltTransformError(ctxt, NULL, inst,
3444 "xsltDocumentElem: href/URI-Reference not found\n");
3445 return;
3446 }
3447
3448 /*
3449 * If the computation failed, it's likely that the URL wasn't escaped
3450 */
3451 filename = xmlBuildURI(URL, (const xmlChar *) ctxt->outputFile);
3452 if (filename == NULL) {
3453 xmlChar *escURL;
3454
3455 escURL=xmlURIEscapeStr(URL, BAD_CAST ":/.?,");
3456 if (escURL != NULL) {
3457 filename = xmlBuildURI(escURL, (const xmlChar *) ctxt->outputFile);
3458 xmlFree(escURL);
3459 }
3460 }
3461
3462 if (filename == NULL) {
3463 xsltTransformError(ctxt, NULL, inst,
3464 "xsltDocumentElem: URL computation failed for %s\n",
3465 URL);
3466 xmlFree(URL);
3467 return;
3468 }
3469
3470 /*
3471 * Security checking: can we write to this resource
3472 */
3473 if (ctxt->sec != NULL) {
3474 ret = xsltCheckWrite(ctxt->sec, ctxt, filename);
3475 if (ret == 0) {
3476 xsltTransformError(ctxt, NULL, inst,
3477 "xsltDocumentElem: write rights for %s denied\n",
3478 filename);
3479 xmlFree(URL);
3480 xmlFree(filename);
3481 return;
3482 }
3483 }
3484
3485 oldOutputFile = ctxt->outputFile;
3486 oldOutput = ctxt->output;
3487 oldInsert = ctxt->insert;
3488 oldType = ctxt->type;
3489 ctxt->outputFile = (const char *) filename;
3490
3491 style = xsltNewStylesheet();
3492 if (style == NULL) {
3493 xsltTransformError(ctxt, NULL, inst,
3494 "xsltDocumentElem: out of memory\n");
3495 goto error;
3496 }
3497
3498 /*
3499 * Version described in 1.1 draft allows full parameterization
3500 * of the output.
3501 */
3502 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3503 (const xmlChar *) "version",
3504 NULL);
3505 if (prop != NULL) {
3506 if (style->version != NULL)
3507 xmlFree(style->version);
3508 style->version = prop;
3509 }
3510 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3511 (const xmlChar *) "encoding",
3512 NULL);
3513 if (prop != NULL) {
3514 if (style->encoding != NULL)
3515 xmlFree(style->encoding);
3516 style->encoding = prop;
3517 }
3518 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3519 (const xmlChar *) "method",
3520 NULL);
3521 if (prop != NULL) {
3522 const xmlChar *URI;
3523
3524 if (style->method != NULL)
3525 xmlFree(style->method);
3526 style->method = NULL;
3527 if (style->methodURI != NULL)
3528 xmlFree(style->methodURI);
3529 style->methodURI = NULL;
3530
3531 URI = xsltGetQNameURI(inst, &prop);
3532 if (prop == NULL) {
3533 if (style != NULL) style->errors++;
3534 } else if (URI == NULL) {
3535 if ((xmlStrEqual(prop, (const xmlChar *) "xml")) ||
3536 (xmlStrEqual(prop, (const xmlChar *) "html")) ||
3537 (xmlStrEqual(prop, (const xmlChar *) "text"))) {
3538 style->method = prop;
3539 } else {
3540 xsltTransformError(ctxt, NULL, inst,
3541 "invalid value for method: %s\n", prop);
3542 if (style != NULL) style->warnings++;
3543 }
3544 } else {
3545 style->method = prop;
3546 style->methodURI = xmlStrdup(URI);
3547 }
3548 }
3549 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3550 (const xmlChar *)
3551 "doctype-system", NULL);
3552 if (prop != NULL) {
3553 if (style->doctypeSystem != NULL)
3554 xmlFree(style->doctypeSystem);
3555 style->doctypeSystem = prop;
3556 }
3557 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3558 (const xmlChar *)
3559 "doctype-public", NULL);
3560 if (prop != NULL) {
3561 if (style->doctypePublic != NULL)
3562 xmlFree(style->doctypePublic);
3563 style->doctypePublic = prop;
3564 }
3565 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3566 (const xmlChar *) "standalone",
3567 NULL);
3568 if (prop != NULL) {
3569 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
3570 style->standalone = 1;
3571 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
3572 style->standalone = 0;
3573 } else {
3574 xsltTransformError(ctxt, NULL, inst,
3575 "invalid value for standalone: %s\n",
3576 prop);
3577 if (style != NULL) style->warnings++;
3578 }
3579 xmlFree(prop);
3580 }
3581
3582 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3583 (const xmlChar *) "indent",
3584 NULL);
3585 if (prop != NULL) {
3586 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
3587 style->indent = 1;
3588 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
3589 style->indent = 0;
3590 } else {
3591 xsltTransformError(ctxt, NULL, inst,
3592 "invalid value for indent: %s\n", prop);
3593 if (style != NULL) style->warnings++;
3594 }
3595 xmlFree(prop);
3596 }
3597
3598 prop = xsltEvalAttrValueTemplate(ctxt, inst,
3599 (const xmlChar *)
3600 "omit-xml-declaration",
3601 NULL);
3602 if (prop != NULL) {
3603 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
3604 style->omitXmlDeclaration = 1;
3605 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
3606 style->omitXmlDeclaration = 0;
3607 } else {
3608 xsltTransformError(ctxt, NULL, inst,
3609 "invalid value for omit-xml-declaration: %s\n",
3610 prop);
3611 if (style != NULL) style->warnings++;
3612 }
3613 xmlFree(prop);
3614 }
3615
3616 elements = xsltEvalAttrValueTemplate(ctxt, inst,
3617 (const xmlChar *)
3618 "cdata-section-elements",
3619 NULL);
3620 if (elements != NULL) {
3621 if (style->stripSpaces == NULL)
3622 style->stripSpaces = xmlHashCreate(10);
3623 if (style->stripSpaces == NULL)
3624 return;
3625
3626 element = elements;
3627 while (*element != 0) {
3628 while (IS_BLANK_CH(*element))
3629 element++;
3630 if (*element == 0)
3631 break;
3632 end = element;
3633 while ((*end != 0) && (!IS_BLANK_CH(*end)))
3634 end++;
3635 element = xmlStrndup(element, end - element);
3636 if (element) {
3637 const xmlChar *URI;
3638
3639 #ifdef WITH_XSLT_DEBUG_PARSING
3640 xsltGenericDebug(xsltGenericDebugContext,
3641 "add cdata section output element %s\n",
3642 element);
3643 #endif
3644 URI = xsltGetQNameURI(inst, &element);
3645
3646 xmlHashAddEntry2(style->stripSpaces, element, URI,
3647 (xmlChar *) "cdata");
3648 xmlFree(element);
3649 }
3650 element = end;
3651 }
3652 xmlFree(elements);
3653 }
3654
3655 /*
3656 * Create a new document tree and process the element template
3657 */
3658 XSLT_GET_IMPORT_PTR(method, style, method)
3659 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
3660 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
3661 XSLT_GET_IMPORT_PTR(version, style, version)
3662 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
3663
3664 if ((method != NULL) &&
3665 (!xmlStrEqual(method, (const xmlChar *) "xml"))) {
3666 if (xmlStrEqual(method, (const xmlChar *) "html")) {
3667 ctxt->type = XSLT_OUTPUT_HTML;
3668 if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
3669 res = htmlNewDoc(doctypeSystem, doctypePublic);
3670 else {
3671 if (version != NULL) {
3672 #ifdef XSLT_GENERATE_HTML_DOCTYPE
3673 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
3674 #endif
3675 }
3676 res = htmlNewDocNoDtD(doctypeSystem, doctypePublic);
3677 }
3678 if (res == NULL)
3679 goto error;
3680 res->dict = ctxt->dict;
3681 xmlDictReference(res->dict);
3682 } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
3683 xsltTransformError(ctxt, NULL, inst,
3684 "xsltDocumentElem: unsupported method xhtml\n");
3685 ctxt->type = XSLT_OUTPUT_HTML;
3686 res = htmlNewDocNoDtD(doctypeSystem, doctypePublic);
3687 if (res == NULL)
3688 goto error;
3689 res->dict = ctxt->dict;
3690 xmlDictReference(res->dict);
3691 } else if (xmlStrEqual(method, (const xmlChar *) "text")) {
3692 ctxt->type = XSLT_OUTPUT_TEXT;
3693 res = xmlNewDoc(style->version);
3694 if (res == NULL)
3695 goto error;
3696 res->dict = ctxt->dict;
3697 xmlDictReference(res->dict);
3698 #ifdef WITH_XSLT_DEBUG
3699 xsltGenericDebug(xsltGenericDebugContext,
3700 "reusing transformation dict for output\n");
3701 #endif
3702 } else {
3703 xsltTransformError(ctxt, NULL, inst,
3704 "xsltDocumentElem: unsupported method (%s)\n",
3705 method);
3706 goto error;
3707 }
3708 } else {
3709 ctxt->type = XSLT_OUTPUT_XML;
3710 res = xmlNewDoc(style->version);
3711 if (res == NULL)
3712 goto error;
3713 res->dict = ctxt->dict;
3714 xmlDictReference(res->dict);
3715 #ifdef WITH_XSLT_DEBUG
3716 xsltGenericDebug(xsltGenericDebugContext,
3717 "reusing transformation dict for output\n");
3718 #endif
3719 }
3720 res->charset = XML_CHAR_ENCODING_UTF8;
3721 if (encoding != NULL)
3722 res->encoding = xmlStrdup(encoding);
3723 ctxt->output = res;
3724 ctxt->insert = (xmlNodePtr) res;
3725 xsltApplySequenceConstructor(ctxt, node, inst->children, NULL);
3726
3727 /*
3728 * Do some post processing work depending on the generated output
3729 */
3730 root = xmlDocGetRootElement(res);
3731 if (root != NULL) {
3732 const xmlChar *doctype = NULL;
3733
3734 if ((root->ns != NULL) && (root->ns->prefix != NULL))
3735 doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name);
3736 if (doctype == NULL)
3737 doctype = root->name;
3738
3739 /*
3740 * Apply the default selection of the method
3741 */
3742