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