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