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