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