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