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