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