2
* xsltutils.c: Utilities for the XSL Transformation 1.0 engine
5
* http://www.w3.org/TR/1999/REC-xslt-19991116
7
* See Copyright for the status of this software.
15
#ifndef XSLT_NEED_TRIO
23
#ifdef HAVE_SYS_TIME_H
34
#include <libxml/xmlmemory.h>
35
#include <libxml/tree.h>
36
#include <libxml/HTMLtree.h>
37
#include <libxml/xmlerror.h>
38
#include <libxml/xmlIO.h>
39
#include "xsltutils.h"
40
#include "templates.h"
41
#include "xsltInternals.h"
43
#include "transform.h"
45
/* gettimeofday on Windows ??? */
46
#if defined(WIN32) && !defined(__CYGWIN__)
49
#pragma comment(lib, "ws2_32.lib")
50
#define gettimeofday(p1,p2)
51
#define HAVE_GETTIMEOFDAY
52
#define XSLT_WIN32_PERFORMANCE_COUNTER
56
/************************************************************************
58
* Convenience function *
60
************************************************************************/
64
* @style: the stylesheet
66
* @name: the attribute name
67
* @nameSpace: the URI of the namespace
69
* Similar to xmlGetNsProp() but with a slightly different semantic
71
* Search and get the value of an attribute associated to a node
72
* This attribute has to be anchored in the namespace specified,
73
* or has no namespace and the element is in that namespace.
75
* This does the entity substitution.
76
* This function looks in DTD attribute declaration for #FIXED or
77
* default declaration values unless DTD use has been turned off.
79
* Returns the attribute value or NULL if not found. The string is allocated
80
* in the stylesheet dictionary.
83
xsltGetCNsProp(xsltStylesheetPtr style, xmlNodePtr node,
84
const xmlChar *name, const xmlChar *nameSpace) {
91
if ((node == NULL) || (style == NULL) || (style->dict == NULL))
94
if (nameSpace == NULL)
95
return xmlGetProp(node, name);
97
if (node->type == XML_NAMESPACE_DECL)
99
if (node->type == XML_ELEMENT_NODE)
100
prop = node->properties;
103
while (prop != NULL) {
106
* - same attribute names
107
* - and the attribute carrying that namespace
109
if ((xmlStrEqual(prop->name, name)) &&
110
(((prop->ns == NULL) && (node->ns != NULL) &&
111
(xmlStrEqual(node->ns->href, nameSpace))) ||
112
((prop->ns != NULL) &&
113
(xmlStrEqual(prop->ns->href, nameSpace))))) {
115
tmp = xmlNodeListGetString(node->doc, prop->children, 1);
117
ret = xmlDictLookup(style->dict, BAD_CAST "", 0);
119
ret = xmlDictLookup(style->dict, tmp, -1);
128
* Check if there is a default declaration in the internal
129
* or external subsets
133
if (doc->intSubset != NULL) {
134
xmlAttributePtr attrDecl;
136
attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
137
if ((attrDecl == NULL) && (doc->extSubset != NULL))
138
attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
140
if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) {
142
* The DTD declaration only allows a prefix search
144
ns = xmlSearchNs(doc, node, attrDecl->prefix);
145
if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace)))
146
return(xmlDictLookup(style->dict,
147
attrDecl->defaultValue, -1));
156
* @name: the attribute name
157
* @nameSpace: the URI of the namespace
159
* Similar to xmlGetNsProp() but with a slightly different semantic
161
* Search and get the value of an attribute associated to a node
162
* This attribute has to be anchored in the namespace specified,
163
* or has no namespace and the element is in that namespace.
165
* This does the entity substitution.
166
* This function looks in DTD attribute declaration for #FIXED or
167
* default declaration values unless DTD use has been turned off.
169
* Returns the attribute value or NULL if not found.
170
* It's up to the caller to free the memory.
173
xsltGetNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) {
181
if (nameSpace == NULL)
182
return xmlGetProp(node, name);
184
if (node->type == XML_NAMESPACE_DECL)
186
if (node->type == XML_ELEMENT_NODE)
187
prop = node->properties;
191
* TODO: Substitute xmlGetProp() for xmlGetNsProp(), since the former
192
* is not namespace-aware and will return an attribute with equal
193
* name regardless of its namespace.
195
* <xsl:element foo:name="myName"/>
196
* So this would return "myName" even if an attribute @name
197
* in the XSLT was requested.
199
while (prop != NULL) {
202
* - same attribute names
203
* - and the attribute carrying that namespace
205
if ((xmlStrEqual(prop->name, name)) &&
206
(((prop->ns == NULL) && (node->ns != NULL) &&
207
(xmlStrEqual(node->ns->href, nameSpace))) ||
208
((prop->ns != NULL) &&
209
(xmlStrEqual(prop->ns->href, nameSpace))))) {
212
ret = xmlNodeListGetString(node->doc, prop->children, 1);
213
if (ret == NULL) return(xmlStrdup((xmlChar *)""));
220
* Check if there is a default declaration in the internal
221
* or external subsets
225
if (doc->intSubset != NULL) {
226
xmlAttributePtr attrDecl;
228
attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
229
if ((attrDecl == NULL) && (doc->extSubset != NULL))
230
attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
232
if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) {
234
* The DTD declaration only allows a prefix search
236
ns = xmlSearchNs(doc, node, attrDecl->prefix);
237
if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace)))
238
return(xmlStrdup(attrDecl->defaultValue));
247
* @utf: a sequence of UTF-8 encoded bytes
248
* @len: a pointer to @bytes len
250
* Read one UTF8 Char from @utf
251
* Function copied from libxml2 xmlGetUTF8Char() ... to discard ultimately
252
* and use the original API
254
* Returns the char value or -1 in case of error and update @len with the
255
* number of bytes used
258
xsltGetUTF8Char(const unsigned char *utf, int *len) {
272
if ((utf[1] & 0xc0) != 0x80)
274
if ((c & 0xe0) == 0xe0) {
277
if ((utf[2] & 0xc0) != 0x80)
279
if ((c & 0xf0) == 0xf0) {
282
if ((c & 0xf8) != 0xf0 || (utf[3] & 0xc0) != 0x80)
286
c = (utf[0] & 0x7) << 18;
287
c |= (utf[1] & 0x3f) << 12;
288
c |= (utf[2] & 0x3f) << 6;
293
c = (utf[0] & 0xf) << 12;
294
c |= (utf[1] & 0x3f) << 6;
300
c = (utf[0] & 0x1f) << 6;
315
#ifdef XSLT_REFACTORED
318
* xsltPointerListAddSize:
319
* @list: the pointer list structure
320
* @item: the item to be stored
321
* @initialSize: the initial size of the list
323
* Adds an item to the list.
325
* Returns the position of the added item in the list or
326
* -1 in case of an error.
329
xsltPointerListAddSize(xsltPointerListPtr list,
333
if (list->items == NULL) {
334
if (initialSize <= 0)
336
list->items = (void **) xmlMalloc(
337
initialSize * sizeof(void *));
338
if (list->items == NULL) {
339
xsltGenericError(xsltGenericErrorContext,
340
"xsltPointerListAddSize: memory allocation failure.\n");
344
list->size = initialSize;
345
} else if (list->size <= list->number) {
347
list->items = (void **) xmlRealloc(list->items,
348
list->size * sizeof(void *));
349
if (list->items == NULL) {
350
xsltGenericError(xsltGenericErrorContext,
351
"xsltPointerListAddSize: memory re-allocation failure.\n");
356
list->items[list->number++] = item;
361
* xsltPointerListCreate:
362
* @initialSize: the initial size for the list
364
* Creates an xsltPointerList structure.
366
* Returns a xsltPointerList structure or NULL in case of an error.
369
xsltPointerListCreate(int initialSize)
371
xsltPointerListPtr ret;
373
ret = xmlMalloc(sizeof(xsltPointerList));
375
xsltGenericError(xsltGenericErrorContext,
376
"xsltPointerListCreate: memory allocation failure.\n");
379
memset(ret, 0, sizeof(xsltPointerList));
380
if (initialSize > 0) {
381
xsltPointerListAddSize(ret, NULL, initialSize);
388
* xsltPointerListFree:
389
* @list: pointer to the list to be freed
391
* Frees the xsltPointerList structure. This does not free
392
* the content of the list.
395
xsltPointerListFree(xsltPointerListPtr list)
399
if (list->items != NULL)
400
xmlFree(list->items);
405
* xsltPointerListClear:
406
* @list: pointer to the list to be cleared
408
* Resets the list, but does not free the allocated array
409
* and does not free the content of the list.
412
xsltPointerListClear(xsltPointerListPtr list)
414
if (list->items != NULL) {
415
xmlFree(list->items);
422
#endif /* XSLT_REFACTORED */
424
/************************************************************************
426
* Handling of XSLT stylesheets messages *
428
************************************************************************/
432
* @ctxt: an XSLT processing context
433
* @node: The current node
434
* @inst: The node containing the message instruction
436
* Process and xsl:message construct
439
xsltMessage(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst) {
440
xmlGenericErrorFunc error = xsltGenericError;
441
void *errctx = xsltGenericErrorContext;
442
xmlChar *prop, *message;
445
if ((ctxt == NULL) || (inst == NULL))
448
if (ctxt->error != NULL) {
450
errctx = ctxt->errctx;
453
prop = xmlGetNsProp(inst, (const xmlChar *)"terminate", NULL);
455
if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
457
} else if (xmlStrEqual(prop, (const xmlChar *)"no")) {
461
"xsl:message : terminate expecting 'yes' or 'no'\n");
462
ctxt->state = XSLT_STATE_ERROR;
466
message = xsltEvalTemplateString(ctxt, node, inst);
467
if (message != NULL) {
468
int len = xmlStrlen(message);
470
error(errctx, "%s", (const char *)message);
471
if ((len > 0) && (message[len - 1] != '\n'))
476
ctxt->state = XSLT_STATE_STOPPED;
479
/************************************************************************
481
* Handling of out of context errors *
483
************************************************************************/
485
#define XSLT_GET_VAR_STR(msg, str) { \
491
str = (char *) xmlMalloc(150); \
497
while (size < 64000) { \
499
chars = vsnprintf(str, size, msg, ap); \
501
if ((chars > -1) && (chars < size)) \
507
if ((larger = (char *) xmlRealloc(str, size)) == NULL) {\
515
* xsltGenericErrorDefaultFunc:
516
* @ctx: an error context
517
* @msg: the message to display/transmit
518
* @...: extra parameters for the message display
520
* Default handler for out of context error messages.
523
xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
526
if (xsltGenericErrorContext == NULL)
527
xsltGenericErrorContext = (void *) stderr;
530
vfprintf((FILE *)xsltGenericErrorContext, msg, args);
534
xmlGenericErrorFunc xsltGenericError = xsltGenericErrorDefaultFunc;
535
void *xsltGenericErrorContext = NULL;
539
* xsltSetGenericErrorFunc:
540
* @ctx: the new error handling context
541
* @handler: the new handler function
543
* Function to reset the handler and the error context for out of
544
* context error messages.
545
* This simply means that @handler will be called for subsequent
546
* error messages while not parsing nor validating. And @ctx will
547
* be passed as first argument to @handler
548
* One can simply force messages to be emitted to another FILE * than
549
* stderr by setting @ctx to this file handle and @handler to NULL.
552
xsltSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) {
553
xsltGenericErrorContext = ctx;
555
xsltGenericError = handler;
557
xsltGenericError = xsltGenericErrorDefaultFunc;
561
* xsltGenericDebugDefaultFunc:
562
* @ctx: an error context
563
* @msg: the message to display/transmit
564
* @...: extra parameters for the message display
566
* Default handler for out of context error messages.
569
xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
572
if (xsltGenericDebugContext == NULL)
576
vfprintf((FILE *)xsltGenericDebugContext, msg, args);
580
xmlGenericErrorFunc xsltGenericDebug = xsltGenericDebugDefaultFunc;
581
void *xsltGenericDebugContext = NULL;
585
* xsltSetGenericDebugFunc:
586
* @ctx: the new error handling context
587
* @handler: the new handler function
589
* Function to reset the handler and the error context for out of
590
* context error messages.
591
* This simply means that @handler will be called for subsequent
592
* error messages while not parsing or validating. And @ctx will
593
* be passed as first argument to @handler
594
* One can simply force messages to be emitted to another FILE * than
595
* stderr by setting @ctx to this file handle and @handler to NULL.
598
xsltSetGenericDebugFunc(void *ctx, xmlGenericErrorFunc handler) {
599
xsltGenericDebugContext = ctx;
601
xsltGenericDebug = handler;
603
xsltGenericDebug = xsltGenericDebugDefaultFunc;
607
* xsltPrintErrorContext:
608
* @ctxt: the transformation context
609
* @style: the stylesheet
610
* @node: the current node being processed
612
* Display the context of an error.
615
xsltPrintErrorContext(xsltTransformContextPtr ctxt,
616
xsltStylesheetPtr style, xmlNodePtr node) {
618
const xmlChar *file = NULL;
619
const xmlChar *name = NULL;
620
const char *type = "error";
621
xmlGenericErrorFunc error = xsltGenericError;
622
void *errctx = xsltGenericErrorContext;
625
ctxt->state = XSLT_STATE_ERROR;
626
if (ctxt->error != NULL) {
628
errctx = ctxt->errctx;
631
if ((node == NULL) && (ctxt != NULL))
635
if ((node->type == XML_DOCUMENT_NODE) ||
636
(node->type == XML_HTML_DOCUMENT_NODE)) {
637
xmlDocPtr doc = (xmlDocPtr) node;
641
line = xmlGetLineNo(node);
642
if ((node->doc != NULL) && (node->doc->URL != NULL))
643
file = node->doc->URL;
644
if (node->name != NULL)
650
type = "runtime error";
651
else if (style != NULL) {
652
#ifdef XSLT_REFACTORED
653
if (XSLT_CCTXT(style)->errSeverity == XSLT_ERROR_SEVERITY_WARNING)
654
type = "compilation warning";
656
type = "compilation error";
658
type = "compilation error";
662
if ((file != NULL) && (line != 0) && (name != NULL))
663
error(errctx, "%s: file %s line %d element %s\n",
664
type, file, line, name);
665
else if ((file != NULL) && (name != NULL))
666
error(errctx, "%s: file %s element %s\n", type, file, name);
667
else if ((file != NULL) && (line != 0))
668
error(errctx, "%s: file %s line %d\n", type, file, line);
669
else if (file != NULL)
670
error(errctx, "%s: file %s\n", type, file);
671
else if (name != NULL)
672
error(errctx, "%s: element %s\n", type, name);
674
error(errctx, "%s\n", type);
678
* xsltSetTransformErrorFunc:
679
* @ctxt: the XSLT transformation context
680
* @ctx: the new error handling context
681
* @handler: the new handler function
683
* Function to reset the handler and the error context for out of
684
* context error messages specific to a given XSLT transromation.
686
* This simply means that @handler will be called for subsequent
687
* error messages while running the transformation.
690
xsltSetTransformErrorFunc(xsltTransformContextPtr ctxt,
691
void *ctx, xmlGenericErrorFunc handler)
693
ctxt->error = handler;
698
* xsltTransformError:
699
* @ctxt: an XSLT transformation context
700
* @style: the XSLT stylesheet used
701
* @node: the current node in the stylesheet
702
* @msg: the message to display/transmit
703
* @...: extra parameters for the message display
705
* Display and format an error messages, gives file, line, position and
706
* extra parameters, will use the specific transformation context if available
709
xsltTransformError(xsltTransformContextPtr ctxt,
710
xsltStylesheetPtr style,
712
const char *msg, ...) {
713
xmlGenericErrorFunc error = xsltGenericError;
714
void *errctx = xsltGenericErrorContext;
718
ctxt->state = XSLT_STATE_ERROR;
719
if (ctxt->error != NULL) {
721
errctx = ctxt->errctx;
724
if ((node == NULL) && (ctxt != NULL))
726
xsltPrintErrorContext(ctxt, style, node);
727
XSLT_GET_VAR_STR(msg, str);
728
error(errctx, "%s", str);
733
/************************************************************************
737
************************************************************************/
741
* @dict: a dictionary
742
* @name: the full QName
743
* @prefix: the return value
745
* Split QNames into prefix and local names, both allocated from a dictionary.
747
* Returns: the localname or NULL in case of error.
750
xsltSplitQName(xmlDictPtr dict, const xmlChar *name, const xmlChar **prefix) {
752
const xmlChar *ret = NULL;
755
if ((name == NULL) || (dict == NULL)) return(NULL);
757
return(xmlDictLookup(dict, name, -1));
758
while ((name[len] != 0) && (name[len] != ':')) len++;
759
if (name[len] == 0) return(xmlDictLookup(dict, name, -1));
760
*prefix = xmlDictLookup(dict, name, len);
761
ret = xmlDictLookup(dict, &name[len + 1], -1);
767
* @node: the node holding the QName
768
* @name: pointer to the initial QName value
770
* This function analyzes @name, if the name contains a prefix,
771
* the function seaches the associated namespace in scope for it.
772
* It will also replace @name value with the NCName, the old value being
774
* Errors in the prefix lookup are signalled by setting @name to NULL.
776
* NOTE: the namespace returned is a pointer to the place where it is
777
* defined and hence has the same lifespan as the document holding it.
779
* Returns the namespace URI if there is a prefix, or NULL if @name is
783
xsltGetQNameURI(xmlNodePtr node, xmlChar ** name)
792
if ((qname == NULL) || (*qname == 0))
795
xsltGenericError(xsltGenericErrorContext,
796
"QName: no element for namespace lookup %s\n",
803
/* nasty but valid */
808
* we are not trying to validate but just to cut, and yes it will
809
* work even if this is a set of UTF-8 encoded chars
811
while ((qname[len] != 0) && (qname[len] != ':'))
818
* handle xml: separately, this one is magical
820
if ((qname[0] == 'x') && (qname[1] == 'm') &&
821
(qname[2] == 'l') && (qname[3] == ':')) {
824
*name = xmlStrdup(&qname[4]);
826
return(XML_XML_NAMESPACE);
830
ns = xmlSearchNs(node->doc, node, qname);
832
xsltGenericError(xsltGenericErrorContext,
833
"%s:%s : no namespace bound to prefix %s\n",
834
qname, &qname[len + 1], qname);
839
*name = xmlStrdup(&qname[len + 1]);
846
* @style: stylesheet pointer
847
* @node: the node holding the QName
848
* @name: pointer to the initial QName value
850
* This function is similar to xsltGetQNameURI, but is used when
851
* @name is a dictionary entry.
853
* Returns the namespace URI if there is a prefix, or NULL if @name is
857
xsltGetQNameURI2(xsltStylesheetPtr style, xmlNodePtr node,
858
const xmlChar **name) {
865
qname = (xmlChar *)*name;
866
if ((qname == NULL) || (*qname == 0))
869
xsltGenericError(xsltGenericErrorContext,
870
"QName: no element for namespace lookup %s\n",
877
* we are not trying to validate but just to cut, and yes it will
878
* work even if this is a set of UTF-8 encoded chars
880
while ((qname[len] != 0) && (qname[len] != ':'))
887
* handle xml: separately, this one is magical
889
if ((qname[0] == 'x') && (qname[1] == 'm') &&
890
(qname[2] == 'l') && (qname[3] == ':')) {
893
*name = xmlDictLookup(style->dict, &qname[4], -1);
894
return(XML_XML_NAMESPACE);
897
qname = xmlStrndup(*name, len);
898
ns = xmlSearchNs(node->doc, node, qname);
901
xsltTransformError(NULL, style, node,
902
"No namespace bound to prefix '%s'.\n",
906
xsltGenericError(xsltGenericErrorContext,
907
"%s : no namespace bound to prefix %s\n",
914
*name = xmlDictLookup(style->dict, (*name)+len+1, -1);
919
/************************************************************************
923
************************************************************************/
926
* xsltDocumentSortFunction:
927
* @list: the node set
929
* reorder the current node list @list accordingly to the document order
930
* This function is slow, obsolete and should not be used anymore.
933
xsltDocumentSortFunction(xmlNodeSetPtr list) {
943
/* TODO: sort is really not optimized, does it needs to ? */
944
for (i = 0;i < len -1;i++) {
945
for (j = i + 1; j < len; j++) {
946
tst = xmlXPathCmpNodes(list->nodeTab[i], list->nodeTab[j]);
948
node = list->nodeTab[i];
949
list->nodeTab[i] = list->nodeTab[j];
950
list->nodeTab[j] = node;
957
* xsltComputeSortResult:
958
* @ctxt: a XSLT process context
961
* reorder the current node list accordingly to the set of sorting
962
* requirement provided by the array of nodes.
964
* Returns a ordered XPath nodeset or NULL in case of error.
967
xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) {
968
#ifdef XSLT_REFACTORED
969
xsltStyleItemSortPtr comp;
971
xsltStylePreCompPtr comp;
973
xmlXPathObjectPtr *results = NULL;
974
xmlNodeSetPtr list = NULL;
975
xmlXPathObjectPtr res;
980
int oldPos, oldSize ;
982
xmlNsPtr *oldNamespaces;
986
xsltGenericError(xsltGenericErrorContext,
987
"xsl:sort : compilation failed\n");
991
if ((comp->select == NULL) || (comp->comp == NULL))
994
list = ctxt->nodeList;
995
if ((list == NULL) || (list->nodeNr <= 1))
1000
/* TODO: xsl:sort lang attribute */
1001
/* TODO: xsl:sort case-order attribute */
1004
results = xmlMalloc(len * sizeof(xmlXPathObjectPtr));
1005
if (results == NULL) {
1006
xsltGenericError(xsltGenericErrorContext,
1007
"xsltComputeSortResult: memory allocation failure\n");
1011
oldNode = ctxt->node;
1012
oldInst = ctxt->inst;
1013
oldPos = ctxt->xpathCtxt->proximityPosition;
1014
oldSize = ctxt->xpathCtxt->contextSize;
1015
oldNsNr = ctxt->xpathCtxt->nsNr;
1016
oldNamespaces = ctxt->xpathCtxt->namespaces;
1017
for (i = 0;i < len;i++) {
1019
ctxt->xpathCtxt->contextSize = len;
1020
ctxt->xpathCtxt->proximityPosition = i + 1;
1021
ctxt->node = list->nodeTab[i];
1022
ctxt->xpathCtxt->node = ctxt->node;
1023
#ifdef XSLT_REFACTORED
1024
if (comp->inScopeNs != NULL) {
1025
ctxt->xpathCtxt->namespaces = comp->inScopeNs->list;
1026
ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber;
1028
ctxt->xpathCtxt->namespaces = NULL;
1029
ctxt->xpathCtxt->nsNr = 0;
1032
ctxt->xpathCtxt->namespaces = comp->nsList;
1033
ctxt->xpathCtxt->nsNr = comp->nsNr;
1035
res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
1037
if (res->type != XPATH_STRING)
1038
res = xmlXPathConvertString(res);
1040
res = xmlXPathConvertNumber(res);
1041
res->index = i; /* Save original pos for dupl resolv */
1043
if (res->type == XPATH_NUMBER) {
1046
#ifdef WITH_XSLT_DEBUG_PROCESS
1047
xsltGenericDebug(xsltGenericDebugContext,
1048
"xsltComputeSortResult: select didn't evaluate to a number\n");
1053
if (res->type == XPATH_STRING) {
1054
if (comp->locale != (xsltLocale)0) {
1055
xmlChar *str = res->stringval;
1056
res->stringval = (xmlChar *) xsltStrxfrm(comp->locale, str);
1062
#ifdef WITH_XSLT_DEBUG_PROCESS
1063
xsltGenericDebug(xsltGenericDebugContext,
1064
"xsltComputeSortResult: select didn't evaluate to a string\n");
1070
ctxt->state = XSLT_STATE_STOPPED;
1074
ctxt->node = oldNode;
1075
ctxt->inst = oldInst;
1076
ctxt->xpathCtxt->contextSize = oldSize;
1077
ctxt->xpathCtxt->proximityPosition = oldPos;
1078
ctxt->xpathCtxt->nsNr = oldNsNr;
1079
ctxt->xpathCtxt->namespaces = oldNamespaces;
1085
* xsltDefaultSortFunction:
1086
* @ctxt: a XSLT process context
1087
* @sorts: array of sort nodes
1088
* @nbsorts: the number of sorts in the array
1090
* reorder the current node list accordingly to the set of sorting
1091
* requirement provided by the arry of nodes.
1094
xsltDefaultSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts,
1096
#ifdef XSLT_REFACTORED
1097
xsltStyleItemSortPtr comp;
1099
xsltStylePreCompPtr comp;
1101
xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT];
1102
xmlXPathObjectPtr *results = NULL, *res;
1103
xmlNodeSetPtr list = NULL;
1104
int descending, number, desc, numb;
1110
xmlXPathObjectPtr tmp;
1111
int tempstype[XSLT_MAX_SORT], temporder[XSLT_MAX_SORT];
1113
if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) ||
1114
(nbsorts >= XSLT_MAX_SORT))
1116
if (sorts[0] == NULL)
1118
comp = sorts[0]->psvi;
1122
list = ctxt->nodeList;
1123
if ((list == NULL) || (list->nodeNr <= 1))
1124
return; /* nothing to do */
1126
for (j = 0; j < nbsorts; j++) {
1127
comp = sorts[j]->psvi;
1129
if ((comp->stype == NULL) && (comp->has_stype != 0)) {
1131
xsltEvalAttrValueTemplate(ctxt, sorts[j],
1132
(const xmlChar *) "data-type",
1134
if (comp->stype != NULL) {
1136
if (xmlStrEqual(comp->stype, (const xmlChar *) "text"))
1138
else if (xmlStrEqual(comp->stype, (const xmlChar *) "number"))
1141
xsltTransformError(ctxt, NULL, sorts[j],
1142
"xsltDoSortFunction: no support for data-type = %s\n",
1144
comp->number = 0; /* use default */
1149
if ((comp->order == NULL) && (comp->has_order != 0)) {
1150
comp->order = xsltEvalAttrValueTemplate(ctxt, sorts[j],
1151
(const xmlChar *) "order",
1153
if (comp->order != NULL) {
1155
if (xmlStrEqual(comp->order, (const xmlChar *) "ascending"))
1156
comp->descending = 0;
1157
else if (xmlStrEqual(comp->order,
1158
(const xmlChar *) "descending"))
1159
comp->descending = 1;
1161
xsltTransformError(ctxt, NULL, sorts[j],
1162
"xsltDoSortFunction: invalid value %s for order\n",
1164
comp->descending = 0; /* use default */
1172
resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]);
1173
for (i = 1;i < XSLT_MAX_SORT;i++)
1174
resultsTab[i] = NULL;
1176
results = resultsTab[0];
1178
comp = sorts[0]->psvi;
1179
descending = comp->descending;
1180
number = comp->number;
1181
if (results == NULL)
1184
/* Shell's sort of node-set */
1185
for (incr = len / 2; incr > 0; incr /= 2) {
1186
for (i = incr; i < len; i++) {
1188
if (results[i] == NULL)
1192
if (results[j] == NULL)
1196
/* We make NaN smaller than number in accordance
1198
if (xmlXPathIsNaN(results[j]->floatval)) {
1199
if (xmlXPathIsNaN(results[j + incr]->floatval))
1203
} else if (xmlXPathIsNaN(results[j + incr]->floatval))
1205
else if (results[j]->floatval ==
1206
results[j + incr]->floatval)
1208
else if (results[j]->floatval >
1209
results[j + incr]->floatval)
1212
} else if(comp->locale != (xsltLocale)0) {
1213
tst = xsltLocaleStrcmp(
1215
(xsltLocaleChar *) results[j]->stringval,
1216
(xsltLocaleChar *) results[j + incr]->stringval);
1218
tst = xmlStrcmp(results[j]->stringval,
1219
results[j + incr]->stringval);
1226
* Okay we need to use multi level sorts
1229
while (depth < nbsorts) {
1230
if (sorts[depth] == NULL)
1232
comp = sorts[depth]->psvi;
1235
desc = comp->descending;
1236
numb = comp->number;
1239
* Compute the result of the next level for the
1240
* full set, this might be optimized ... or not
1242
if (resultsTab[depth] == NULL)
1243
resultsTab[depth] = xsltComputeSortResult(ctxt,
1245
res = resultsTab[depth];
1248
if (res[j] == NULL) {
1249
if (res[j+incr] != NULL)
1253
/* We make NaN smaller than number in
1254
accordance with XSLT spec */
1255
if (xmlXPathIsNaN(res[j]->floatval)) {
1256
if (xmlXPathIsNaN(res[j +
1261
} else if (xmlXPathIsNaN(res[j + incr]->
1264
else if (res[j]->floatval == res[j + incr]->
1267
else if (res[j]->floatval >
1268
res[j + incr]->floatval)
1271
} else if(comp->locale != (xsltLocale)0) {
1272
tst = xsltLocaleStrcmp(
1274
(xsltLocaleChar *) res[j]->stringval,
1275
(xsltLocaleChar *) res[j + incr]->stringval);
1277
tst = xmlStrcmp(res[j]->stringval,
1278
res[j + incr]->stringval);
1285
* if we still can't differenciate at this level
1286
* try one level deeper.
1294
tst = results[j]->index > results[j + incr]->index;
1298
results[j] = results[j + incr];
1299
results[j + incr] = tmp;
1300
node = list->nodeTab[j];
1301
list->nodeTab[j] = list->nodeTab[j + incr];
1302
list->nodeTab[j + incr] = node;
1304
while (depth < nbsorts) {
1305
if (sorts[depth] == NULL)
1307
if (resultsTab[depth] == NULL)
1309
res = resultsTab[depth];
1311
res[j] = res[j + incr];
1312
res[j + incr] = tmp;
1322
for (j = 0; j < nbsorts; j++) {
1323
comp = sorts[j]->psvi;
1324
if (tempstype[j] == 1) {
1325
/* The data-type needs to be recomputed each time */
1326
xmlFree((void *)(comp->stype));
1329
if (temporder[j] == 1) {
1330
/* The order needs to be recomputed each time */
1331
xmlFree((void *)(comp->order));
1334
if (resultsTab[j] != NULL) {
1335
for (i = 0;i < len;i++)
1336
xmlXPathFreeObject(resultsTab[j][i]);
1337
xmlFree(resultsTab[j]);
1343
static xsltSortFunc xsltSortFunction = xsltDefaultSortFunction;
1346
* xsltDoSortFunction:
1347
* @ctxt: a XSLT process context
1348
* @sorts: array of sort nodes
1349
* @nbsorts: the number of sorts in the array
1351
* reorder the current node list accordingly to the set of sorting
1352
* requirement provided by the arry of nodes.
1353
* This is a wrapper function, the actual function used is specified
1354
* using xsltSetCtxtSortFunc() to set the context specific sort function,
1355
* or xsltSetSortFunc() to set the global sort function.
1356
* If a sort function is set on the context, this will get called.
1357
* Otherwise the global sort function is called.
1360
xsltDoSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr * sorts,
1363
if (ctxt->sortfunc != NULL)
1364
(ctxt->sortfunc)(ctxt, sorts, nbsorts);
1365
else if (xsltSortFunction != NULL)
1366
xsltSortFunction(ctxt, sorts, nbsorts);
1371
* @handler: the new handler function
1373
* Function to reset the global handler for XSLT sorting.
1374
* If the handler is NULL, the default sort function will be used.
1377
xsltSetSortFunc(xsltSortFunc handler) {
1378
if (handler != NULL)
1379
xsltSortFunction = handler;
1381
xsltSortFunction = xsltDefaultSortFunction;
1385
* xsltSetCtxtSortFunc:
1386
* @ctxt: a XSLT process context
1387
* @handler: the new handler function
1389
* Function to set the handler for XSLT sorting
1390
* for the specified context.
1391
* If the handler is NULL, then the global
1392
* sort function will be called
1395
xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt, xsltSortFunc handler) {
1396
ctxt->sortfunc = handler;
1399
/************************************************************************
1403
************************************************************************/
1406
* xsltSetCtxtParseOptions:
1407
* @ctxt: a XSLT process context
1408
* @options: a combination of libxml2 xmlParserOption
1410
* Change the default parser option passed by the XSLT engine to the
1411
* parser when using document() loading.
1413
* Returns the previous options or -1 in case of error
1416
xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt, int options)
1422
oldopts = ctxt->parserOptions;
1424
oldopts |= XML_PARSE_XINCLUDE;
1425
ctxt->parserOptions = options;
1426
if (options & XML_PARSE_XINCLUDE)
1433
/************************************************************************
1437
************************************************************************/
1441
* @buf: an output buffer
1442
* @result: the result xmlDocPtr
1443
* @style: the stylesheet
1445
* Save the result @result obtained by applying the @style stylesheet
1446
* to an I/O output channel @buf
1448
* Returns the number of byte written or -1 in case of failure.
1451
xsltSaveResultTo(xmlOutputBufferPtr buf, xmlDocPtr result,
1452
xsltStylesheetPtr style) {
1453
const xmlChar *encoding;
1455
const xmlChar *method;
1458
if ((buf == NULL) || (result == NULL) || (style == NULL))
1460
if ((result->children == NULL) ||
1461
((result->children->type == XML_DTD_NODE) &&
1462
(result->children->next == NULL)))
1465
if ((style->methodURI != NULL) &&
1466
((style->method == NULL) ||
1467
(!xmlStrEqual(style->method, (const xmlChar *) "xhtml")))) {
1468
xsltGenericError(xsltGenericErrorContext,
1469
"xsltSaveResultTo : unknown ouput method\n");
1473
base = buf->written;
1475
XSLT_GET_IMPORT_PTR(method, style, method)
1476
XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1477
XSLT_GET_IMPORT_INT(indent, style, indent);
1479
if ((method == NULL) && (result->type == XML_HTML_DOCUMENT_NODE))
1480
method = (const xmlChar *) "html";
1482
if ((method != NULL) &&
1483
(xmlStrEqual(method, (const xmlChar *) "html"))) {
1484
if (encoding != NULL) {
1485
htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1487
htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
1491
htmlDocContentDumpFormatOutput(buf, result, (const char *) encoding,
1493
xmlOutputBufferFlush(buf);
1494
} else if ((method != NULL) &&
1495
(xmlStrEqual(method, (const xmlChar *) "xhtml"))) {
1496
if (encoding != NULL) {
1497
htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1499
htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
1501
htmlDocContentDumpOutput(buf, result, (const char *) encoding);
1502
xmlOutputBufferFlush(buf);
1503
} else if ((method != NULL) &&
1504
(xmlStrEqual(method, (const xmlChar *) "text"))) {
1507
cur = result->children;
1508
while (cur != NULL) {
1509
if (cur->type == XML_TEXT_NODE)
1510
xmlOutputBufferWriteString(buf, (const char *) cur->content);
1515
if (cur->children != NULL) {
1516
if ((cur->children->type != XML_ENTITY_DECL) &&
1517
(cur->children->type != XML_ENTITY_REF_NODE) &&
1518
(cur->children->type != XML_ENTITY_NODE)) {
1519
cur = cur->children;
1523
if (cur->next != NULL) {
1532
if (cur == (xmlNodePtr) style->doc) {
1536
if (cur->next != NULL) {
1540
} while (cur != NULL);
1542
xmlOutputBufferFlush(buf);
1547
XSLT_GET_IMPORT_INT(omitXmlDecl, style, omitXmlDeclaration);
1548
XSLT_GET_IMPORT_INT(standalone, style, standalone);
1550
if (omitXmlDecl != 1) {
1551
xmlOutputBufferWriteString(buf, "<?xml version=");
1552
if (result->version != NULL) {
1553
xmlOutputBufferWriteString(buf, "\"");
1554
xmlOutputBufferWriteString(buf, (const char *)result->version);
1555
xmlOutputBufferWriteString(buf, "\"");
1557
xmlOutputBufferWriteString(buf, "\"1.0\"");
1558
if (encoding == NULL) {
1559
if (result->encoding != NULL)
1560
encoding = result->encoding;
1561
else if (result->charset != XML_CHAR_ENCODING_UTF8)
1562
encoding = (const xmlChar *)
1563
xmlGetCharEncodingName((xmlCharEncoding)
1566
if (encoding != NULL) {
1567
xmlOutputBufferWriteString(buf, " encoding=");
1568
xmlOutputBufferWriteString(buf, "\"");
1569
xmlOutputBufferWriteString(buf, (const char *) encoding);
1570
xmlOutputBufferWriteString(buf, "\"");
1572
switch (standalone) {
1574
xmlOutputBufferWriteString(buf, " standalone=\"no\"");
1577
xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
1582
xmlOutputBufferWriteString(buf, "?>\n");
1584
if (result->children != NULL) {
1585
xmlNodePtr child = result->children;
1587
while (child != NULL) {
1588
xmlNodeDumpOutput(buf, result, child, 0, (indent == 1),
1589
(const char *) encoding);
1590
if (indent && ((child->type == XML_DTD_NODE) ||
1591
((child->type == XML_COMMENT_NODE) &&
1592
(child->next != NULL))))
1593
xmlOutputBufferWriteString(buf, "\n");
1594
child = child->next;
1597
xmlOutputBufferWriteString(buf, "\n");
1599
xmlOutputBufferFlush(buf);
1601
return(buf->written - base);
1605
* xsltSaveResultToFilename:
1606
* @URL: a filename or URL
1607
* @result: the result xmlDocPtr
1608
* @style: the stylesheet
1609
* @compression: the compression factor (0 - 9 included)
1611
* Save the result @result obtained by applying the @style stylesheet
1614
* Returns the number of byte written or -1 in case of failure.
1617
xsltSaveResultToFilename(const char *URL, xmlDocPtr result,
1618
xsltStylesheetPtr style, int compression) {
1619
xmlOutputBufferPtr buf;
1620
const xmlChar *encoding;
1623
if ((URL == NULL) || (result == NULL) || (style == NULL))
1625
if (result->children == NULL)
1628
XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1629
if (encoding != NULL) {
1630
xmlCharEncodingHandlerPtr encoder;
1632
encoder = xmlFindCharEncodingHandler((char *)encoding);
1633
if ((encoder != NULL) &&
1634
(xmlStrEqual((const xmlChar *)encoder->name,
1635
(const xmlChar *) "UTF-8")))
1637
buf = xmlOutputBufferCreateFilename(URL, encoder, compression);
1639
buf = xmlOutputBufferCreateFilename(URL, NULL, compression);
1643
xsltSaveResultTo(buf, result, style);
1644
ret = xmlOutputBufferClose(buf);
1649
* xsltSaveResultToFile:
1650
* @file: a FILE * I/O
1651
* @result: the result xmlDocPtr
1652
* @style: the stylesheet
1654
* Save the result @result obtained by applying the @style stylesheet
1655
* to an open FILE * I/O.
1656
* This does not close the FILE @file
1658
* Returns the number of bytes written or -1 in case of failure.
1661
xsltSaveResultToFile(FILE *file, xmlDocPtr result, xsltStylesheetPtr style) {
1662
xmlOutputBufferPtr buf;
1663
const xmlChar *encoding;
1666
if ((file == NULL) || (result == NULL) || (style == NULL))
1668
if (result->children == NULL)
1671
XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1672
if (encoding != NULL) {
1673
xmlCharEncodingHandlerPtr encoder;
1675
encoder = xmlFindCharEncodingHandler((char *)encoding);
1676
if ((encoder != NULL) &&
1677
(xmlStrEqual((const xmlChar *)encoder->name,
1678
(const xmlChar *) "UTF-8")))
1680
buf = xmlOutputBufferCreateFile(file, encoder);
1682
buf = xmlOutputBufferCreateFile(file, NULL);
1687
xsltSaveResultTo(buf, result, style);
1688
ret = xmlOutputBufferClose(buf);
1693
* xsltSaveResultToFd:
1694
* @fd: a file descriptor
1695
* @result: the result xmlDocPtr
1696
* @style: the stylesheet
1698
* Save the result @result obtained by applying the @style stylesheet
1699
* to an open file descriptor
1700
* This does not close the descriptor.
1702
* Returns the number of bytes written or -1 in case of failure.
1705
xsltSaveResultToFd(int fd, xmlDocPtr result, xsltStylesheetPtr style) {
1706
xmlOutputBufferPtr buf;
1707
const xmlChar *encoding;
1710
if ((fd < 0) || (result == NULL) || (style == NULL))
1712
if (result->children == NULL)
1715
XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1716
if (encoding != NULL) {
1717
xmlCharEncodingHandlerPtr encoder;
1719
encoder = xmlFindCharEncodingHandler((char *)encoding);
1720
if ((encoder != NULL) &&
1721
(xmlStrEqual((const xmlChar *)encoder->name,
1722
(const xmlChar *) "UTF-8")))
1724
buf = xmlOutputBufferCreateFd(fd, encoder);
1726
buf = xmlOutputBufferCreateFd(fd, NULL);
1730
xsltSaveResultTo(buf, result, style);
1731
ret = xmlOutputBufferClose(buf);
1736
* xsltSaveResultToString:
1737
* @doc_txt_ptr: Memory pointer for allocated XML text
1738
* @doc_txt_len: Length of the generated XML text
1739
* @result: the result xmlDocPtr
1740
* @style: the stylesheet
1742
* Save the result @result obtained by applying the @style stylesheet
1743
* to a new allocated string.
1745
* Returns 0 in case of success and -1 in case of error
1748
xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len,
1749
xmlDocPtr result, xsltStylesheetPtr style) {
1750
xmlOutputBufferPtr buf;
1751
const xmlChar *encoding;
1753
*doc_txt_ptr = NULL;
1755
if (result->children == NULL)
1758
XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1759
if (encoding != NULL) {
1760
xmlCharEncodingHandlerPtr encoder;
1762
encoder = xmlFindCharEncodingHandler((char *)encoding);
1763
if ((encoder != NULL) &&
1764
(xmlStrEqual((const xmlChar *)encoder->name,
1765
(const xmlChar *) "UTF-8")))
1767
buf = xmlAllocOutputBuffer(encoder);
1769
buf = xmlAllocOutputBuffer(NULL);
1773
xsltSaveResultTo(buf, result, style);
1774
#ifdef LIBXML2_NEW_BUFFER
1775
if (buf->conv != NULL) {
1776
*doc_txt_len = xmlBufUse(buf->conv);
1777
*doc_txt_ptr = xmlStrndup(xmlBufContent(buf->conv), *doc_txt_len);
1779
*doc_txt_len = xmlBufUse(buf->buffer);
1780
*doc_txt_ptr = xmlStrndup(xmlBufContent(buf->buffer), *doc_txt_len);
1783
if (buf->conv != NULL) {
1784
*doc_txt_len = buf->conv->use;
1785
*doc_txt_ptr = xmlStrndup(buf->conv->content, *doc_txt_len);
1787
*doc_txt_len = buf->buffer->use;
1788
*doc_txt_ptr = xmlStrndup(buf->buffer->content, *doc_txt_len);
1791
(void)xmlOutputBufferClose(buf);
1795
/************************************************************************
1797
* Generating profiling informations *
1799
************************************************************************/
1801
static long calibration = -1;
1804
* xsltCalibrateTimestamps:
1806
* Used for to calibrate the xsltTimestamp() function
1807
* Should work if launched at startup and we don't loose our quantum :-)
1809
* Returns the number of milliseconds used by xsltTimestamp()
1812
xsltCalibrateTimestamps(void) {
1815
for (i = 0;i < 999;i++)
1817
return(xsltTimestamp() / 1000);
1821
* xsltCalibrateAdjust:
1822
* @delta: a negative dealy value found
1824
* Used for to correct the calibration for xsltTimestamp()
1827
xsltCalibrateAdjust(long delta) {
1828
calibration += delta;
1834
* Used for gathering profiling data
1836
* Returns the number of tenth of milliseconds since the beginning of the
1842
#ifdef XSLT_WIN32_PERFORMANCE_COUNTER
1844
LARGE_INTEGER performanceCount;
1845
LARGE_INTEGER performanceFrequency;
1848
static LONGLONG startupQuadCount = 0;
1849
static LONGLONG startupQuadFreq = 0;
1851
ok = QueryPerformanceCounter(&performanceCount);
1854
quadCount = performanceCount.QuadPart;
1855
if (calibration < 0) {
1857
ok = QueryPerformanceFrequency(&performanceFrequency);
1860
startupQuadFreq = performanceFrequency.QuadPart;
1861
startupQuadCount = quadCount;
1864
if (startupQuadFreq == 0)
1866
seconds = (quadCount - startupQuadCount) / (double) startupQuadFreq;
1867
return (long) (seconds * XSLT_TIMESTAMP_TICS_PER_SEC);
1869
#else /* XSLT_WIN32_PERFORMANCE_COUNTER */
1870
#ifdef HAVE_CLOCK_GETTIME
1871
# if defined(CLOCK_MONOTONIC)
1872
# define XSLT_CLOCK CLOCK_MONOTONIC
1873
# elif defined(CLOCK_HIGHRES)
1874
# define XSLT_CLOCK CLOCK_HIGHRES
1876
# define XSLT_CLOCK CLOCK_REALTIME
1878
static struct timespec startup;
1879
struct timespec cur;
1882
if (calibration < 0) {
1883
clock_gettime(XSLT_CLOCK, &startup);
1885
calibration = xsltCalibrateTimestamps();
1886
clock_gettime(XSLT_CLOCK, &startup);
1890
clock_gettime(XSLT_CLOCK, &cur);
1891
tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
1892
tics += (cur.tv_nsec - startup.tv_nsec) /
1893
(1000000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
1895
tics -= calibration;
1898
#elif HAVE_GETTIMEOFDAY
1899
static struct timeval startup;
1903
if (calibration < 0) {
1904
gettimeofday(&startup, NULL);
1906
calibration = xsltCalibrateTimestamps();
1907
gettimeofday(&startup, NULL);
1911
gettimeofday(&cur, NULL);
1912
tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
1913
tics += (cur.tv_usec - startup.tv_usec) /
1914
(1000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
1916
tics -= calibration;
1920
/* Neither gettimeofday() nor Win32 performance counter available */
1924
#endif /* HAVE_GETTIMEOFDAY */
1925
#endif /* XSLT_WIN32_PERFORMANCE_COUNTER */
1929
pretty_templ_match(xsltTemplatePtr templ) {
1930
static char dst[1001];
1931
char *src = (char *)templ->match;
1934
/* strip white spaces */
1935
for (j=0; i<1000 && src[j]; i++,j++) {
1936
for(;src[j]==' ';j++);
1939
if(i<998 && templ->mode) {
1942
src=(char *)templ->mode;
1943
for (j=0; i<999 && src[j]; i++,j++) {
1952
#define MAX_TEMPLATES 10000
1955
* xsltSaveProfiling:
1956
* @ctxt: an XSLT context
1957
* @output: a FILE * for saving the informations
1959
* Save the profiling informations on @output
1962
xsltSaveProfiling(xsltTransformContextPtr ctxt, FILE *output) {
1967
xsltTemplatePtr *templates;
1968
xsltStylesheetPtr style;
1969
xsltTemplatePtr templ1,templ2;
1972
if ((output == NULL) || (ctxt == NULL))
1974
if (ctxt->profile == 0)
1978
max = MAX_TEMPLATES;
1979
templates = xmlMalloc(max * sizeof(xsltTemplatePtr));
1980
if (templates == NULL)
1983
style = ctxt->style;
1984
while (style != NULL) {
1985
templ1 = style->templates;
1986
while (templ1 != NULL) {
1990
if (templ1->nbCalls > 0)
1991
templates[nb++] = templ1;
1992
templ1 = templ1->next;
1995
style = xsltNextImport(style);
1998
for (i = 0;i < nb -1;i++) {
1999
for (j = i + 1; j < nb; j++) {
2000
if ((templates[i]->time <= templates[j]->time) ||
2001
((templates[i]->time == templates[j]->time) &&
2002
(templates[i]->nbCalls <= templates[j]->nbCalls))) {
2003
templ1 = templates[j];
2004
templates[j] = templates[i];
2005
templates[i] = templ1;
2011
/* print flat profile */
2013
fprintf(output, "%6s%20s%20s%10s Calls Tot 100us Avg\n\n",
2014
"number", "match", "name", "mode");
2017
for (i = 0;i < nb;i++) {
2018
templ1 = templates[i];
2019
fprintf(output, "%5d ", i);
2020
if (templ1->match != NULL) {
2021
if (xmlStrlen(templ1->match) > 20)
2022
fprintf(output, "%s\n%26s", templ1->match, "");
2024
fprintf(output, "%20s", templ1->match);
2026
fprintf(output, "%20s", "");
2028
if (templ1->name != NULL) {
2029
if (xmlStrlen(templ1->name) > 20)
2030
fprintf(output, "%s\n%46s", templ1->name, "");
2032
fprintf(output, "%20s", templ1->name);
2034
fprintf(output, "%20s", "");
2036
if (templ1->mode != NULL) {
2037
if (xmlStrlen(templ1->mode) > 10)
2038
fprintf(output, "%s\n%56s", templ1->mode, "");
2040
fprintf(output, "%10s", templ1->mode);
2042
fprintf(output, "%10s", "");
2044
fprintf(output, " %6d", templ1->nbCalls);
2045
fprintf(output, " %6ld %6ld\n", templ1->time,
2046
templ1->time / templ1->nbCalls);
2047
total += templ1->nbCalls;
2048
totalt += templ1->time;
2050
fprintf(output, "\n%30s%26s %6d %6ld\n", "Total", "", total, totalt);
2053
/* print call graph */
2055
childt = xmlMalloc((nb + 1) * sizeof(int));
2059
/* precalculate children times */
2060
for (i = 0; i < nb; i++) {
2061
templ1 = templates[i];
2064
for (k = 0; k < nb; k++) {
2065
templ2 = templates[k];
2066
for (l = 0; l < templ2->templNr; l++) {
2067
if (templ2->templCalledTab[l] == templ1) {
2068
childt[i] +=templ2->time;
2075
fprintf(output, "\nindex %% time self children called name\n");
2077
for (i = 0; i < nb; i++) {
2078
char ix_str[20], timep_str[20], times_str[20], timec_str[20], called_str[20];
2081
templ1 = templates[i];
2083
for (j = 0; j < templ1->templNr; j++) {
2084
templ2 = templ1->templCalledTab[j];
2085
for (k = 0; k < nb; k++) {
2086
if (templates[k] == templ2)
2089
t=templ2?templ2->time:totalt;
2090
sprintf(times_str,"%8.3f",(float)t/XSLT_TIMESTAMP_TICS_PER_SEC);
2091
sprintf(timec_str,"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC);
2092
sprintf(called_str,"%6d/%d",
2093
templ1->templCountTab[j], /* number of times caller calls 'this' */
2094
templ1->nbCalls); /* total number of calls to 'this' */
2096
fprintf(output, " %-8s %-8s %-12s %s [%d]\n",
2097
times_str,timec_str,called_str,
2098
(templ2?(templ2->name?(char *)templ2->name:pretty_templ_match(templ2)):"-"),k);
2101
sprintf(ix_str,"[%d]",i);
2102
sprintf(timep_str,"%6.2f",(float)templ1->time*100.0/totalt);
2103
sprintf(times_str,"%8.3f",(float)templ1->time/XSLT_TIMESTAMP_TICS_PER_SEC);
2104
sprintf(timec_str,"%8.3f",(float)childt[i]/XSLT_TIMESTAMP_TICS_PER_SEC);
2105
fprintf(output, "%-5s %-6s %-8s %-8s %6d %s [%d]\n",
2106
ix_str, timep_str,times_str,timec_str,
2108
templ1->name?(char *)templ1->name:pretty_templ_match(templ1),i);
2110
* - go over templates[0..nb] and their templCalledTab[]
2111
* - print those where we in the the call-stack
2114
for (k = 0; k < nb; k++) {
2115
templ2 = templates[k];
2116
for (l = 0; l < templ2->templNr; l++) {
2117
if (templ2->templCalledTab[l] == templ1) {
2118
total+=templ2->templCountTab[l];
2122
for (k = 0; k < nb; k++) {
2123
templ2 = templates[k];
2124
for (l = 0; l < templ2->templNr; l++) {
2125
if (templ2->templCalledTab[l] == templ1) {
2126
sprintf(times_str,"%8.3f",(float)templ2->time/XSLT_TIMESTAMP_TICS_PER_SEC);
2127
sprintf(timec_str,"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC);
2128
sprintf(called_str,"%6d/%d",
2129
templ2->templCountTab[l], /* number of times 'this' calls callee */
2130
total); /* total number of calls from 'this' */
2131
fprintf(output, " %-8s %-8s %-12s %s [%d]\n",
2132
times_str,timec_str,called_str,
2133
templ2->name?(char *)templ2->name:pretty_templ_match(templ2),k);
2137
fprintf(output, "-----------------------------------------------\n");
2140
fprintf(output, "\f\nIndex by function name\n");
2141
for (i = 0; i < nb; i++) {
2142
templ1 = templates[i];
2143
fprintf(output, "[%d] %s (%s:%d)\n",
2144
i, templ1->name?(char *)templ1->name:pretty_templ_match(templ1),
2145
templ1->style->doc->URL,templ1->elem->line);
2148
fprintf(output, "\f\n");
2154
/************************************************************************
2156
* Fetching profiling informations *
2158
************************************************************************/
2161
* xsltGetProfileInformation:
2162
* @ctxt: a transformation context
2164
* This function should be called after the transformation completed
2165
* to extract template processing profiling informations if availble.
2166
* The informations are returned as an XML document tree like
2167
* <?xml version="1.0"?>
2169
* <template rank="1" match="*" name=""
2170
* mode="" calls="6" time="48" average="8"/>
2171
* <template rank="2" match="item2|item3" name=""
2172
* mode="" calls="10" time="30" average="3"/>
2173
* <template rank="3" match="item1" name=""
2174
* mode="" calls="5" time="17" average="3"/>
2176
* The caller will need to free up the returned tree with xmlFreeDoc()
2178
* Returns the xmlDocPtr corresponding to the result or NULL if not available.
2182
xsltGetProfileInformation(xsltTransformContextPtr ctxt)
2184
xmlDocPtr ret = NULL;
2185
xmlNodePtr root, child;
2188
xsltStylesheetPtr style;
2189
xsltTemplatePtr *templates;
2190
xsltTemplatePtr templ;
2191
int nb = 0, max = 0, i, j;
2202
(xsltTemplatePtr *) xmlMalloc(max * sizeof(xsltTemplatePtr));
2203
if (templates == NULL)
2207
* collect all the templates in an array
2209
style = ctxt->style;
2210
while (style != NULL) {
2211
templ = style->templates;
2212
while (templ != NULL) {
2216
if (templ->nbCalls > 0)
2217
templates[nb++] = templ;
2218
templ = templ->next;
2221
style = (xsltStylesheetPtr) xsltNextImport(style);
2225
* Sort the array by time spent
2227
for (i = 0; i < nb - 1; i++) {
2228
for (j = i + 1; j < nb; j++) {
2229
if ((templates[i]->time <= templates[j]->time) ||
2230
((templates[i]->time == templates[j]->time) &&
2231
(templates[i]->nbCalls <= templates[j]->nbCalls))) {
2232
templ = templates[j];
2233
templates[j] = templates[i];
2234
templates[i] = templ;
2240
* Generate a document corresponding to the results.
2242
ret = xmlNewDoc(BAD_CAST "1.0");
2243
root = xmlNewDocNode(ret, NULL, BAD_CAST "profile", NULL);
2244
xmlDocSetRootElement(ret, root);
2246
for (i = 0; i < nb; i++) {
2247
child = xmlNewChild(root, NULL, BAD_CAST "template", NULL);
2248
sprintf(buf, "%d", i + 1);
2249
xmlSetProp(child, BAD_CAST "rank", BAD_CAST buf);
2250
xmlSetProp(child, BAD_CAST "match", BAD_CAST templates[i]->match);
2251
xmlSetProp(child, BAD_CAST "name", BAD_CAST templates[i]->name);
2252
xmlSetProp(child, BAD_CAST "mode", BAD_CAST templates[i]->mode);
2254
sprintf(buf, "%d", templates[i]->nbCalls);
2255
xmlSetProp(child, BAD_CAST "calls", BAD_CAST buf);
2257
sprintf(buf, "%ld", templates[i]->time);
2258
xmlSetProp(child, BAD_CAST "time", BAD_CAST buf);
2260
sprintf(buf, "%ld", templates[i]->time / templates[i]->nbCalls);
2261
xmlSetProp(child, BAD_CAST "average", BAD_CAST buf);
2269
/************************************************************************
2271
* Hooks for libxml2 XPath *
2273
************************************************************************/
2276
* xsltXPathCompileFlags:
2277
* @style: the stylesheet
2278
* @str: the XPath expression
2279
* @flags: extra compilation flags to pass down to libxml2 XPath
2281
* Compile an XPath expression
2283
* Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2284
* the caller has to free the object.
2287
xsltXPathCompileFlags(xsltStylesheetPtr style, const xmlChar *str, int flags) {
2288
xmlXPathContextPtr xpathCtxt;
2289
xmlXPathCompExprPtr ret;
2291
if (style != NULL) {
2292
#ifdef XSLT_REFACTORED_XPATHCOMP
2293
if (XSLT_CCTXT(style)) {
2295
* Proposed by Jerome Pesenti
2296
* --------------------------
2297
* For better efficiency we'll reuse the compilation
2298
* context's XPath context. For the common stylesheet using
2299
* XPath expressions this will reduce compilation time to
2302
* See http://mail.gnome.org/archives/xslt/2006-April/msg00037.html
2304
xpathCtxt = XSLT_CCTXT(style)->xpathCtxt;
2305
xpathCtxt->doc = style->doc;
2307
xpathCtxt = xmlXPathNewContext(style->doc);
2309
xpathCtxt = xmlXPathNewContext(style->doc);
2311
if (xpathCtxt == NULL)
2313
xpathCtxt->dict = style->dict;
2315
xpathCtxt = xmlXPathNewContext(NULL);
2316
if (xpathCtxt == NULL)
2319
xpathCtxt->flags = flags;
2322
* Compile the expression.
2324
ret = xmlXPathCtxtCompile(xpathCtxt, str);
2326
#ifdef XSLT_REFACTORED_XPATHCOMP
2327
if ((style == NULL) || (! XSLT_CCTXT(style))) {
2328
xmlXPathFreeContext(xpathCtxt);
2331
xmlXPathFreeContext(xpathCtxt);
2334
* TODO: there is a lot of optimizations which should be possible
2335
* like variable slot precomputations, function precomputations, etc.
2343
* @style: the stylesheet
2344
* @str: the XPath expression
2346
* Compile an XPath expression
2348
* Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2349
* the caller has to free the object.
2352
xsltXPathCompile(xsltStylesheetPtr style, const xmlChar *str) {
2353
return(xsltXPathCompileFlags(style, str, 0));
2356
/************************************************************************
2358
* Hooks for the debugger *
2360
************************************************************************/
2363
* There is currently only 3 debugging callback defined
2364
* Debugger callbacks are disabled by default
2366
#define XSLT_CALLBACK_NUMBER 3
2368
typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks;
2369
typedef xsltDebuggerCallbacks *xsltDebuggerCallbacksPtr;
2370
struct _xsltDebuggerCallbacks {
2371
xsltHandleDebuggerCallback handler;
2372
xsltAddCallCallback add;
2373
xsltDropCallCallback drop;
2376
static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks = {
2385
* xsltSetDebuggerStatus:
2386
* @value : the value to be set
2388
* This function sets the value of xslDebugStatus.
2391
xsltSetDebuggerStatus(int value)
2393
xslDebugStatus = value;
2397
* xsltGetDebuggerStatus:
2399
* Get xslDebugStatus.
2401
* Returns the value of xslDebugStatus.
2404
xsltGetDebuggerStatus(void)
2406
return(xslDebugStatus);
2410
* xsltSetDebuggerCallbacks:
2411
* @no : number of callbacks
2412
* @block : the block of callbacks
2414
* This function allow to plug a debugger into the XSLT library
2415
* @block points to a block of memory containing the address of @no
2416
* callback routines.
2418
* Returns 0 in case of success and -1 in case of error
2421
xsltSetDebuggerCallbacks(int no, void *block)
2423
xsltDebuggerCallbacksPtr callbacks;
2425
if ((block == NULL) || (no != XSLT_CALLBACK_NUMBER))
2428
callbacks = (xsltDebuggerCallbacksPtr) block;
2429
xsltDebuggerCurrentCallbacks.handler = callbacks->handler;
2430
xsltDebuggerCurrentCallbacks.add = callbacks->add;
2431
xsltDebuggerCurrentCallbacks.drop = callbacks->drop;
2436
* xslHandleDebugger:
2437
* @cur : source node being executed
2438
* @node : data node being processed
2439
* @templ : temlate that applies to node
2440
* @ctxt : the xslt transform context
2442
* If either cur or node are a breakpoint, or xslDebugStatus in state
2443
* where debugging must occcur at this time then transfer control
2444
* to the xslDebugBreak function
2447
xslHandleDebugger(xmlNodePtr cur, xmlNodePtr node, xsltTemplatePtr templ,
2448
xsltTransformContextPtr ctxt)
2450
if (xsltDebuggerCurrentCallbacks.handler != NULL)
2451
xsltDebuggerCurrentCallbacks.handler(cur, node, templ, ctxt);
2456
* @templ : current template being applied
2457
* @source : the source node being processed
2459
* Add template "call" to call stack
2460
* Returns : 1 on sucess 0 otherwise an error may be printed if
2461
* WITH_XSLT_DEBUG_BREAKPOINTS is defined
2464
xslAddCall(xsltTemplatePtr templ, xmlNodePtr source)
2466
if (xsltDebuggerCurrentCallbacks.add != NULL)
2467
return(xsltDebuggerCurrentCallbacks.add(templ, source));
2474
* Drop the topmost item off the call stack
2479
if (xsltDebuggerCurrentCallbacks.drop != NULL)
2480
xsltDebuggerCurrentCallbacks.drop();