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