Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/xslt/libxslt/xsltutils.c
4389 views
1
/*
2
* xsltutils.c: Utilities for the XSL Transformation 1.0 engine
3
*
4
* Reference:
5
* http://www.w3.org/TR/1999/REC-xslt-19991116
6
*
7
* See Copyright for the status of this software.
8
*
9
* [email protected]
10
*/
11
12
#define IN_LIBXSLT
13
#include "libxslt.h"
14
15
#ifndef XSLT_NEED_TRIO
16
#include <stdio.h>
17
#else
18
#include <trio.h>
19
#endif
20
21
#include <string.h>
22
#include <stdlib.h>
23
#include <stdarg.h>
24
#include <time.h>
25
26
#ifdef HAVE_SYS_TIME_H
27
#include <sys/time.h>
28
#endif
29
#ifdef HAVE_UNISTD_H
30
#include <unistd.h>
31
#endif
32
33
#include <libxml/xmlmemory.h>
34
#include <libxml/tree.h>
35
#include <libxml/HTMLtree.h>
36
#include <libxml/xmlerror.h>
37
#include <libxml/xmlIO.h>
38
#include "xsltutils.h"
39
#include "templates.h"
40
#include "xsltInternals.h"
41
#include "imports.h"
42
#include "transform.h"
43
44
#if defined(_WIN32)
45
#include <windows.h>
46
#define XSLT_WIN32_PERFORMANCE_COUNTER
47
#endif
48
49
/************************************************************************
50
* *
51
* Convenience function *
52
* *
53
************************************************************************/
54
55
/**
56
* xsltGetCNsProp:
57
* @style: the stylesheet
58
* @node: the node
59
* @name: the attribute name
60
* @nameSpace: the URI of the namespace
61
*
62
* Similar to xmlGetNsProp() but with a slightly different semantic
63
*
64
* Search and get the value of an attribute associated to a node
65
* This attribute has to be anchored in the namespace specified,
66
* or has no namespace and the element is in that namespace.
67
*
68
* This does the entity substitution.
69
* This function looks in DTD attribute declaration for #FIXED or
70
* default declaration values unless DTD use has been turned off.
71
*
72
* Returns the attribute value or NULL if not found. The string is allocated
73
* in the stylesheet dictionary.
74
*/
75
const xmlChar *
76
xsltGetCNsProp(xsltStylesheetPtr style, xmlNodePtr node,
77
const xmlChar *name, const xmlChar *nameSpace) {
78
xmlAttrPtr prop;
79
xmlDocPtr doc;
80
xmlNsPtr ns;
81
xmlChar *tmp;
82
const xmlChar *ret;
83
84
if ((node == NULL) || (style == NULL) || (style->dict == NULL))
85
return(NULL);
86
87
if (nameSpace == NULL)
88
return xmlGetProp(node, name);
89
90
if (node->type == XML_NAMESPACE_DECL)
91
return(NULL);
92
if (node->type == XML_ELEMENT_NODE)
93
prop = node->properties;
94
else
95
prop = NULL;
96
while (prop != NULL) {
97
/*
98
* One need to have
99
* - same attribute names
100
* - and the attribute carrying that namespace
101
*/
102
if ((xmlStrEqual(prop->name, name)) &&
103
(((prop->ns == NULL) && (node->ns != NULL) &&
104
(xmlStrEqual(node->ns->href, nameSpace))) ||
105
((prop->ns != NULL) &&
106
(xmlStrEqual(prop->ns->href, nameSpace))))) {
107
108
tmp = xmlNodeListGetString(node->doc, prop->children, 1);
109
if (tmp == NULL)
110
ret = xmlDictLookup(style->dict, BAD_CAST "", 0);
111
else {
112
ret = xmlDictLookup(style->dict, tmp, -1);
113
xmlFree(tmp);
114
}
115
return ret;
116
}
117
prop = prop->next;
118
}
119
tmp = NULL;
120
/*
121
* Check if there is a default declaration in the internal
122
* or external subsets
123
*/
124
doc = node->doc;
125
if (doc != NULL) {
126
if (doc->intSubset != NULL) {
127
xmlAttributePtr attrDecl;
128
129
attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
130
if ((attrDecl == NULL) && (doc->extSubset != NULL))
131
attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
132
133
if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) {
134
/*
135
* The DTD declaration only allows a prefix search
136
*/
137
ns = xmlSearchNs(doc, node, attrDecl->prefix);
138
if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace)))
139
return(xmlDictLookup(style->dict,
140
attrDecl->defaultValue, -1));
141
}
142
}
143
}
144
return(NULL);
145
}
146
/**
147
* xsltGetNsProp:
148
* @node: the node
149
* @name: the attribute name
150
* @nameSpace: the URI of the namespace
151
*
152
* Similar to xmlGetNsProp() but with a slightly different semantic
153
*
154
* Search and get the value of an attribute associated to a node
155
* This attribute has to be anchored in the namespace specified,
156
* or has no namespace and the element is in that namespace.
157
*
158
* This does the entity substitution.
159
* This function looks in DTD attribute declaration for #FIXED or
160
* default declaration values unless DTD use has been turned off.
161
*
162
* Returns the attribute value or NULL if not found.
163
* It's up to the caller to free the memory.
164
*/
165
xmlChar *
166
xsltGetNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) {
167
xmlAttrPtr prop;
168
xmlDocPtr doc;
169
xmlNsPtr ns;
170
171
if (node == NULL)
172
return(NULL);
173
174
if (nameSpace == NULL)
175
return xmlGetProp(node, name);
176
177
if (node->type == XML_NAMESPACE_DECL)
178
return(NULL);
179
if (node->type == XML_ELEMENT_NODE)
180
prop = node->properties;
181
else
182
prop = NULL;
183
/*
184
* TODO: Substitute xmlGetProp() for xmlGetNsProp(), since the former
185
* is not namespace-aware and will return an attribute with equal
186
* name regardless of its namespace.
187
* Example:
188
* <xsl:element foo:name="myName"/>
189
* So this would return "myName" even if an attribute @name
190
* in the XSLT was requested.
191
*/
192
while (prop != NULL) {
193
/*
194
* One need to have
195
* - same attribute names
196
* - and the attribute carrying that namespace
197
*/
198
if ((xmlStrEqual(prop->name, name)) &&
199
(((prop->ns == NULL) && (node->ns != NULL) &&
200
(xmlStrEqual(node->ns->href, nameSpace))) ||
201
((prop->ns != NULL) &&
202
(xmlStrEqual(prop->ns->href, nameSpace))))) {
203
xmlChar *ret;
204
205
ret = xmlNodeListGetString(node->doc, prop->children, 1);
206
if (ret == NULL) return(xmlStrdup((xmlChar *)""));
207
return(ret);
208
}
209
prop = prop->next;
210
}
211
212
/*
213
* Check if there is a default declaration in the internal
214
* or external subsets
215
*/
216
doc = node->doc;
217
if (doc != NULL) {
218
if (doc->intSubset != NULL) {
219
xmlAttributePtr attrDecl;
220
221
attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
222
if ((attrDecl == NULL) && (doc->extSubset != NULL))
223
attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
224
225
if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) {
226
/*
227
* The DTD declaration only allows a prefix search
228
*/
229
ns = xmlSearchNs(doc, node, attrDecl->prefix);
230
if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace)))
231
return(xmlStrdup(attrDecl->defaultValue));
232
}
233
}
234
}
235
return(NULL);
236
}
237
238
/**
239
* xsltGetUTF8Char:
240
* @utf: a sequence of UTF-8 encoded bytes
241
* @len: a pointer to @bytes len
242
*
243
* Read one UTF8 Char from @utf
244
* Function copied from libxml2 xmlGetUTF8Char() ... to discard ultimately
245
* and use the original API
246
*
247
* Returns the char value or -1 in case of error and update @len with the
248
* number of bytes used
249
*/
250
int
251
xsltGetUTF8Char(const unsigned char *utf, int *len) {
252
unsigned int c;
253
254
if (utf == NULL)
255
goto error;
256
if (len == NULL)
257
goto error;
258
if (*len < 1)
259
goto error;
260
261
c = utf[0];
262
if (c & 0x80) {
263
if (*len < 2)
264
goto error;
265
if ((utf[1] & 0xc0) != 0x80)
266
goto error;
267
if ((c & 0xe0) == 0xe0) {
268
if (*len < 3)
269
goto error;
270
if ((utf[2] & 0xc0) != 0x80)
271
goto error;
272
if ((c & 0xf0) == 0xf0) {
273
if (*len < 4)
274
goto error;
275
if ((c & 0xf8) != 0xf0 || (utf[3] & 0xc0) != 0x80)
276
goto error;
277
*len = 4;
278
/* 4-byte code */
279
c = (utf[0] & 0x7) << 18;
280
c |= (utf[1] & 0x3f) << 12;
281
c |= (utf[2] & 0x3f) << 6;
282
c |= utf[3] & 0x3f;
283
} else {
284
/* 3-byte code */
285
*len = 3;
286
c = (utf[0] & 0xf) << 12;
287
c |= (utf[1] & 0x3f) << 6;
288
c |= utf[2] & 0x3f;
289
}
290
} else {
291
/* 2-byte code */
292
*len = 2;
293
c = (utf[0] & 0x1f) << 6;
294
c |= utf[1] & 0x3f;
295
}
296
} else {
297
/* 1-byte code */
298
*len = 1;
299
}
300
return(c);
301
302
error:
303
if (len != NULL)
304
*len = 0;
305
return(-1);
306
}
307
308
/**
309
* xsltGetUTF8CharZ:
310
* @utf: a sequence of UTF-8 encoded bytes
311
* @len: a pointer to @bytes len
312
*
313
* Read one UTF8 Char from a null-terminated string.
314
*
315
* Returns the char value or -1 in case of error and update @len with the
316
* number of bytes used
317
*/
318
int
319
xsltGetUTF8CharZ(const unsigned char *utf, int *len) {
320
unsigned int c;
321
322
if (utf == NULL)
323
goto error;
324
if (len == NULL)
325
goto error;
326
327
c = utf[0];
328
if (c & 0x80) {
329
if ((utf[1] & 0xc0) != 0x80)
330
goto error;
331
if ((c & 0xe0) == 0xe0) {
332
if ((utf[2] & 0xc0) != 0x80)
333
goto error;
334
if ((c & 0xf0) == 0xf0) {
335
if ((c & 0xf8) != 0xf0 || (utf[3] & 0xc0) != 0x80)
336
goto error;
337
*len = 4;
338
/* 4-byte code */
339
c = (utf[0] & 0x7) << 18;
340
c |= (utf[1] & 0x3f) << 12;
341
c |= (utf[2] & 0x3f) << 6;
342
c |= utf[3] & 0x3f;
343
} else {
344
/* 3-byte code */
345
*len = 3;
346
c = (utf[0] & 0xf) << 12;
347
c |= (utf[1] & 0x3f) << 6;
348
c |= utf[2] & 0x3f;
349
}
350
} else {
351
/* 2-byte code */
352
*len = 2;
353
c = (utf[0] & 0x1f) << 6;
354
c |= utf[1] & 0x3f;
355
}
356
} else {
357
/* 1-byte code */
358
*len = 1;
359
}
360
return(c);
361
362
error:
363
if (len != NULL)
364
*len = 0;
365
return(-1);
366
}
367
368
#ifdef XSLT_REFACTORED
369
370
/**
371
* xsltPointerListAddSize:
372
* @list: the pointer list structure
373
* @item: the item to be stored
374
* @initialSize: the initial size of the list
375
*
376
* Adds an item to the list.
377
*
378
* Returns the position of the added item in the list or
379
* -1 in case of an error.
380
*/
381
int
382
xsltPointerListAddSize(xsltPointerListPtr list,
383
void *item,
384
int initialSize)
385
{
386
if (list->items == NULL) {
387
if (initialSize <= 0)
388
initialSize = 1;
389
list->items = (void **) xmlMalloc(
390
initialSize * sizeof(void *));
391
if (list->items == NULL) {
392
xsltGenericError(xsltGenericErrorContext,
393
"xsltPointerListAddSize: memory allocation failure.\n");
394
return(-1);
395
}
396
list->number = 0;
397
list->size = initialSize;
398
} else if (list->size <= list->number) {
399
list->size *= 2;
400
list->items = (void **) xmlRealloc(list->items,
401
list->size * sizeof(void *));
402
if (list->items == NULL) {
403
xsltGenericError(xsltGenericErrorContext,
404
"xsltPointerListAddSize: memory re-allocation failure.\n");
405
list->size = 0;
406
return(-1);
407
}
408
}
409
list->items[list->number++] = item;
410
return(0);
411
}
412
413
/**
414
* xsltPointerListCreate:
415
* @initialSize: the initial size for the list
416
*
417
* Creates an xsltPointerList structure.
418
*
419
* Returns a xsltPointerList structure or NULL in case of an error.
420
*/
421
xsltPointerListPtr
422
xsltPointerListCreate(int initialSize)
423
{
424
xsltPointerListPtr ret;
425
426
ret = xmlMalloc(sizeof(xsltPointerList));
427
if (ret == NULL) {
428
xsltGenericError(xsltGenericErrorContext,
429
"xsltPointerListCreate: memory allocation failure.\n");
430
return (NULL);
431
}
432
memset(ret, 0, sizeof(xsltPointerList));
433
if (initialSize > 0) {
434
xsltPointerListAddSize(ret, NULL, initialSize);
435
ret->number = 0;
436
}
437
return (ret);
438
}
439
440
/**
441
* xsltPointerListFree:
442
* @list: pointer to the list to be freed
443
*
444
* Frees the xsltPointerList structure. This does not free
445
* the content of the list.
446
*/
447
void
448
xsltPointerListFree(xsltPointerListPtr list)
449
{
450
if (list == NULL)
451
return;
452
if (list->items != NULL)
453
xmlFree(list->items);
454
xmlFree(list);
455
}
456
457
/**
458
* xsltPointerListClear:
459
* @list: pointer to the list to be cleared
460
*
461
* Resets the list, but does not free the allocated array
462
* and does not free the content of the list.
463
*/
464
void
465
xsltPointerListClear(xsltPointerListPtr list)
466
{
467
if (list->items != NULL) {
468
xmlFree(list->items);
469
list->items = NULL;
470
}
471
list->number = 0;
472
list->size = 0;
473
}
474
475
#endif /* XSLT_REFACTORED */
476
477
/************************************************************************
478
* *
479
* Handling of XSLT stylesheets messages *
480
* *
481
************************************************************************/
482
483
/**
484
* xsltMessage:
485
* @ctxt: an XSLT processing context
486
* @node: The current node
487
* @inst: The node containing the message instruction
488
*
489
* Process and xsl:message construct
490
*/
491
void
492
xsltMessage(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst) {
493
xmlGenericErrorFunc error = xsltGenericError;
494
void *errctx = xsltGenericErrorContext;
495
xmlChar *prop, *message;
496
int terminate = 0;
497
498
if ((ctxt == NULL) || (inst == NULL))
499
return;
500
501
if (ctxt->error != NULL) {
502
error = ctxt->error;
503
errctx = ctxt->errctx;
504
}
505
506
prop = xmlGetNsProp(inst, (const xmlChar *)"terminate", NULL);
507
if (prop != NULL) {
508
if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
509
terminate = 1;
510
} else if (xmlStrEqual(prop, (const xmlChar *)"no")) {
511
terminate = 0;
512
} else {
513
xsltTransformError(ctxt, NULL, inst,
514
"xsl:message : terminate expecting 'yes' or 'no'\n");
515
}
516
xmlFree(prop);
517
}
518
message = xsltEvalTemplateString(ctxt, node, inst);
519
if (message != NULL) {
520
int len = xmlStrlen(message);
521
522
error(errctx, "%s", (const char *)message);
523
if ((len > 0) && (message[len - 1] != '\n'))
524
error(errctx, "\n");
525
xmlFree(message);
526
}
527
if (terminate)
528
ctxt->state = XSLT_STATE_STOPPED;
529
}
530
531
/************************************************************************
532
* *
533
* Handling of out of context errors *
534
* *
535
************************************************************************/
536
537
#define XSLT_GET_VAR_STR(msg, str) { \
538
int size; \
539
int chars; \
540
char *larger; \
541
va_list ap; \
542
\
543
str = (char *) xmlMalloc(150); \
544
if (str == NULL) \
545
return; \
546
\
547
size = 150; \
548
\
549
while (size < 64000) { \
550
va_start(ap, msg); \
551
chars = vsnprintf(str, size, msg, ap); \
552
va_end(ap); \
553
if ((chars > -1) && (chars < size)) \
554
break; \
555
if (chars > -1) \
556
size += chars + 1; \
557
else \
558
size += 100; \
559
if ((larger = (char *) xmlRealloc(str, size)) == NULL) {\
560
xmlFree(str); \
561
return; \
562
} \
563
str = larger; \
564
} \
565
}
566
/**
567
* xsltGenericErrorDefaultFunc:
568
* @ctx: an error context
569
* @msg: the message to display/transmit
570
* @...: extra parameters for the message display
571
*
572
* Default handler for out of context error messages.
573
*/
574
static void LIBXSLT_ATTR_FORMAT(2,3)
575
xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
576
va_list args;
577
578
if (xsltGenericErrorContext == NULL)
579
xsltGenericErrorContext = (void *) stderr;
580
581
va_start(args, msg);
582
vfprintf((FILE *)xsltGenericErrorContext, msg, args);
583
va_end(args);
584
}
585
586
xmlGenericErrorFunc xsltGenericError = xsltGenericErrorDefaultFunc;
587
void *xsltGenericErrorContext = NULL;
588
589
590
/**
591
* xsltSetGenericErrorFunc:
592
* @ctx: the new error handling context
593
* @handler: the new handler function
594
*
595
* Function to reset the handler and the error context for out of
596
* context error messages.
597
* This simply means that @handler will be called for subsequent
598
* error messages while not parsing nor validating. And @ctx will
599
* be passed as first argument to @handler
600
* One can simply force messages to be emitted to another FILE * than
601
* stderr by setting @ctx to this file handle and @handler to NULL.
602
*/
603
void
604
xsltSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) {
605
xsltGenericErrorContext = ctx;
606
if (handler != NULL)
607
xsltGenericError = handler;
608
else
609
xsltGenericError = xsltGenericErrorDefaultFunc;
610
}
611
612
/**
613
* xsltGenericDebugDefaultFunc:
614
* @ctx: an error context
615
* @msg: the message to display/transmit
616
* @...: extra parameters for the message display
617
*
618
* Default handler for out of context error messages.
619
*/
620
static void LIBXSLT_ATTR_FORMAT(2,3)
621
xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
622
va_list args;
623
624
if (xsltGenericDebugContext == NULL)
625
return;
626
627
va_start(args, msg);
628
vfprintf((FILE *)xsltGenericDebugContext, msg, args);
629
va_end(args);
630
}
631
632
xmlGenericErrorFunc xsltGenericDebug = xsltGenericDebugDefaultFunc;
633
void *xsltGenericDebugContext = NULL;
634
635
636
/**
637
* xsltSetGenericDebugFunc:
638
* @ctx: the new error handling context
639
* @handler: the new handler function
640
*
641
* Function to reset the handler and the error context for out of
642
* context error messages.
643
* This simply means that @handler will be called for subsequent
644
* error messages while not parsing or validating. And @ctx will
645
* be passed as first argument to @handler
646
* One can simply force messages to be emitted to another FILE * than
647
* stderr by setting @ctx to this file handle and @handler to NULL.
648
*/
649
void
650
xsltSetGenericDebugFunc(void *ctx, xmlGenericErrorFunc handler) {
651
xsltGenericDebugContext = ctx;
652
if (handler != NULL)
653
xsltGenericDebug = handler;
654
else
655
xsltGenericDebug = xsltGenericDebugDefaultFunc;
656
}
657
658
/**
659
* xsltPrintErrorContext:
660
* @ctxt: the transformation context
661
* @style: the stylesheet
662
* @node: the current node being processed
663
*
664
* Display the context of an error.
665
*/
666
void
667
xsltPrintErrorContext(xsltTransformContextPtr ctxt,
668
xsltStylesheetPtr style, xmlNodePtr node) {
669
int line = 0;
670
const xmlChar *file = NULL;
671
const xmlChar *name = NULL;
672
const char *type = "error";
673
xmlGenericErrorFunc error = xsltGenericError;
674
void *errctx = xsltGenericErrorContext;
675
676
if (ctxt != NULL) {
677
if (ctxt->state == XSLT_STATE_OK)
678
ctxt->state = XSLT_STATE_ERROR;
679
if (ctxt->error != NULL) {
680
error = ctxt->error;
681
errctx = ctxt->errctx;
682
}
683
}
684
if ((node == NULL) && (ctxt != NULL))
685
node = ctxt->inst;
686
687
if (node != NULL) {
688
if ((node->type == XML_DOCUMENT_NODE) ||
689
(node->type == XML_HTML_DOCUMENT_NODE)) {
690
xmlDocPtr doc = (xmlDocPtr) node;
691
692
file = doc->URL;
693
} else {
694
line = xmlGetLineNo(node);
695
if ((node->doc != NULL) && (node->doc->URL != NULL))
696
file = node->doc->URL;
697
if (node->name != NULL)
698
name = node->name;
699
}
700
}
701
702
if (ctxt != NULL)
703
type = "runtime error";
704
else if (style != NULL) {
705
#ifdef XSLT_REFACTORED
706
if (XSLT_CCTXT(style)->errSeverity == XSLT_ERROR_SEVERITY_WARNING)
707
type = "compilation warning";
708
else
709
type = "compilation error";
710
#else
711
type = "compilation error";
712
#endif
713
}
714
715
if ((file != NULL) && (line != 0) && (name != NULL))
716
error(errctx, "%s: file %s line %d element %s\n",
717
type, file, line, name);
718
else if ((file != NULL) && (name != NULL))
719
error(errctx, "%s: file %s element %s\n", type, file, name);
720
else if ((file != NULL) && (line != 0))
721
error(errctx, "%s: file %s line %d\n", type, file, line);
722
else if (file != NULL)
723
error(errctx, "%s: file %s\n", type, file);
724
else if (name != NULL)
725
error(errctx, "%s: element %s\n", type, name);
726
else
727
error(errctx, "%s\n", type);
728
}
729
730
/**
731
* xsltSetTransformErrorFunc:
732
* @ctxt: the XSLT transformation context
733
* @ctx: the new error handling context
734
* @handler: the new handler function
735
*
736
* Function to reset the handler and the error context for out of
737
* context error messages specific to a given XSLT transromation.
738
*
739
* This simply means that @handler will be called for subsequent
740
* error messages while running the transformation.
741
*/
742
void
743
xsltSetTransformErrorFunc(xsltTransformContextPtr ctxt,
744
void *ctx, xmlGenericErrorFunc handler)
745
{
746
ctxt->error = handler;
747
ctxt->errctx = ctx;
748
}
749
750
/**
751
* xsltTransformError:
752
* @ctxt: an XSLT transformation context
753
* @style: the XSLT stylesheet used
754
* @node: the current node in the stylesheet
755
* @msg: the message to display/transmit
756
* @...: extra parameters for the message display
757
*
758
* Display and format an error messages, gives file, line, position and
759
* extra parameters, will use the specific transformation context if available
760
*/
761
void
762
xsltTransformError(xsltTransformContextPtr ctxt,
763
xsltStylesheetPtr style,
764
xmlNodePtr node,
765
const char *msg, ...) {
766
xmlGenericErrorFunc error = xsltGenericError;
767
void *errctx = xsltGenericErrorContext;
768
char * str;
769
770
if (ctxt != NULL) {
771
if (ctxt->state == XSLT_STATE_OK)
772
ctxt->state = XSLT_STATE_ERROR;
773
if (ctxt->error != NULL) {
774
error = ctxt->error;
775
errctx = ctxt->errctx;
776
}
777
}
778
if ((node == NULL) && (ctxt != NULL))
779
node = ctxt->inst;
780
xsltPrintErrorContext(ctxt, style, node);
781
XSLT_GET_VAR_STR(msg, str);
782
error(errctx, "%s", str);
783
if (str != NULL)
784
xmlFree(str);
785
}
786
787
/************************************************************************
788
* *
789
* QNames *
790
* *
791
************************************************************************/
792
793
/**
794
* xsltSplitQName:
795
* @dict: a dictionary
796
* @name: the full QName
797
* @prefix: the return value
798
*
799
* Split QNames into prefix and local names, both allocated from a dictionary.
800
*
801
* Returns: the localname or NULL in case of error.
802
*/
803
const xmlChar *
804
xsltSplitQName(xmlDictPtr dict, const xmlChar *name, const xmlChar **prefix) {
805
int len = 0;
806
const xmlChar *ret = NULL;
807
808
*prefix = NULL;
809
if ((name == NULL) || (dict == NULL)) return(NULL);
810
if (name[0] == ':')
811
return(xmlDictLookup(dict, name, -1));
812
while ((name[len] != 0) && (name[len] != ':')) len++;
813
if (name[len] == 0) return(xmlDictLookup(dict, name, -1));
814
*prefix = xmlDictLookup(dict, name, len);
815
ret = xmlDictLookup(dict, &name[len + 1], -1);
816
return(ret);
817
}
818
819
/**
820
* xsltGetQNameURI:
821
* @node: the node holding the QName
822
* @name: pointer to the initial QName value
823
*
824
* This function analyzes @name, if the name contains a prefix,
825
* the function seaches the associated namespace in scope for it.
826
* It will also replace @name value with the NCName, the old value being
827
* freed.
828
* Errors in the prefix lookup are signalled by setting @name to NULL.
829
*
830
* NOTE: the namespace returned is a pointer to the place where it is
831
* defined and hence has the same lifespan as the document holding it.
832
*
833
* Returns the namespace URI if there is a prefix, or NULL if @name is
834
* not prefixed.
835
*/
836
const xmlChar *
837
xsltGetQNameURI(xmlNodePtr node, xmlChar ** name)
838
{
839
int len = 0;
840
xmlChar *qname;
841
xmlNsPtr ns;
842
843
if (name == NULL)
844
return(NULL);
845
qname = *name;
846
if ((qname == NULL) || (*qname == 0))
847
return(NULL);
848
if (node == NULL) {
849
xsltGenericError(xsltGenericErrorContext,
850
"QName: no element for namespace lookup %s\n",
851
qname);
852
xmlFree(qname);
853
*name = NULL;
854
return(NULL);
855
}
856
857
/* nasty but valid */
858
if (qname[0] == ':')
859
return(NULL);
860
861
/*
862
* we are not trying to validate but just to cut, and yes it will
863
* work even if this is a set of UTF-8 encoded chars
864
*/
865
while ((qname[len] != 0) && (qname[len] != ':'))
866
len++;
867
868
if (qname[len] == 0)
869
return(NULL);
870
871
/*
872
* handle xml: separately, this one is magical
873
*/
874
if ((qname[0] == 'x') && (qname[1] == 'm') &&
875
(qname[2] == 'l') && (qname[3] == ':')) {
876
if (qname[4] == 0)
877
return(NULL);
878
*name = xmlStrdup(&qname[4]);
879
xmlFree(qname);
880
return(XML_XML_NAMESPACE);
881
}
882
883
qname[len] = 0;
884
ns = xmlSearchNs(node->doc, node, qname);
885
if (ns == NULL) {
886
xsltGenericError(xsltGenericErrorContext,
887
"%s:%s : no namespace bound to prefix %s\n",
888
qname, &qname[len + 1], qname);
889
*name = NULL;
890
xmlFree(qname);
891
return(NULL);
892
}
893
*name = xmlStrdup(&qname[len + 1]);
894
xmlFree(qname);
895
return(ns->href);
896
}
897
898
/**
899
* xsltGetQNameURI2:
900
* @style: stylesheet pointer
901
* @node: the node holding the QName
902
* @name: pointer to the initial QName value
903
*
904
* This function is similar to xsltGetQNameURI, but is used when
905
* @name is a dictionary entry.
906
*
907
* Returns the namespace URI if there is a prefix, or NULL if @name is
908
* not prefixed.
909
*/
910
const xmlChar *
911
xsltGetQNameURI2(xsltStylesheetPtr style, xmlNodePtr node,
912
const xmlChar **name) {
913
int len = 0;
914
xmlChar *qname;
915
xmlNsPtr ns;
916
917
if (name == NULL)
918
return(NULL);
919
qname = (xmlChar *)*name;
920
if ((qname == NULL) || (*qname == 0))
921
return(NULL);
922
if (node == NULL) {
923
xsltGenericError(xsltGenericErrorContext,
924
"QName: no element for namespace lookup %s\n",
925
qname);
926
*name = NULL;
927
return(NULL);
928
}
929
930
/*
931
* we are not trying to validate but just to cut, and yes it will
932
* work even if this is a set of UTF-8 encoded chars
933
*/
934
while ((qname[len] != 0) && (qname[len] != ':'))
935
len++;
936
937
if (qname[len] == 0)
938
return(NULL);
939
940
/*
941
* handle xml: separately, this one is magical
942
*/
943
if ((qname[0] == 'x') && (qname[1] == 'm') &&
944
(qname[2] == 'l') && (qname[3] == ':')) {
945
if (qname[4] == 0)
946
return(NULL);
947
*name = xmlDictLookup(style->dict, &qname[4], -1);
948
return(XML_XML_NAMESPACE);
949
}
950
951
qname = xmlStrndup(*name, len);
952
ns = xmlSearchNs(node->doc, node, qname);
953
if (ns == NULL) {
954
if (style) {
955
xsltTransformError(NULL, style, node,
956
"No namespace bound to prefix '%s'.\n",
957
qname);
958
style->errors++;
959
} else {
960
xsltGenericError(xsltGenericErrorContext,
961
"%s : no namespace bound to prefix %s\n",
962
*name, qname);
963
}
964
*name = NULL;
965
xmlFree(qname);
966
return(NULL);
967
}
968
*name = xmlDictLookup(style->dict, (*name)+len+1, -1);
969
xmlFree(qname);
970
return(ns->href);
971
}
972
973
/************************************************************************
974
* *
975
* Sorting *
976
* *
977
************************************************************************/
978
979
/**
980
* xsltDocumentSortFunction:
981
* @list: the node set
982
*
983
* reorder the current node list @list accordingly to the document order
984
* This function is slow, obsolete and should not be used anymore.
985
*/
986
void
987
xsltDocumentSortFunction(xmlNodeSetPtr list) {
988
int i, j;
989
int len, tst;
990
xmlNodePtr node;
991
992
if (list == NULL)
993
return;
994
len = list->nodeNr;
995
if (len <= 1)
996
return;
997
/* TODO: sort is really not optimized, does it needs to ? */
998
for (i = 0;i < len -1;i++) {
999
for (j = i + 1; j < len; j++) {
1000
tst = xmlXPathCmpNodes(list->nodeTab[i], list->nodeTab[j]);
1001
if (tst == -1) {
1002
node = list->nodeTab[i];
1003
list->nodeTab[i] = list->nodeTab[j];
1004
list->nodeTab[j] = node;
1005
}
1006
}
1007
}
1008
}
1009
1010
/**
1011
* xsltComputeSortResultInternal:
1012
* @ctxt: a XSLT process context
1013
* @sort: xsl:sort node
1014
* @number: data-type is number
1015
* @locale: transform strings according to locale
1016
*
1017
* reorder the current node list accordingly to the set of sorting
1018
* requirement provided by the array of nodes.
1019
*
1020
* Returns a ordered XPath nodeset or NULL in case of error.
1021
*/
1022
static xmlXPathObjectPtr *
1023
xsltComputeSortResultInternal(xsltTransformContextPtr ctxt, xmlNodePtr sort,
1024
int number, void *locale) {
1025
#ifdef XSLT_REFACTORED
1026
xsltStyleItemSortPtr comp;
1027
#else
1028
const xsltStylePreComp *comp;
1029
#endif
1030
xmlXPathObjectPtr *results = NULL;
1031
xmlNodeSetPtr list = NULL;
1032
xmlXPathObjectPtr res;
1033
int len = 0;
1034
int i;
1035
xmlNodePtr oldNode;
1036
xmlNodePtr oldInst;
1037
int oldPos, oldSize ;
1038
int oldNsNr;
1039
xmlNsPtr *oldNamespaces;
1040
1041
comp = sort->psvi;
1042
if (comp == NULL) {
1043
xsltGenericError(xsltGenericErrorContext,
1044
"xsl:sort : compilation failed\n");
1045
return(NULL);
1046
}
1047
1048
if ((comp->select == NULL) || (comp->comp == NULL))
1049
return(NULL);
1050
1051
list = ctxt->nodeList;
1052
if ((list == NULL) || (list->nodeNr <= 1))
1053
return(NULL);
1054
1055
len = list->nodeNr;
1056
1057
/* TODO: xsl:sort lang attribute */
1058
/* TODO: xsl:sort case-order attribute */
1059
1060
1061
results = xmlMalloc(len * sizeof(xmlXPathObjectPtr));
1062
if (results == NULL) {
1063
xsltGenericError(xsltGenericErrorContext,
1064
"xsltComputeSortResult: memory allocation failure\n");
1065
return(NULL);
1066
}
1067
1068
oldInst = ctxt->inst;
1069
oldNode = ctxt->xpathCtxt->node;
1070
oldPos = ctxt->xpathCtxt->proximityPosition;
1071
oldSize = ctxt->xpathCtxt->contextSize;
1072
oldNsNr = ctxt->xpathCtxt->nsNr;
1073
oldNamespaces = ctxt->xpathCtxt->namespaces;
1074
for (i = 0;i < len;i++) {
1075
ctxt->inst = sort;
1076
ctxt->xpathCtxt->contextSize = len;
1077
ctxt->xpathCtxt->proximityPosition = i + 1;
1078
ctxt->node = list->nodeTab[i];
1079
ctxt->xpathCtxt->node = ctxt->node;
1080
#ifdef XSLT_REFACTORED
1081
if (comp->inScopeNs != NULL) {
1082
ctxt->xpathCtxt->namespaces = comp->inScopeNs->list;
1083
ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber;
1084
} else {
1085
ctxt->xpathCtxt->namespaces = NULL;
1086
ctxt->xpathCtxt->nsNr = 0;
1087
}
1088
#else
1089
ctxt->xpathCtxt->namespaces = comp->nsList;
1090
ctxt->xpathCtxt->nsNr = comp->nsNr;
1091
#endif
1092
res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
1093
if (res != NULL) {
1094
if (res->type != XPATH_STRING)
1095
res = xmlXPathConvertString(res);
1096
if (number)
1097
res = xmlXPathConvertNumber(res);
1098
}
1099
if (res != NULL) {
1100
res->index = i; /* Save original pos for dupl resolv */
1101
if (number) {
1102
if (res->type == XPATH_NUMBER) {
1103
results[i] = res;
1104
} else {
1105
#ifdef WITH_XSLT_DEBUG_PROCESS
1106
xsltGenericDebug(xsltGenericDebugContext,
1107
"xsltComputeSortResult: select didn't evaluate to a number\n");
1108
#endif
1109
results[i] = NULL;
1110
}
1111
} else {
1112
if (res->type == XPATH_STRING) {
1113
if (locale != NULL) {
1114
xmlChar *str = res->stringval;
1115
xmlChar *sortKey = ctxt->genSortKey(locale, str);
1116
1117
if (sortKey == NULL) {
1118
xsltTransformError(ctxt, NULL, sort,
1119
"xsltComputeSortResult: sort key is null\n");
1120
} else {
1121
res->stringval = sortKey;
1122
xmlFree(str);
1123
}
1124
}
1125
1126
results[i] = res;
1127
} else {
1128
#ifdef WITH_XSLT_DEBUG_PROCESS
1129
xsltGenericDebug(xsltGenericDebugContext,
1130
"xsltComputeSortResult: select didn't evaluate to a string\n");
1131
#endif
1132
results[i] = NULL;
1133
}
1134
}
1135
} else {
1136
ctxt->state = XSLT_STATE_STOPPED;
1137
results[i] = NULL;
1138
}
1139
}
1140
ctxt->inst = oldInst;
1141
ctxt->xpathCtxt->node = oldNode;
1142
ctxt->xpathCtxt->contextSize = oldSize;
1143
ctxt->xpathCtxt->proximityPosition = oldPos;
1144
ctxt->xpathCtxt->nsNr = oldNsNr;
1145
ctxt->xpathCtxt->namespaces = oldNamespaces;
1146
1147
return(results);
1148
}
1149
1150
/**
1151
* xsltComputeSortResult:
1152
* @ctxt: a XSLT process context
1153
* @sort: node list
1154
*
1155
* reorder the current node list accordingly to the set of sorting
1156
* requirement provided by the array of nodes.
1157
*
1158
* Returns a ordered XPath nodeset or NULL in case of error.
1159
*/
1160
xmlXPathObjectPtr *
1161
xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) {
1162
const xsltStylePreComp *comp = sort->psvi;
1163
int number = 0;
1164
1165
if (comp != NULL)
1166
number = comp->number;
1167
return xsltComputeSortResultInternal(ctxt, sort, number,
1168
/* locale */ NULL);
1169
}
1170
1171
/**
1172
* xsltDefaultSortFunction:
1173
* @ctxt: a XSLT process context
1174
* @sorts: array of sort nodes
1175
* @nbsorts: the number of sorts in the array
1176
*
1177
* reorder the current node list accordingly to the set of sorting
1178
* requirement provided by the arry of nodes.
1179
*/
1180
void
1181
xsltDefaultSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts,
1182
int nbsorts) {
1183
#ifdef XSLT_REFACTORED
1184
xsltStyleItemSortPtr comp;
1185
#else
1186
const xsltStylePreComp *comp;
1187
#endif
1188
xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT];
1189
xmlXPathObjectPtr *results = NULL, *res;
1190
xmlNodeSetPtr list = NULL;
1191
int len = 0;
1192
int i, j, incr;
1193
int tst;
1194
int depth;
1195
xmlNodePtr node;
1196
xmlXPathObjectPtr tmp;
1197
int number[XSLT_MAX_SORT], desc[XSLT_MAX_SORT];
1198
void *locale[XSLT_MAX_SORT];
1199
1200
if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) ||
1201
(nbsorts >= XSLT_MAX_SORT))
1202
return;
1203
if (sorts[0] == NULL)
1204
return;
1205
comp = sorts[0]->psvi;
1206
if (comp == NULL)
1207
return;
1208
1209
list = ctxt->nodeList;
1210
if ((list == NULL) || (list->nodeNr <= 1))
1211
return; /* nothing to do */
1212
1213
for (j = 0; j < nbsorts; j++) {
1214
xmlChar *lang;
1215
1216
comp = sorts[j]->psvi;
1217
if ((comp->stype == NULL) && (comp->has_stype != 0)) {
1218
xmlChar *stype =
1219
xsltEvalAttrValueTemplate(ctxt, sorts[j],
1220
BAD_CAST "data-type", NULL);
1221
number[j] = 0;
1222
if (stype != NULL) {
1223
if (xmlStrEqual(stype, (const xmlChar *) "text"))
1224
;
1225
else if (xmlStrEqual(stype, (const xmlChar *) "number"))
1226
number[j] = 1;
1227
else {
1228
xsltTransformError(ctxt, NULL, sorts[j],
1229
"xsltDoSortFunction: no support for data-type = %s\n",
1230
stype);
1231
}
1232
xmlFree(stype);
1233
}
1234
} else {
1235
number[j] = comp->number;
1236
}
1237
if ((comp->order == NULL) && (comp->has_order != 0)) {
1238
xmlChar *order = xsltEvalAttrValueTemplate(ctxt, sorts[j],
1239
BAD_CAST "order", NULL);
1240
desc[j] = 0;
1241
if (order != NULL) {
1242
if (xmlStrEqual(order, (const xmlChar *) "ascending"))
1243
;
1244
else if (xmlStrEqual(order, (const xmlChar *) "descending"))
1245
desc[j] = 1;
1246
else {
1247
xsltTransformError(ctxt, NULL, sorts[j],
1248
"xsltDoSortFunction: invalid value %s for order\n",
1249
order);
1250
}
1251
xmlFree(order);
1252
}
1253
} else {
1254
desc[j] = comp->descending;
1255
}
1256
if ((comp->lang == NULL) && (comp->has_lang != 0)) {
1257
lang = xsltEvalAttrValueTemplate(ctxt, sorts[j],
1258
(xmlChar *) "lang",
1259
NULL);
1260
} else {
1261
lang = (xmlChar *) comp->lang;
1262
}
1263
if (lang != NULL) {
1264
locale[j] = ctxt->newLocale(lang, comp->lower_first);
1265
if (lang != comp->lang)
1266
xmlFree(lang);
1267
} else {
1268
locale[j] = NULL;
1269
}
1270
}
1271
1272
len = list->nodeNr;
1273
1274
resultsTab[0] = xsltComputeSortResultInternal(ctxt, sorts[0], number[0],
1275
locale[0]);
1276
for (i = 1;i < XSLT_MAX_SORT;i++)
1277
resultsTab[i] = NULL;
1278
1279
results = resultsTab[0];
1280
1281
comp = sorts[0]->psvi;
1282
if (results == NULL)
1283
goto cleanup;
1284
1285
/* Shell's sort of node-set */
1286
for (incr = len / 2; incr > 0; incr /= 2) {
1287
for (i = incr; i < len; i++) {
1288
j = i - incr;
1289
if (results[i] == NULL)
1290
continue;
1291
1292
while (j >= 0) {
1293
if (results[j] == NULL)
1294
tst = 1;
1295
else {
1296
if (number[0]) {
1297
/* We make NaN smaller than number in accordance
1298
with XSLT spec */
1299
if (xmlXPathIsNaN(results[j]->floatval)) {
1300
if (xmlXPathIsNaN(results[j + incr]->floatval))
1301
tst = 0;
1302
else
1303
tst = -1;
1304
} else if (xmlXPathIsNaN(results[j + incr]->floatval))
1305
tst = 1;
1306
else if (results[j]->floatval ==
1307
results[j + incr]->floatval)
1308
tst = 0;
1309
else if (results[j]->floatval >
1310
results[j + incr]->floatval)
1311
tst = 1;
1312
else tst = -1;
1313
} else {
1314
tst = xmlStrcmp(results[j]->stringval,
1315
results[j + incr]->stringval);
1316
}
1317
if (desc[0])
1318
tst = -tst;
1319
}
1320
if (tst == 0) {
1321
/*
1322
* Okay we need to use multi level sorts
1323
*/
1324
depth = 1;
1325
while (depth < nbsorts) {
1326
if (sorts[depth] == NULL)
1327
break;
1328
comp = sorts[depth]->psvi;
1329
if (comp == NULL)
1330
break;
1331
1332
/*
1333
* Compute the result of the next level for the
1334
* full set, this might be optimized ... or not
1335
*/
1336
if (resultsTab[depth] == NULL)
1337
resultsTab[depth] =
1338
xsltComputeSortResultInternal(ctxt,
1339
sorts[depth],
1340
number[depth],
1341
locale[depth]);
1342
res = resultsTab[depth];
1343
if (res == NULL)
1344
break;
1345
if (res[j] == NULL) {
1346
if (res[j+incr] != NULL)
1347
tst = 1;
1348
} else if (res[j+incr] == NULL) {
1349
tst = -1;
1350
} else {
1351
if (number[depth]) {
1352
/* We make NaN smaller than number in
1353
accordance with XSLT spec */
1354
if (xmlXPathIsNaN(res[j]->floatval)) {
1355
if (xmlXPathIsNaN(res[j +
1356
incr]->floatval))
1357
tst = 0;
1358
else
1359
tst = -1;
1360
} else if (xmlXPathIsNaN(res[j + incr]->
1361
floatval))
1362
tst = 1;
1363
else if (res[j]->floatval == res[j + incr]->
1364
floatval)
1365
tst = 0;
1366
else if (res[j]->floatval >
1367
res[j + incr]->floatval)
1368
tst = 1;
1369
else tst = -1;
1370
} else {
1371
tst = xmlStrcmp(res[j]->stringval,
1372
res[j + incr]->stringval);
1373
}
1374
if (desc[depth])
1375
tst = -tst;
1376
}
1377
1378
/*
1379
* if we still can't differenciate at this level
1380
* try one level deeper.
1381
*/
1382
if (tst != 0)
1383
break;
1384
depth++;
1385
}
1386
}
1387
if (tst == 0) {
1388
tst = results[j]->index > results[j + incr]->index;
1389
}
1390
if (tst > 0) {
1391
tmp = results[j];
1392
results[j] = results[j + incr];
1393
results[j + incr] = tmp;
1394
node = list->nodeTab[j];
1395
list->nodeTab[j] = list->nodeTab[j + incr];
1396
list->nodeTab[j + incr] = node;
1397
depth = 1;
1398
while (depth < nbsorts) {
1399
if (sorts[depth] == NULL)
1400
break;
1401
if (resultsTab[depth] == NULL)
1402
break;
1403
res = resultsTab[depth];
1404
tmp = res[j];
1405
res[j] = res[j + incr];
1406
res[j + incr] = tmp;
1407
depth++;
1408
}
1409
j -= incr;
1410
} else
1411
break;
1412
}
1413
}
1414
}
1415
1416
cleanup:
1417
for (j = 0; j < nbsorts; j++) {
1418
if (locale[j] != NULL) {
1419
ctxt->freeLocale(locale[j]);
1420
}
1421
if (resultsTab[j] != NULL) {
1422
for (i = 0;i < len;i++)
1423
xmlXPathFreeObject(resultsTab[j][i]);
1424
xmlFree(resultsTab[j]);
1425
}
1426
}
1427
}
1428
1429
1430
static xsltSortFunc xsltSortFunction = xsltDefaultSortFunction;
1431
1432
/**
1433
* xsltDoSortFunction:
1434
* @ctxt: a XSLT process context
1435
* @sorts: array of sort nodes
1436
* @nbsorts: the number of sorts in the array
1437
*
1438
* reorder the current node list accordingly to the set of sorting
1439
* requirement provided by the arry of nodes.
1440
* This is a wrapper function, the actual function used is specified
1441
* using xsltSetCtxtSortFunc() to set the context specific sort function,
1442
* or xsltSetSortFunc() to set the global sort function.
1443
* If a sort function is set on the context, this will get called.
1444
* Otherwise the global sort function is called.
1445
*/
1446
void
1447
xsltDoSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr * sorts,
1448
int nbsorts)
1449
{
1450
if (ctxt->sortfunc != NULL)
1451
(ctxt->sortfunc)(ctxt, sorts, nbsorts);
1452
else if (xsltSortFunction != NULL)
1453
xsltSortFunction(ctxt, sorts, nbsorts);
1454
}
1455
1456
/**
1457
* xsltSetSortFunc:
1458
* @handler: the new handler function
1459
*
1460
* DEPRECATED: Use xsltSetCtxtLocaleHandlers.
1461
*
1462
* Function to reset the global handler for XSLT sorting.
1463
* If the handler is NULL, the default sort function will be used.
1464
*/
1465
void
1466
xsltSetSortFunc(xsltSortFunc handler) {
1467
if (handler != NULL)
1468
xsltSortFunction = handler;
1469
else
1470
xsltSortFunction = xsltDefaultSortFunction;
1471
}
1472
1473
/**
1474
* xsltSetCtxtSortFunc:
1475
* @ctxt: a XSLT process context
1476
* @handler: the new handler function
1477
*
1478
* DEPRECATED: Use xsltSetCtxtLocaleHandlers.
1479
*
1480
* Function to set the handler for XSLT sorting
1481
* for the specified context.
1482
* If the handler is NULL, then the global
1483
* sort function will be called
1484
*/
1485
void
1486
xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt, xsltSortFunc handler) {
1487
ctxt->sortfunc = handler;
1488
}
1489
1490
/**
1491
* xsltSetCtxtLocaleHandlers:
1492
* @ctxt: an XSLT transform context
1493
* @newLocale: locale constructor
1494
* @freeLocale: locale destructor
1495
* @genSortKey: sort key generator
1496
*
1497
* Set the locale handlers.
1498
*/
1499
void
1500
xsltSetCtxtLocaleHandlers(xsltTransformContextPtr ctxt,
1501
xsltNewLocaleFunc newLocale,
1502
xsltFreeLocaleFunc freeLocale,
1503
xsltGenSortKeyFunc genSortKey) {
1504
if (ctxt == NULL)
1505
return;
1506
1507
ctxt->newLocale = newLocale;
1508
ctxt->freeLocale = freeLocale;
1509
ctxt->genSortKey = genSortKey;
1510
}
1511
1512
/************************************************************************
1513
* *
1514
* Parsing options *
1515
* *
1516
************************************************************************/
1517
1518
/**
1519
* xsltSetCtxtParseOptions:
1520
* @ctxt: a XSLT process context
1521
* @options: a combination of libxml2 xmlParserOption
1522
*
1523
* Change the default parser option passed by the XSLT engine to the
1524
* parser when using document() loading.
1525
*
1526
* Returns the previous options or -1 in case of error
1527
*/
1528
int
1529
xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt, int options)
1530
{
1531
int oldopts;
1532
1533
if (ctxt == NULL)
1534
return(-1);
1535
oldopts = ctxt->parserOptions;
1536
if (ctxt->xinclude)
1537
oldopts |= XML_PARSE_XINCLUDE;
1538
ctxt->parserOptions = options;
1539
if (options & XML_PARSE_XINCLUDE)
1540
ctxt->xinclude = 1;
1541
else
1542
ctxt->xinclude = 0;
1543
return(oldopts);
1544
}
1545
1546
/************************************************************************
1547
* *
1548
* Output *
1549
* *
1550
************************************************************************/
1551
1552
/**
1553
* xsltSaveResultTo:
1554
* @buf: an output buffer
1555
* @result: the result xmlDocPtr
1556
* @style: the stylesheet
1557
*
1558
* Save the result @result obtained by applying the @style stylesheet
1559
* to an I/O output channel @buf
1560
*
1561
* Returns the number of byte written or -1 in case of failure.
1562
*/
1563
int
1564
xsltSaveResultTo(xmlOutputBufferPtr buf, xmlDocPtr result,
1565
xsltStylesheetPtr style) {
1566
const xmlChar *encoding;
1567
int base;
1568
const xmlChar *method;
1569
int indent;
1570
1571
if ((buf == NULL) || (result == NULL) || (style == NULL))
1572
return(-1);
1573
if ((result->children == NULL) ||
1574
((result->children->type == XML_DTD_NODE) &&
1575
(result->children->next == NULL)))
1576
return(0);
1577
1578
if ((style->methodURI != NULL) &&
1579
((style->method == NULL) ||
1580
(!xmlStrEqual(style->method, (const xmlChar *) "xhtml")))) {
1581
xsltGenericError(xsltGenericErrorContext,
1582
"xsltSaveResultTo : unknown output method\n");
1583
return(-1);
1584
}
1585
1586
base = buf->written;
1587
1588
XSLT_GET_IMPORT_PTR(method, style, method)
1589
XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1590
XSLT_GET_IMPORT_INT(indent, style, indent);
1591
1592
if ((method == NULL) && (result->type == XML_HTML_DOCUMENT_NODE))
1593
method = (const xmlChar *) "html";
1594
1595
if ((method != NULL) &&
1596
(xmlStrEqual(method, (const xmlChar *) "html"))) {
1597
if (encoding != NULL) {
1598
htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1599
} else {
1600
htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
1601
}
1602
if (indent == -1)
1603
indent = 1;
1604
htmlDocContentDumpFormatOutput(buf, result, (const char *) encoding,
1605
indent);
1606
xmlOutputBufferFlush(buf);
1607
} else if ((method != NULL) &&
1608
(xmlStrEqual(method, (const xmlChar *) "xhtml"))) {
1609
if (encoding != NULL) {
1610
htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1611
} else {
1612
htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
1613
}
1614
htmlDocContentDumpOutput(buf, result, (const char *) encoding);
1615
xmlOutputBufferFlush(buf);
1616
} else if ((method != NULL) &&
1617
(xmlStrEqual(method, (const xmlChar *) "text"))) {
1618
xmlNodePtr cur;
1619
1620
cur = result->children;
1621
while (cur != NULL) {
1622
if (cur->type == XML_TEXT_NODE)
1623
xmlOutputBufferWriteString(buf, (const char *) cur->content);
1624
1625
/*
1626
* Skip to next node
1627
*/
1628
if (cur->children != NULL) {
1629
if ((cur->children->type != XML_ENTITY_DECL) &&
1630
(cur->children->type != XML_ENTITY_REF_NODE) &&
1631
(cur->children->type != XML_ENTITY_NODE)) {
1632
cur = cur->children;
1633
continue;
1634
}
1635
}
1636
if (cur->next != NULL) {
1637
cur = cur->next;
1638
continue;
1639
}
1640
1641
do {
1642
cur = cur->parent;
1643
if (cur == NULL)
1644
break;
1645
if (cur == (xmlNodePtr) style->doc) {
1646
cur = NULL;
1647
break;
1648
}
1649
if (cur->next != NULL) {
1650
cur = cur->next;
1651
break;
1652
}
1653
} while (cur != NULL);
1654
}
1655
xmlOutputBufferFlush(buf);
1656
} else {
1657
int omitXmlDecl;
1658
int standalone;
1659
1660
XSLT_GET_IMPORT_INT(omitXmlDecl, style, omitXmlDeclaration);
1661
XSLT_GET_IMPORT_INT(standalone, style, standalone);
1662
1663
if (omitXmlDecl != 1) {
1664
xmlOutputBufferWriteString(buf, "<?xml version=");
1665
if (result->version != NULL) {
1666
xmlOutputBufferWriteString(buf, "\"");
1667
xmlOutputBufferWriteString(buf, (const char *)result->version);
1668
xmlOutputBufferWriteString(buf, "\"");
1669
} else
1670
xmlOutputBufferWriteString(buf, "\"1.0\"");
1671
if (encoding == NULL) {
1672
if (result->encoding != NULL)
1673
encoding = result->encoding;
1674
else if (result->charset != XML_CHAR_ENCODING_UTF8)
1675
encoding = (const xmlChar *)
1676
xmlGetCharEncodingName((xmlCharEncoding)
1677
result->charset);
1678
}
1679
if (encoding != NULL) {
1680
xmlOutputBufferWriteString(buf, " encoding=");
1681
xmlOutputBufferWriteString(buf, "\"");
1682
xmlOutputBufferWriteString(buf, (const char *) encoding);
1683
xmlOutputBufferWriteString(buf, "\"");
1684
}
1685
switch (standalone) {
1686
case 0:
1687
xmlOutputBufferWriteString(buf, " standalone=\"no\"");
1688
break;
1689
case 1:
1690
xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
1691
break;
1692
default:
1693
break;
1694
}
1695
xmlOutputBufferWriteString(buf, "?>\n");
1696
}
1697
if (result->children != NULL) {
1698
xmlNodePtr children = result->children;
1699
xmlNodePtr child = children;
1700
1701
/*
1702
* Hack to avoid quadratic behavior when scanning
1703
* result->children in xmlGetIntSubset called by
1704
* xmlNodeDumpOutput.
1705
*/
1706
result->children = NULL;
1707
1708
while (child != NULL) {
1709
xmlNodeDumpOutput(buf, result, child, 0, (indent == 1),
1710
(const char *) encoding);
1711
if (indent && ((child->type == XML_DTD_NODE) ||
1712
((child->type == XML_COMMENT_NODE) &&
1713
(child->next != NULL))))
1714
xmlOutputBufferWriteString(buf, "\n");
1715
child = child->next;
1716
}
1717
if (indent)
1718
xmlOutputBufferWriteString(buf, "\n");
1719
1720
result->children = children;
1721
}
1722
xmlOutputBufferFlush(buf);
1723
}
1724
return(buf->written - base);
1725
}
1726
1727
/**
1728
* xsltSaveResultToFilename:
1729
* @URL: a filename or URL
1730
* @result: the result xmlDocPtr
1731
* @style: the stylesheet
1732
* @compression: the compression factor (0 - 9 included)
1733
*
1734
* Save the result @result obtained by applying the @style stylesheet
1735
* to a file or @URL
1736
*
1737
* Returns the number of byte written or -1 in case of failure.
1738
*/
1739
int
1740
xsltSaveResultToFilename(const char *URL, xmlDocPtr result,
1741
xsltStylesheetPtr style, int compression) {
1742
xmlOutputBufferPtr buf;
1743
const xmlChar *encoding;
1744
int ret;
1745
1746
if ((URL == NULL) || (result == NULL) || (style == NULL))
1747
return(-1);
1748
if (result->children == NULL)
1749
return(0);
1750
1751
XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1752
if (encoding != NULL) {
1753
xmlCharEncodingHandlerPtr encoder = NULL;
1754
1755
/* Don't use UTF-8 dummy encoder */
1756
if ((xmlStrcasecmp(encoding, BAD_CAST "UTF-8") != 0) &&
1757
(xmlStrcasecmp(encoding, BAD_CAST "UTF8") != 0))
1758
encoder = xmlFindCharEncodingHandler((char *) encoding);
1759
buf = xmlOutputBufferCreateFilename(URL, encoder, compression);
1760
} else {
1761
buf = xmlOutputBufferCreateFilename(URL, NULL, compression);
1762
}
1763
if (buf == NULL)
1764
return(-1);
1765
xsltSaveResultTo(buf, result, style);
1766
ret = xmlOutputBufferClose(buf);
1767
return(ret);
1768
}
1769
1770
/**
1771
* xsltSaveResultToFile:
1772
* @file: a FILE * I/O
1773
* @result: the result xmlDocPtr
1774
* @style: the stylesheet
1775
*
1776
* Save the result @result obtained by applying the @style stylesheet
1777
* to an open FILE * I/O.
1778
* This does not close the FILE @file
1779
*
1780
* Returns the number of bytes written or -1 in case of failure.
1781
*/
1782
int
1783
xsltSaveResultToFile(FILE *file, xmlDocPtr result, xsltStylesheetPtr style) {
1784
xmlOutputBufferPtr buf;
1785
const xmlChar *encoding;
1786
int ret;
1787
1788
if ((file == NULL) || (result == NULL) || (style == NULL))
1789
return(-1);
1790
if (result->children == NULL)
1791
return(0);
1792
1793
XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1794
if (encoding != NULL) {
1795
xmlCharEncodingHandlerPtr encoder = NULL;
1796
1797
/* Don't use UTF-8 dummy encoder */
1798
if ((xmlStrcasecmp(encoding, BAD_CAST "UTF-8") != 0) &&
1799
(xmlStrcasecmp(encoding, BAD_CAST "UTF8") != 0))
1800
encoder = xmlFindCharEncodingHandler((char *) encoding);
1801
buf = xmlOutputBufferCreateFile(file, encoder);
1802
} else {
1803
buf = xmlOutputBufferCreateFile(file, NULL);
1804
}
1805
1806
if (buf == NULL)
1807
return(-1);
1808
xsltSaveResultTo(buf, result, style);
1809
ret = xmlOutputBufferClose(buf);
1810
return(ret);
1811
}
1812
1813
/**
1814
* xsltSaveResultToFd:
1815
* @fd: a file descriptor
1816
* @result: the result xmlDocPtr
1817
* @style: the stylesheet
1818
*
1819
* Save the result @result obtained by applying the @style stylesheet
1820
* to an open file descriptor
1821
* This does not close the descriptor.
1822
*
1823
* Returns the number of bytes written or -1 in case of failure.
1824
*/
1825
int
1826
xsltSaveResultToFd(int fd, xmlDocPtr result, xsltStylesheetPtr style) {
1827
xmlOutputBufferPtr buf;
1828
const xmlChar *encoding;
1829
int ret;
1830
1831
if ((fd < 0) || (result == NULL) || (style == NULL))
1832
return(-1);
1833
if (result->children == NULL)
1834
return(0);
1835
1836
XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1837
if (encoding != NULL) {
1838
xmlCharEncodingHandlerPtr encoder = NULL;
1839
1840
/* Don't use UTF-8 dummy encoder */
1841
if ((xmlStrcasecmp(encoding, BAD_CAST "UTF-8") != 0) &&
1842
(xmlStrcasecmp(encoding, BAD_CAST "UTF8") != 0))
1843
encoder = xmlFindCharEncodingHandler((char *) encoding);
1844
buf = xmlOutputBufferCreateFd(fd, encoder);
1845
} else {
1846
buf = xmlOutputBufferCreateFd(fd, NULL);
1847
}
1848
if (buf == NULL)
1849
return(-1);
1850
xsltSaveResultTo(buf, result, style);
1851
ret = xmlOutputBufferClose(buf);
1852
return(ret);
1853
}
1854
1855
/**
1856
* xsltSaveResultToString:
1857
* @doc_txt_ptr: Memory pointer for allocated XML text
1858
* @doc_txt_len: Length of the generated XML text
1859
* @result: the result xmlDocPtr
1860
* @style: the stylesheet
1861
*
1862
* Save the result @result obtained by applying the @style stylesheet
1863
* to a new allocated string.
1864
*
1865
* Returns 0 in case of success and -1 in case of error
1866
*/
1867
int
1868
xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len,
1869
xmlDocPtr result, xsltStylesheetPtr style) {
1870
xmlOutputBufferPtr buf;
1871
const xmlChar *encoding;
1872
1873
*doc_txt_ptr = NULL;
1874
*doc_txt_len = 0;
1875
if (result->children == NULL)
1876
return(0);
1877
1878
XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1879
if (encoding != NULL) {
1880
xmlCharEncodingHandlerPtr encoder = NULL;
1881
1882
/* Don't use UTF-8 dummy encoder */
1883
if ((xmlStrcasecmp(encoding, BAD_CAST "UTF-8") != 0) &&
1884
(xmlStrcasecmp(encoding, BAD_CAST "UTF8") != 0))
1885
encoder = xmlFindCharEncodingHandler((char *) encoding);
1886
buf = xmlAllocOutputBuffer(encoder);
1887
} else {
1888
buf = xmlAllocOutputBuffer(NULL);
1889
}
1890
if (buf == NULL)
1891
return(-1);
1892
xsltSaveResultTo(buf, result, style);
1893
#ifdef LIBXML2_NEW_BUFFER
1894
if (buf->conv != NULL) {
1895
*doc_txt_len = xmlBufUse(buf->conv);
1896
*doc_txt_ptr = xmlStrndup(xmlBufContent(buf->conv), *doc_txt_len);
1897
} else {
1898
*doc_txt_len = xmlBufUse(buf->buffer);
1899
*doc_txt_ptr = xmlStrndup(xmlBufContent(buf->buffer), *doc_txt_len);
1900
}
1901
#else
1902
if (buf->conv != NULL) {
1903
*doc_txt_len = buf->conv->use;
1904
*doc_txt_ptr = xmlStrndup(buf->conv->content, *doc_txt_len);
1905
} else {
1906
*doc_txt_len = buf->buffer->use;
1907
*doc_txt_ptr = xmlStrndup(buf->buffer->content, *doc_txt_len);
1908
}
1909
#endif
1910
(void)xmlOutputBufferClose(buf);
1911
return 0;
1912
}
1913
1914
/**
1915
* xsltGetSourceNodeFlags:
1916
* @node: Node from source document
1917
*
1918
* Returns the flags for a source node.
1919
*/
1920
int
1921
xsltGetSourceNodeFlags(xmlNodePtr node) {
1922
/*
1923
* Squeeze the bit flags into the upper bits of
1924
*
1925
* - 'int properties' member in struct _xmlDoc
1926
* - 'xmlAttributeType atype' member in struct _xmlAttr
1927
* - 'unsigned short extra' member in struct _xmlNode
1928
*/
1929
switch (node->type) {
1930
case XML_DOCUMENT_NODE:
1931
case XML_HTML_DOCUMENT_NODE:
1932
return ((xmlDocPtr) node)->properties >> 27;
1933
1934
case XML_ATTRIBUTE_NODE:
1935
return ((xmlAttrPtr) node)->atype >> 27;
1936
1937
case XML_ELEMENT_NODE:
1938
case XML_TEXT_NODE:
1939
case XML_CDATA_SECTION_NODE:
1940
case XML_PI_NODE:
1941
case XML_COMMENT_NODE:
1942
return node->extra >> 12;
1943
1944
default:
1945
return 0;
1946
}
1947
}
1948
1949
/**
1950
* xsltSetSourceNodeFlags:
1951
* @node: Node from source document
1952
* @flags: Flags
1953
*
1954
* Sets the specified flags to 1.
1955
*
1956
* Returns 0 on success, -1 on error.
1957
*/
1958
int
1959
xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
1960
int flags) {
1961
if (node->doc == ctxt->initialContextDoc)
1962
ctxt->sourceDocDirty = 1;
1963
1964
switch (node->type) {
1965
case XML_DOCUMENT_NODE:
1966
case XML_HTML_DOCUMENT_NODE:
1967
((xmlDocPtr) node)->properties |= flags << 27;
1968
return 0;
1969
1970
case XML_ATTRIBUTE_NODE:
1971
((xmlAttrPtr) node)->atype |= flags << 27;
1972
return 0;
1973
1974
case XML_ELEMENT_NODE:
1975
case XML_TEXT_NODE:
1976
case XML_CDATA_SECTION_NODE:
1977
case XML_PI_NODE:
1978
case XML_COMMENT_NODE:
1979
node->extra |= flags << 12;
1980
return 0;
1981
1982
default:
1983
return -1;
1984
}
1985
}
1986
1987
/**
1988
* xsltClearSourceNodeFlags:
1989
* @node: Node from source document
1990
* @flags: Flags
1991
*
1992
* Sets the specified flags to 0.
1993
*
1994
* Returns 0 on success, -1 on error.
1995
*/
1996
int
1997
xsltClearSourceNodeFlags(xmlNodePtr node, int flags) {
1998
switch (node->type) {
1999
case XML_DOCUMENT_NODE:
2000
case XML_HTML_DOCUMENT_NODE:
2001
((xmlDocPtr) node)->properties &= ~(flags << 27);
2002
return 0;
2003
2004
case XML_ATTRIBUTE_NODE:
2005
((xmlAttrPtr) node)->atype &= ~(flags << 27);
2006
return 0;
2007
2008
case XML_ELEMENT_NODE:
2009
case XML_TEXT_NODE:
2010
case XML_CDATA_SECTION_NODE:
2011
case XML_PI_NODE:
2012
case XML_COMMENT_NODE:
2013
node->extra &= ~(flags << 12);
2014
return 0;
2015
2016
default:
2017
return -1;
2018
}
2019
}
2020
2021
/**
2022
* xsltGetPSVIPtr:
2023
* @cur: Node
2024
*
2025
* Returns a pointer to the psvi member of a node or NULL on error.
2026
*/
2027
void **
2028
xsltGetPSVIPtr(xmlNodePtr cur) {
2029
switch (cur->type) {
2030
case XML_DOCUMENT_NODE:
2031
case XML_HTML_DOCUMENT_NODE:
2032
return &((xmlDocPtr) cur)->psvi;
2033
2034
case XML_ATTRIBUTE_NODE:
2035
return &((xmlAttrPtr) cur)->psvi;
2036
2037
case XML_ELEMENT_NODE:
2038
case XML_TEXT_NODE:
2039
case XML_CDATA_SECTION_NODE:
2040
case XML_PI_NODE:
2041
case XML_COMMENT_NODE:
2042
return &cur->psvi;
2043
2044
default:
2045
return NULL;
2046
}
2047
}
2048
2049
#ifdef WITH_PROFILER
2050
2051
/************************************************************************
2052
* *
2053
* Generating profiling information *
2054
* *
2055
************************************************************************/
2056
2057
static long calibration = -1;
2058
2059
/**
2060
* xsltCalibrateTimestamps:
2061
*
2062
* Used for to calibrate the xsltTimestamp() function
2063
* Should work if launched at startup and we don't loose our quantum :-)
2064
*
2065
* Returns the number of milliseconds used by xsltTimestamp()
2066
*/
2067
#if !defined(XSLT_WIN32_PERFORMANCE_COUNTER) && \
2068
(defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY))
2069
static long
2070
xsltCalibrateTimestamps(void) {
2071
register int i;
2072
2073
for (i = 0;i < 999;i++)
2074
xsltTimestamp();
2075
return(xsltTimestamp() / 1000);
2076
}
2077
#endif
2078
2079
/**
2080
* xsltCalibrateAdjust:
2081
* @delta: a negative dealy value found
2082
*
2083
* Used for to correct the calibration for xsltTimestamp()
2084
*/
2085
void
2086
xsltCalibrateAdjust(long delta) {
2087
calibration += delta;
2088
}
2089
2090
/**
2091
* xsltTimestamp:
2092
*
2093
* Used for gathering profiling data
2094
*
2095
* Returns the number of tenth of milliseconds since the beginning of the
2096
* profiling
2097
*/
2098
long
2099
xsltTimestamp(void)
2100
{
2101
#ifdef XSLT_WIN32_PERFORMANCE_COUNTER
2102
BOOL ok;
2103
LARGE_INTEGER performanceCount;
2104
LARGE_INTEGER performanceFrequency;
2105
LONGLONG quadCount;
2106
double seconds;
2107
static LONGLONG startupQuadCount = 0;
2108
static LONGLONG startupQuadFreq = 0;
2109
2110
ok = QueryPerformanceCounter(&performanceCount);
2111
if (!ok)
2112
return 0;
2113
quadCount = performanceCount.QuadPart;
2114
if (calibration < 0) {
2115
calibration = 0;
2116
ok = QueryPerformanceFrequency(&performanceFrequency);
2117
if (!ok)
2118
return 0;
2119
startupQuadFreq = performanceFrequency.QuadPart;
2120
startupQuadCount = quadCount;
2121
return (0);
2122
}
2123
if (startupQuadFreq == 0)
2124
return 0;
2125
seconds = (quadCount - startupQuadCount) / (double) startupQuadFreq;
2126
return (long) (seconds * XSLT_TIMESTAMP_TICS_PER_SEC);
2127
2128
#else /* XSLT_WIN32_PERFORMANCE_COUNTER */
2129
#ifdef HAVE_CLOCK_GETTIME
2130
# if defined(CLOCK_MONOTONIC)
2131
# define XSLT_CLOCK CLOCK_MONOTONIC
2132
# elif defined(CLOCK_HIGHRES)
2133
# define XSLT_CLOCK CLOCK_HIGHRES
2134
# else
2135
# define XSLT_CLOCK CLOCK_REALTIME
2136
# endif
2137
static struct timespec startup;
2138
struct timespec cur;
2139
long tics;
2140
2141
if (calibration < 0) {
2142
clock_gettime(XSLT_CLOCK, &startup);
2143
calibration = 0;
2144
calibration = xsltCalibrateTimestamps();
2145
clock_gettime(XSLT_CLOCK, &startup);
2146
return (0);
2147
}
2148
2149
clock_gettime(XSLT_CLOCK, &cur);
2150
tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
2151
tics += (cur.tv_nsec - startup.tv_nsec) /
2152
(1000000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
2153
2154
tics -= calibration;
2155
return(tics);
2156
2157
#elif HAVE_GETTIMEOFDAY
2158
static struct timeval startup;
2159
struct timeval cur;
2160
long tics;
2161
2162
if (calibration < 0) {
2163
gettimeofday(&startup, NULL);
2164
calibration = 0;
2165
calibration = xsltCalibrateTimestamps();
2166
gettimeofday(&startup, NULL);
2167
return (0);
2168
}
2169
2170
gettimeofday(&cur, NULL);
2171
tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
2172
tics += (cur.tv_usec - startup.tv_usec) /
2173
(1000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
2174
2175
tics -= calibration;
2176
return(tics);
2177
#else
2178
2179
/* Neither gettimeofday() nor Win32 performance counter available */
2180
2181
return (0);
2182
2183
#endif /* HAVE_GETTIMEOFDAY */
2184
#endif /* XSLT_WIN32_PERFORMANCE_COUNTER */
2185
}
2186
2187
static char *
2188
pretty_templ_match(xsltTemplatePtr templ) {
2189
static char dst[1001];
2190
char *src = (char *)templ->match;
2191
int i=0,j;
2192
2193
/* strip white spaces */
2194
for (j=0; i<1000 && src[j]; i++,j++) {
2195
for(;src[j]==' ';j++);
2196
dst[i]=src[j];
2197
}
2198
if(i<998 && templ->mode) {
2199
/* append [mode] */
2200
dst[i++]='[';
2201
src=(char *)templ->mode;
2202
for (j=0; i<999 && src[j]; i++,j++) {
2203
dst[i]=src[j];
2204
}
2205
dst[i++]=']';
2206
}
2207
dst[i]='\0';
2208
return dst;
2209
}
2210
2211
#define MAX_TEMPLATES 10000
2212
2213
/**
2214
* xsltSaveProfiling:
2215
* @ctxt: an XSLT context
2216
* @output: a FILE * for saving the information
2217
*
2218
* Save the profiling information on @output
2219
*/
2220
void
2221
xsltSaveProfiling(xsltTransformContextPtr ctxt, FILE *output) {
2222
int nb, i,j,k,l;
2223
int max;
2224
int total;
2225
unsigned long totalt;
2226
xsltTemplatePtr *templates;
2227
xsltStylesheetPtr style;
2228
xsltTemplatePtr templ1,templ2;
2229
int *childt;
2230
2231
if ((output == NULL) || (ctxt == NULL))
2232
return;
2233
if (ctxt->profile == 0)
2234
return;
2235
2236
nb = 0;
2237
max = MAX_TEMPLATES;
2238
templates = xmlMalloc(max * sizeof(xsltTemplatePtr));
2239
if (templates == NULL)
2240
return;
2241
2242
style = ctxt->style;
2243
while (style != NULL) {
2244
templ1 = style->templates;
2245
while (templ1 != NULL) {
2246
if (nb >= max)
2247
break;
2248
2249
if (templ1->nbCalls > 0)
2250
templates[nb++] = templ1;
2251
templ1 = templ1->next;
2252
}
2253
2254
style = xsltNextImport(style);
2255
}
2256
2257
for (i = 0;i < nb -1;i++) {
2258
for (j = i + 1; j < nb; j++) {
2259
if ((templates[i]->time <= templates[j]->time) ||
2260
((templates[i]->time == templates[j]->time) &&
2261
(templates[i]->nbCalls <= templates[j]->nbCalls))) {
2262
templ1 = templates[j];
2263
templates[j] = templates[i];
2264
templates[i] = templ1;
2265
}
2266
}
2267
}
2268
2269
2270
/* print flat profile */
2271
2272
fprintf(output, "%6s%20s%20s%10s Calls Tot 100us Avg\n\n",
2273
"number", "match", "name", "mode");
2274
total = 0;
2275
totalt = 0;
2276
for (i = 0;i < nb;i++) {
2277
templ1 = templates[i];
2278
fprintf(output, "%5d ", i);
2279
if (templ1->match != NULL) {
2280
if (xmlStrlen(templ1->match) > 20)
2281
fprintf(output, "%s\n%26s", templ1->match, "");
2282
else
2283
fprintf(output, "%20s", templ1->match);
2284
} else {
2285
fprintf(output, "%20s", "");
2286
}
2287
if (templ1->name != NULL) {
2288
if (xmlStrlen(templ1->name) > 20)
2289
fprintf(output, "%s\n%46s", templ1->name, "");
2290
else
2291
fprintf(output, "%20s", templ1->name);
2292
} else {
2293
fprintf(output, "%20s", "");
2294
}
2295
if (templ1->mode != NULL) {
2296
if (xmlStrlen(templ1->mode) > 10)
2297
fprintf(output, "%s\n%56s", templ1->mode, "");
2298
else
2299
fprintf(output, "%10s", templ1->mode);
2300
} else {
2301
fprintf(output, "%10s", "");
2302
}
2303
fprintf(output, " %6d", templ1->nbCalls);
2304
fprintf(output, " %6ld %6ld\n", templ1->time,
2305
templ1->time / templ1->nbCalls);
2306
total += templ1->nbCalls;
2307
totalt += templ1->time;
2308
}
2309
fprintf(output, "\n%30s%26s %6d %6ld\n", "Total", "", total, totalt);
2310
2311
2312
/* print call graph */
2313
2314
childt = xmlMalloc((nb + 1) * sizeof(int));
2315
if (childt == NULL)
2316
return;
2317
2318
/* precalculate children times */
2319
for (i = 0; i < nb; i++) {
2320
templ1 = templates[i];
2321
2322
childt[i] = 0;
2323
for (k = 0; k < nb; k++) {
2324
templ2 = templates[k];
2325
for (l = 0; l < templ2->templNr; l++) {
2326
if (templ2->templCalledTab[l] == templ1) {
2327
childt[i] +=templ2->time;
2328
}
2329
}
2330
}
2331
}
2332
childt[i] = 0;
2333
2334
fprintf(output, "\nindex %% time self children called name\n");
2335
2336
for (i = 0; i < nb; i++) {
2337
char ix_str[20], timep_str[20], times_str[20], timec_str[20], called_str[20];
2338
unsigned long t;
2339
2340
templ1 = templates[i];
2341
/* callers */
2342
for (j = 0; j < templ1->templNr; j++) {
2343
templ2 = templ1->templCalledTab[j];
2344
for (k = 0; k < nb; k++) {
2345
if (templates[k] == templ2)
2346
break;
2347
}
2348
t=templ2?templ2->time:totalt;
2349
snprintf(times_str,sizeof(times_str),"%8.3f",(float)t/XSLT_TIMESTAMP_TICS_PER_SEC);
2350
snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC);
2351
snprintf(called_str,sizeof(called_str),"%6d/%d",
2352
templ1->templCountTab[j], /* number of times caller calls 'this' */
2353
templ1->nbCalls); /* total number of calls to 'this' */
2354
2355
fprintf(output, " %-8s %-8s %-12s %s [%d]\n",
2356
times_str,timec_str,called_str,
2357
(templ2?(templ2->name?(char *)templ2->name:pretty_templ_match(templ2)):"-"),k);
2358
}
2359
/* this */
2360
snprintf(ix_str,sizeof(ix_str),"[%d]",i);
2361
snprintf(timep_str,sizeof(timep_str),"%6.2f",(float)templ1->time*100.0/totalt);
2362
snprintf(times_str,sizeof(times_str),"%8.3f",(float)templ1->time/XSLT_TIMESTAMP_TICS_PER_SEC);
2363
snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[i]/XSLT_TIMESTAMP_TICS_PER_SEC);
2364
fprintf(output, "%-5s %-6s %-8s %-8s %6d %s [%d]\n",
2365
ix_str, timep_str,times_str,timec_str,
2366
templ1->nbCalls,
2367
templ1->name?(char *)templ1->name:pretty_templ_match(templ1),i);
2368
/* callees
2369
* - go over templates[0..nb] and their templCalledTab[]
2370
* - print those where we in the the call-stack
2371
*/
2372
total = 0;
2373
for (k = 0; k < nb; k++) {
2374
templ2 = templates[k];
2375
for (l = 0; l < templ2->templNr; l++) {
2376
if (templ2->templCalledTab[l] == templ1) {
2377
total+=templ2->templCountTab[l];
2378
}
2379
}
2380
}
2381
for (k = 0; k < nb; k++) {
2382
templ2 = templates[k];
2383
for (l = 0; l < templ2->templNr; l++) {
2384
if (templ2->templCalledTab[l] == templ1) {
2385
snprintf(times_str,sizeof(times_str),"%8.3f",(float)templ2->time/XSLT_TIMESTAMP_TICS_PER_SEC);
2386
snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC);
2387
snprintf(called_str,sizeof(called_str),"%6d/%d",
2388
templ2->templCountTab[l], /* number of times 'this' calls callee */
2389
total); /* total number of calls from 'this' */
2390
fprintf(output, " %-8s %-8s %-12s %s [%d]\n",
2391
times_str,timec_str,called_str,
2392
templ2->name?(char *)templ2->name:pretty_templ_match(templ2),k);
2393
}
2394
}
2395
}
2396
fprintf(output, "-----------------------------------------------\n");
2397
}
2398
2399
fprintf(output, "\f\nIndex by function name\n");
2400
for (i = 0; i < nb; i++) {
2401
templ1 = templates[i];
2402
fprintf(output, "[%d] %s (%s:%d)\n",
2403
i, templ1->name?(char *)templ1->name:pretty_templ_match(templ1),
2404
templ1->style->doc->URL,templ1->elem->line);
2405
}
2406
2407
fprintf(output, "\f\n");
2408
xmlFree(childt);
2409
2410
xmlFree(templates);
2411
}
2412
2413
/************************************************************************
2414
* *
2415
* Fetching profiling information *
2416
* *
2417
************************************************************************/
2418
2419
/**
2420
* xsltGetProfileInformation:
2421
* @ctxt: a transformation context
2422
*
2423
* This function should be called after the transformation completed
2424
* to extract template processing profiling information if available.
2425
* The information is returned as an XML document tree like
2426
* <?xml version="1.0"?>
2427
* <profile>
2428
* <template rank="1" match="*" name=""
2429
* mode="" calls="6" time="48" average="8"/>
2430
* <template rank="2" match="item2|item3" name=""
2431
* mode="" calls="10" time="30" average="3"/>
2432
* <template rank="3" match="item1" name=""
2433
* mode="" calls="5" time="17" average="3"/>
2434
* </profile>
2435
* The caller will need to free up the returned tree with xmlFreeDoc()
2436
*
2437
* Returns the xmlDocPtr corresponding to the result or NULL if not available.
2438
*/
2439
2440
xmlDocPtr
2441
xsltGetProfileInformation(xsltTransformContextPtr ctxt)
2442
{
2443
xmlDocPtr ret = NULL;
2444
xmlNodePtr root, child;
2445
char buf[100];
2446
2447
xsltStylesheetPtr style;
2448
xsltTemplatePtr *templates;
2449
xsltTemplatePtr templ;
2450
int nb = 0, max = 0, i, j;
2451
2452
if (!ctxt)
2453
return NULL;
2454
2455
if (!ctxt->profile)
2456
return NULL;
2457
2458
nb = 0;
2459
max = 10000;
2460
templates =
2461
(xsltTemplatePtr *) xmlMalloc(max * sizeof(xsltTemplatePtr));
2462
if (templates == NULL)
2463
return NULL;
2464
2465
/*
2466
* collect all the templates in an array
2467
*/
2468
style = ctxt->style;
2469
while (style != NULL) {
2470
templ = style->templates;
2471
while (templ != NULL) {
2472
if (nb >= max)
2473
break;
2474
2475
if (templ->nbCalls > 0)
2476
templates[nb++] = templ;
2477
templ = templ->next;
2478
}
2479
2480
style = (xsltStylesheetPtr) xsltNextImport(style);
2481
}
2482
2483
/*
2484
* Sort the array by time spent
2485
*/
2486
for (i = 0; i < nb - 1; i++) {
2487
for (j = i + 1; j < nb; j++) {
2488
if ((templates[i]->time <= templates[j]->time) ||
2489
((templates[i]->time == templates[j]->time) &&
2490
(templates[i]->nbCalls <= templates[j]->nbCalls))) {
2491
templ = templates[j];
2492
templates[j] = templates[i];
2493
templates[i] = templ;
2494
}
2495
}
2496
}
2497
2498
/*
2499
* Generate a document corresponding to the results.
2500
*/
2501
ret = xmlNewDoc(BAD_CAST "1.0");
2502
root = xmlNewDocNode(ret, NULL, BAD_CAST "profile", NULL);
2503
xmlDocSetRootElement(ret, root);
2504
2505
for (i = 0; i < nb; i++) {
2506
child = xmlNewChild(root, NULL, BAD_CAST "template", NULL);
2507
snprintf(buf, sizeof(buf), "%d", i + 1);
2508
xmlSetProp(child, BAD_CAST "rank", BAD_CAST buf);
2509
xmlSetProp(child, BAD_CAST "match", BAD_CAST templates[i]->match);
2510
xmlSetProp(child, BAD_CAST "name", BAD_CAST templates[i]->name);
2511
xmlSetProp(child, BAD_CAST "mode", BAD_CAST templates[i]->mode);
2512
2513
snprintf(buf, sizeof(buf), "%d", templates[i]->nbCalls);
2514
xmlSetProp(child, BAD_CAST "calls", BAD_CAST buf);
2515
2516
snprintf(buf, sizeof(buf), "%ld", templates[i]->time);
2517
xmlSetProp(child, BAD_CAST "time", BAD_CAST buf);
2518
2519
snprintf(buf, sizeof(buf), "%ld", templates[i]->time / templates[i]->nbCalls);
2520
xmlSetProp(child, BAD_CAST "average", BAD_CAST buf);
2521
};
2522
2523
xmlFree(templates);
2524
2525
return ret;
2526
}
2527
2528
#endif /* WITH_PROFILER */
2529
2530
/************************************************************************
2531
* *
2532
* Hooks for libxml2 XPath *
2533
* *
2534
************************************************************************/
2535
2536
/**
2537
* xsltXPathCompileFlags:
2538
* @style: the stylesheet
2539
* @str: the XPath expression
2540
* @flags: extra compilation flags to pass down to libxml2 XPath
2541
*
2542
* Compile an XPath expression
2543
*
2544
* Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2545
* the caller has to free the object.
2546
*/
2547
xmlXPathCompExprPtr
2548
xsltXPathCompileFlags(xsltStylesheetPtr style, const xmlChar *str, int flags) {
2549
xmlXPathContextPtr xpathCtxt;
2550
xmlXPathCompExprPtr ret;
2551
2552
if (style != NULL) {
2553
xpathCtxt = style->principal->xpathCtxt;
2554
if (xpathCtxt == NULL)
2555
return NULL;
2556
xpathCtxt->dict = style->dict;
2557
} else {
2558
xpathCtxt = xmlXPathNewContext(NULL);
2559
if (xpathCtxt == NULL)
2560
return NULL;
2561
}
2562
xpathCtxt->flags = flags;
2563
2564
/*
2565
* Compile the expression.
2566
*/
2567
ret = xmlXPathCtxtCompile(xpathCtxt, str);
2568
2569
if (style == NULL) {
2570
xmlXPathFreeContext(xpathCtxt);
2571
}
2572
/*
2573
* TODO: there is a lot of optimizations which should be possible
2574
* like variable slot precomputations, function precomputations, etc.
2575
*/
2576
2577
return(ret);
2578
}
2579
2580
/**
2581
* xsltXPathCompile:
2582
* @style: the stylesheet
2583
* @str: the XPath expression
2584
*
2585
* Compile an XPath expression
2586
*
2587
* Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2588
* the caller has to free the object.
2589
*/
2590
xmlXPathCompExprPtr
2591
xsltXPathCompile(xsltStylesheetPtr style, const xmlChar *str) {
2592
return(xsltXPathCompileFlags(style, str, 0));
2593
}
2594
2595
/************************************************************************
2596
* *
2597
* Hooks for the debugger *
2598
* *
2599
************************************************************************/
2600
2601
int xslDebugStatus;
2602
2603
/**
2604
* xsltGetDebuggerStatus:
2605
*
2606
* Get xslDebugStatus.
2607
*
2608
* Returns the value of xslDebugStatus.
2609
*/
2610
int
2611
xsltGetDebuggerStatus(void)
2612
{
2613
return(xslDebugStatus);
2614
}
2615
2616
#ifdef WITH_DEBUGGER
2617
2618
/*
2619
* There is currently only 3 debugging callback defined
2620
* Debugger callbacks are disabled by default
2621
*/
2622
#define XSLT_CALLBACK_NUMBER 3
2623
2624
typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks;
2625
typedef xsltDebuggerCallbacks *xsltDebuggerCallbacksPtr;
2626
struct _xsltDebuggerCallbacks {
2627
xsltHandleDebuggerCallback handler;
2628
xsltAddCallCallback add;
2629
xsltDropCallCallback drop;
2630
};
2631
2632
static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks = {
2633
NULL, /* handler */
2634
NULL, /* add */
2635
NULL /* drop */
2636
};
2637
2638
/**
2639
* xsltSetDebuggerStatus:
2640
* @value : the value to be set
2641
*
2642
* This function sets the value of xslDebugStatus.
2643
*/
2644
void
2645
xsltSetDebuggerStatus(int value)
2646
{
2647
xslDebugStatus = value;
2648
}
2649
2650
/**
2651
* xsltSetDebuggerCallbacks:
2652
* @no : number of callbacks
2653
* @block : the block of callbacks
2654
*
2655
* This function allow to plug a debugger into the XSLT library
2656
* @block points to a block of memory containing the address of @no
2657
* callback routines.
2658
*
2659
* Returns 0 in case of success and -1 in case of error
2660
*/
2661
int
2662
xsltSetDebuggerCallbacks(int no, void *block)
2663
{
2664
xsltDebuggerCallbacksPtr callbacks;
2665
2666
if ((block == NULL) || (no != XSLT_CALLBACK_NUMBER))
2667
return(-1);
2668
2669
callbacks = (xsltDebuggerCallbacksPtr) block;
2670
xsltDebuggerCurrentCallbacks.handler = callbacks->handler;
2671
xsltDebuggerCurrentCallbacks.add = callbacks->add;
2672
xsltDebuggerCurrentCallbacks.drop = callbacks->drop;
2673
return(0);
2674
}
2675
2676
/**
2677
* xslHandleDebugger:
2678
* @cur : source node being executed
2679
* @node : data node being processed
2680
* @templ : temlate that applies to node
2681
* @ctxt : the xslt transform context
2682
*
2683
* If either cur or node are a breakpoint, or xslDebugStatus in state
2684
* where debugging must occcur at this time then transfer control
2685
* to the xslDebugBreak function
2686
*/
2687
void
2688
xslHandleDebugger(xmlNodePtr cur, xmlNodePtr node, xsltTemplatePtr templ,
2689
xsltTransformContextPtr ctxt)
2690
{
2691
if (xsltDebuggerCurrentCallbacks.handler != NULL)
2692
xsltDebuggerCurrentCallbacks.handler(cur, node, templ, ctxt);
2693
}
2694
2695
/**
2696
* xslAddCall:
2697
* @templ : current template being applied
2698
* @source : the source node being processed
2699
*
2700
* Add template "call" to call stack
2701
* Returns : 1 on sucess 0 otherwise an error may be printed if
2702
* WITH_XSLT_DEBUG_BREAKPOINTS is defined
2703
*/
2704
int
2705
xslAddCall(xsltTemplatePtr templ, xmlNodePtr source)
2706
{
2707
if (xsltDebuggerCurrentCallbacks.add != NULL)
2708
return(xsltDebuggerCurrentCallbacks.add(templ, source));
2709
return(0);
2710
}
2711
2712
/**
2713
* xslDropCall:
2714
*
2715
* Drop the topmost item off the call stack
2716
*/
2717
void
2718
xslDropCall(void)
2719
{
2720
if (xsltDebuggerCurrentCallbacks.drop != NULL)
2721
xsltDebuggerCurrentCallbacks.drop();
2722
}
2723
2724
#endif /* WITH_DEBUGGER */
2725
2726