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