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
22 #ifdef HAVE_SYS_TIME_H
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"
42 #include "transform.h"
44 /* gettimeofday on Windows ??? */
45 #if defined(WIN32) && !defined(__CYGWIN__)
48 #pragma comment(lib, "ws2_32.lib")
49 #define gettimeofday(p1,p2)
50 #define HAVE_GETTIMEOFDAY
51 #define XSLT_WIN32_PERFORMANCE_COUNTER
55 /************************************************************************
57 * Convenience function *
59 ************************************************************************/
63 * @style: the stylesheet
65 * @name: the attribute name
66 * @nameSpace: the URI of the namespace
68 * Similar to xmlGetNsProp() but with a slightly different semantic
70 * Search and get the value of an attribute associated to a node
71 * This attribute has to be anchored in the namespace specified,
72 * or has no namespace and the element is in that namespace.
74 * This does the entity substitution.
75 * This function looks in DTD attribute declaration for #FIXED or
76 * default declaration values unless DTD use has been turned off.
78 * Returns the attribute value or NULL if not found. The string is allocated
79 * in the stylesheet dictionary.
82 xsltGetCNsProp(xsltStylesheetPtr style
, xmlNodePtr node
,
83 const xmlChar
*name
, const xmlChar
*nameSpace
) {
90 if ((node
== NULL
) || (style
== NULL
) || (style
->dict
== NULL
))
93 prop
= node
->properties
;
94 if (nameSpace
== NULL
) {
95 return xmlGetProp(node
, name
);
97 while (prop
!= NULL
) {
100 * - same attribute names
101 * - and the attribute carrying that namespace
103 if ((xmlStrEqual(prop
->name
, name
)) &&
104 (((prop
->ns
== NULL
) && (node
->ns
!= NULL
) &&
105 (xmlStrEqual(node
->ns
->href
, nameSpace
))) ||
106 ((prop
->ns
!= NULL
) &&
107 (xmlStrEqual(prop
->ns
->href
, nameSpace
))))) {
109 tmp
= xmlNodeListGetString(node
->doc
, prop
->children
, 1);
111 ret
= xmlDictLookup(style
->dict
, BAD_CAST
"", 0);
113 ret
= xmlDictLookup(style
->dict
, tmp
, -1);
122 * Check if there is a default declaration in the internal
123 * or external subsets
127 if (doc
->intSubset
!= NULL
) {
128 xmlAttributePtr attrDecl
;
130 attrDecl
= xmlGetDtdAttrDesc(doc
->intSubset
, node
->name
, name
);
131 if ((attrDecl
== NULL
) && (doc
->extSubset
!= NULL
))
132 attrDecl
= xmlGetDtdAttrDesc(doc
->extSubset
, node
->name
, name
);
134 if ((attrDecl
!= NULL
) && (attrDecl
->prefix
!= NULL
)) {
136 * The DTD declaration only allows a prefix search
138 ns
= xmlSearchNs(doc
, node
, attrDecl
->prefix
);
139 if ((ns
!= NULL
) && (xmlStrEqual(ns
->href
, nameSpace
)))
140 return(xmlDictLookup(style
->dict
,
141 attrDecl
->defaultValue
, -1));
150 * @name: the attribute name
151 * @nameSpace: the URI of the namespace
153 * Similar to xmlGetNsProp() but with a slightly different semantic
155 * Search and get the value of an attribute associated to a node
156 * This attribute has to be anchored in the namespace specified,
157 * or has no namespace and the element is in that namespace.
159 * This does the entity substitution.
160 * This function looks in DTD attribute declaration for #FIXED or
161 * default declaration values unless DTD use has been turned off.
163 * Returns the attribute value or NULL if not found.
164 * It's up to the caller to free the memory.
167 xsltGetNsProp(xmlNodePtr node
, const xmlChar
*name
, const xmlChar
*nameSpace
) {
175 prop
= node
->properties
;
177 * TODO: Substitute xmlGetProp() for xmlGetNsProp(), since the former
178 * is not namespace-aware and will return an attribute with equal
179 * name regardless of its namespace.
181 * <xsl:element foo:name="myName"/>
182 * So this would return "myName" even if an attribute @name
183 * in the XSLT was requested.
185 if (nameSpace
== NULL
)
186 return(xmlGetProp(node
, name
));
187 while (prop
!= NULL
) {
190 * - same attribute names
191 * - and the attribute carrying that namespace
193 if ((xmlStrEqual(prop
->name
, name
)) &&
194 (((prop
->ns
== NULL
) && (node
->ns
!= NULL
) &&
195 (xmlStrEqual(node
->ns
->href
, nameSpace
))) ||
196 ((prop
->ns
!= NULL
) &&
197 (xmlStrEqual(prop
->ns
->href
, nameSpace
))))) {
200 ret
= xmlNodeListGetString(node
->doc
, prop
->children
, 1);
201 if (ret
== NULL
) return(xmlStrdup((xmlChar
*)""));
208 * Check if there is a default declaration in the internal
209 * or external subsets
213 if (doc
->intSubset
!= NULL
) {
214 xmlAttributePtr attrDecl
;
216 attrDecl
= xmlGetDtdAttrDesc(doc
->intSubset
, node
->name
, name
);
217 if ((attrDecl
== NULL
) && (doc
->extSubset
!= NULL
))
218 attrDecl
= xmlGetDtdAttrDesc(doc
->extSubset
, node
->name
, name
);
220 if ((attrDecl
!= NULL
) && (attrDecl
->prefix
!= NULL
)) {
222 * The DTD declaration only allows a prefix search
224 ns
= xmlSearchNs(doc
, node
, attrDecl
->prefix
);
225 if ((ns
!= NULL
) && (xmlStrEqual(ns
->href
, nameSpace
)))
226 return(xmlStrdup(attrDecl
->defaultValue
));
235 * @utf: a sequence of UTF-8 encoded bytes
236 * @len: a pointer to @bytes len
238 * Read one UTF8 Char from @utf
239 * Function copied from libxml2 xmlGetUTF8Char() ... to discard ultimately
240 * and use the original API
242 * Returns the char value or -1 in case of error and update @len with the
243 * number of bytes used
246 xsltGetUTF8Char(const unsigned char *utf
, int *len
) {
260 if ((utf
[1] & 0xc0) != 0x80)
262 if ((c
& 0xe0) == 0xe0) {
265 if ((utf
[2] & 0xc0) != 0x80)
267 if ((c
& 0xf0) == 0xf0) {
270 if ((c
& 0xf8) != 0xf0 || (utf
[3] & 0xc0) != 0x80)
274 c
= (utf
[0] & 0x7) << 18;
275 c
|= (utf
[1] & 0x3f) << 12;
276 c
|= (utf
[2] & 0x3f) << 6;
281 c
= (utf
[0] & 0xf) << 12;
282 c
|= (utf
[1] & 0x3f) << 6;
288 c
= (utf
[0] & 0x1f) << 6;
303 #ifdef XSLT_REFACTORED
306 * xsltPointerListAddSize:
307 * @list: the pointer list structure
308 * @item: the item to be stored
309 * @initialSize: the initial size of the list
311 * Adds an item to the list.
313 * Returns the position of the added item in the list or
314 * -1 in case of an error.
317 xsltPointerListAddSize(xsltPointerListPtr list
,
321 if (list
->items
== NULL
) {
322 if (initialSize
<= 0)
324 list
->items
= (void **) xmlMalloc(
325 initialSize
* sizeof(void *));
326 if (list
->items
== NULL
) {
327 xsltGenericError(xsltGenericErrorContext
,
328 "xsltPointerListAddSize: memory allocation failure.\n");
332 list
->size
= initialSize
;
333 } else if (list
->size
<= list
->number
) {
335 list
->items
= (void **) xmlRealloc(list
->items
,
336 list
->size
* sizeof(void *));
337 if (list
->items
== NULL
) {
338 xsltGenericError(xsltGenericErrorContext
,
339 "xsltPointerListAddSize: memory re-allocation failure.\n");
344 list
->items
[list
->number
++] = item
;
349 * xsltPointerListCreate:
350 * @initialSize: the initial size for the list
352 * Creates an xsltPointerList structure.
354 * Returns a xsltPointerList structure or NULL in case of an error.
357 xsltPointerListCreate(int initialSize
)
359 xsltPointerListPtr ret
;
361 ret
= xmlMalloc(sizeof(xsltPointerList
));
363 xsltGenericError(xsltGenericErrorContext
,
364 "xsltPointerListCreate: memory allocation failure.\n");
367 memset(ret
, 0, sizeof(xsltPointerList
));
368 if (initialSize
> 0) {
369 xsltPointerListAddSize(ret
, NULL
, initialSize
);
376 * xsltPointerListFree:
377 * @list: pointer to the list to be freed
379 * Frees the xsltPointerList structure. This does not free
380 * the content of the list.
383 xsltPointerListFree(xsltPointerListPtr list
)
387 if (list
->items
!= NULL
)
388 xmlFree(list
->items
);
393 * xsltPointerListClear:
394 * @list: pointer to the list to be cleared
396 * Resets the list, but does not free the allocated array
397 * and does not free the content of the list.
400 xsltPointerListClear(xsltPointerListPtr list
)
402 if (list
->items
!= NULL
) {
403 xmlFree(list
->items
);
410 #endif /* XSLT_REFACTORED */
412 /************************************************************************
414 * Handling of XSLT stylesheets messages *
416 ************************************************************************/
420 * @ctxt: an XSLT processing context
421 * @node: The current node
422 * @inst: The node containing the message instruction
424 * Process and xsl:message construct
427 xsltMessage(xsltTransformContextPtr ctxt
, xmlNodePtr node
, xmlNodePtr inst
) {
428 xmlGenericErrorFunc error
= xsltGenericError
;
429 void *errctx
= xsltGenericErrorContext
;
430 xmlChar
*prop
, *message
;
433 if ((ctxt
== NULL
) || (inst
== NULL
))
436 if (ctxt
->error
!= NULL
) {
438 errctx
= ctxt
->errctx
;
441 prop
= xmlGetNsProp(inst
, (const xmlChar
*)"terminate", NULL
);
443 if (xmlStrEqual(prop
, (const xmlChar
*)"yes")) {
445 } else if (xmlStrEqual(prop
, (const xmlChar
*)"no")) {
449 "xsl:message : terminate expecting 'yes' or 'no'\n");
450 ctxt
->state
= XSLT_STATE_ERROR
;
454 message
= xsltEvalTemplateString(ctxt
, node
, inst
);
455 if (message
!= NULL
) {
456 int len
= xmlStrlen(message
);
458 error(errctx
, "%s", (const char *)message
);
459 if ((len
> 0) && (message
[len
- 1] != '\n'))
464 ctxt
->state
= XSLT_STATE_STOPPED
;
467 /************************************************************************
469 * Handling of out of context errors *
471 ************************************************************************/
473 #define XSLT_GET_VAR_STR(msg, str) { \
479 str = (char *) xmlMalloc(150); \
485 while (size < 64000) { \
487 chars = vsnprintf(str, size, msg, ap); \
489 if ((chars > -1) && (chars < size)) \
495 if ((larger = (char *) xmlRealloc(str, size)) == NULL) {\
503 * xsltGenericErrorDefaultFunc:
504 * @ctx: an error context
505 * @msg: the message to display/transmit
506 * @...: extra parameters for the message display
508 * Default handler for out of context error messages.
511 xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED
, const char *msg
, ...) {
514 if (xsltGenericErrorContext
== NULL
)
515 xsltGenericErrorContext
= (void *) stderr
;
518 vfprintf((FILE *)xsltGenericErrorContext
, msg
, args
);
522 xmlGenericErrorFunc xsltGenericError
= xsltGenericErrorDefaultFunc
;
523 void *xsltGenericErrorContext
= NULL
;
527 * xsltSetGenericErrorFunc:
528 * @ctx: the new error handling context
529 * @handler: the new handler function
531 * Function to reset the handler and the error context for out of
532 * context error messages.
533 * This simply means that @handler will be called for subsequent
534 * error messages while not parsing nor validating. And @ctx will
535 * be passed as first argument to @handler
536 * One can simply force messages to be emitted to another FILE * than
537 * stderr by setting @ctx to this file handle and @handler to NULL.
540 xsltSetGenericErrorFunc(void *ctx
, xmlGenericErrorFunc handler
) {
541 xsltGenericErrorContext
= ctx
;
543 xsltGenericError
= handler
;
545 xsltGenericError
= xsltGenericErrorDefaultFunc
;
549 * xsltGenericDebugDefaultFunc:
550 * @ctx: an error context
551 * @msg: the message to display/transmit
552 * @...: extra parameters for the message display
554 * Default handler for out of context error messages.
557 xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED
, const char *msg
, ...) {
560 if (xsltGenericDebugContext
== NULL
)
564 vfprintf((FILE *)xsltGenericDebugContext
, msg
, args
);
568 xmlGenericErrorFunc xsltGenericDebug
= xsltGenericDebugDefaultFunc
;
569 void *xsltGenericDebugContext
= NULL
;
573 * xsltSetGenericDebugFunc:
574 * @ctx: the new error handling context
575 * @handler: the new handler function
577 * Function to reset the handler and the error context for out of
578 * context error messages.
579 * This simply means that @handler will be called for subsequent
580 * error messages while not parsing or validating. And @ctx will
581 * be passed as first argument to @handler
582 * One can simply force messages to be emitted to another FILE * than
583 * stderr by setting @ctx to this file handle and @handler to NULL.
586 xsltSetGenericDebugFunc(void *ctx
, xmlGenericErrorFunc handler
) {
587 xsltGenericDebugContext
= ctx
;
589 xsltGenericDebug
= handler
;
591 xsltGenericDebug
= xsltGenericDebugDefaultFunc
;
595 * xsltPrintErrorContext:
596 * @ctxt: the transformation context
597 * @style: the stylesheet
598 * @node: the current node being processed
600 * Display the context of an error.
603 xsltPrintErrorContext(xsltTransformContextPtr ctxt
,
604 xsltStylesheetPtr style
, xmlNodePtr node
) {
606 const xmlChar
*file
= NULL
;
607 const xmlChar
*name
= NULL
;
608 const char *type
= "error";
609 xmlGenericErrorFunc error
= xsltGenericError
;
610 void *errctx
= xsltGenericErrorContext
;
613 ctxt
->state
= XSLT_STATE_ERROR
;
614 if (ctxt
->error
!= NULL
) {
616 errctx
= ctxt
->errctx
;
619 if ((node
== NULL
) && (ctxt
!= NULL
))
623 if ((node
->type
== XML_DOCUMENT_NODE
) ||
624 (node
->type
== XML_HTML_DOCUMENT_NODE
)) {
625 xmlDocPtr doc
= (xmlDocPtr
) node
;
629 line
= xmlGetLineNo(node
);
630 if ((node
->doc
!= NULL
) && (node
->doc
->URL
!= NULL
))
631 file
= node
->doc
->URL
;
632 if (node
->name
!= NULL
)
638 type
= "runtime error";
639 else if (style
!= NULL
) {
640 #ifdef XSLT_REFACTORED
641 if (XSLT_CCTXT(style
)->errSeverity
== XSLT_ERROR_SEVERITY_WARNING
)
642 type
= "compilation warning";
644 type
= "compilation error";
646 type
= "compilation error";
650 if ((file
!= NULL
) && (line
!= 0) && (name
!= NULL
))
651 error(errctx
, "%s: file %s line %d element %s\n",
652 type
, file
, line
, name
);
653 else if ((file
!= NULL
) && (name
!= NULL
))
654 error(errctx
, "%s: file %s element %s\n", type
, file
, name
);
655 else if ((file
!= NULL
) && (line
!= 0))
656 error(errctx
, "%s: file %s line %d\n", type
, file
, line
);
657 else if (file
!= NULL
)
658 error(errctx
, "%s: file %s\n", type
, file
);
659 else if (name
!= NULL
)
660 error(errctx
, "%s: element %s\n", type
, name
);
662 error(errctx
, "%s\n", type
);
666 * xsltSetTransformErrorFunc:
667 * @ctxt: the XSLT transformation context
668 * @ctx: the new error handling context
669 * @handler: the new handler function
671 * Function to reset the handler and the error context for out of
672 * context error messages specific to a given XSLT transromation.
674 * This simply means that @handler will be called for subsequent
675 * error messages while running the transformation.
678 xsltSetTransformErrorFunc(xsltTransformContextPtr ctxt
,
679 void *ctx
, xmlGenericErrorFunc handler
)
681 ctxt
->error
= handler
;
686 * xsltTransformError:
687 * @ctxt: an XSLT transformation context
688 * @style: the XSLT stylesheet used
689 * @node: the current node in the stylesheet
690 * @msg: the message to display/transmit
691 * @...: extra parameters for the message display
693 * Display and format an error messages, gives file, line, position and
694 * extra parameters, will use the specific transformation context if available
697 xsltTransformError(xsltTransformContextPtr ctxt
,
698 xsltStylesheetPtr style
,
700 const char *msg
, ...) {
701 xmlGenericErrorFunc error
= xsltGenericError
;
702 void *errctx
= xsltGenericErrorContext
;
706 ctxt
->state
= XSLT_STATE_ERROR
;
707 if (ctxt
->error
!= NULL
) {
709 errctx
= ctxt
->errctx
;
712 if ((node
== NULL
) && (ctxt
!= NULL
))
714 xsltPrintErrorContext(ctxt
, style
, node
);
715 XSLT_GET_VAR_STR(msg
, str
);
716 error(errctx
, "%s", str
);
721 /************************************************************************
725 ************************************************************************/
729 * @dict: a dictionary
730 * @name: the full QName
731 * @prefix: the return value
733 * Split QNames into prefix and local names, both allocated from a dictionary.
735 * Returns: the localname or NULL in case of error.
738 xsltSplitQName(xmlDictPtr dict
, const xmlChar
*name
, const xmlChar
**prefix
) {
740 const xmlChar
*ret
= NULL
;
743 if ((name
== NULL
) || (dict
== NULL
)) return(NULL
);
745 return(xmlDictLookup(dict
, name
, -1));
746 while ((name
[len
] != 0) && (name
[len
] != ':')) len
++;
747 if (name
[len
] == 0) return(xmlDictLookup(dict
, name
, -1));
748 *prefix
= xmlDictLookup(dict
, name
, len
);
749 ret
= xmlDictLookup(dict
, &name
[len
+ 1], -1);
755 * @node: the node holding the QName
756 * @name: pointer to the initial QName value
758 * This function analyzes @name, if the name contains a prefix,
759 * the function seaches the associated namespace in scope for it.
760 * It will also replace @name value with the NCName, the old value being
762 * Errors in the prefix lookup are signalled by setting @name to NULL.
764 * NOTE: the namespace returned is a pointer to the place where it is
765 * defined and hence has the same lifespan as the document holding it.
767 * Returns the namespace URI if there is a prefix, or NULL if @name is
771 xsltGetQNameURI(xmlNodePtr node
, xmlChar
** name
)
780 if ((qname
== NULL
) || (*qname
== 0))
783 xsltGenericError(xsltGenericErrorContext
,
784 "QName: no element for namespace lookup %s\n",
791 /* nasty but valid */
796 * we are not trying to validate but just to cut, and yes it will
797 * work even if this is a set of UTF-8 encoded chars
799 while ((qname
[len
] != 0) && (qname
[len
] != ':'))
806 * handle xml: separately, this one is magical
808 if ((qname
[0] == 'x') && (qname
[1] == 'm') &&
809 (qname
[2] == 'l') && (qname
[3] == ':')) {
812 *name
= xmlStrdup(&qname
[4]);
814 return(XML_XML_NAMESPACE
);
818 ns
= xmlSearchNs(node
->doc
, node
, qname
);
820 xsltGenericError(xsltGenericErrorContext
,
821 "%s:%s : no namespace bound to prefix %s\n",
822 qname
, &qname
[len
+ 1], qname
);
827 *name
= xmlStrdup(&qname
[len
+ 1]);
834 * @style: stylesheet pointer
835 * @node: the node holding the QName
836 * @name: pointer to the initial QName value
838 * This function is similar to xsltGetQNameURI, but is used when
839 * @name is a dictionary entry.
841 * Returns the namespace URI if there is a prefix, or NULL if @name is
845 xsltGetQNameURI2(xsltStylesheetPtr style
, xmlNodePtr node
,
846 const xmlChar
**name
) {
853 qname
= (xmlChar
*)*name
;
854 if ((qname
== NULL
) || (*qname
== 0))
857 xsltGenericError(xsltGenericErrorContext
,
858 "QName: no element for namespace lookup %s\n",
865 * we are not trying to validate but just to cut, and yes it will
866 * work even if this is a set of UTF-8 encoded chars
868 while ((qname
[len
] != 0) && (qname
[len
] != ':'))
875 * handle xml: separately, this one is magical
877 if ((qname
[0] == 'x') && (qname
[1] == 'm') &&
878 (qname
[2] == 'l') && (qname
[3] == ':')) {
881 *name
= xmlDictLookup(style
->dict
, &qname
[4], -1);
882 return(XML_XML_NAMESPACE
);
885 qname
= xmlStrndup(*name
, len
);
886 ns
= xmlSearchNs(node
->doc
, node
, qname
);
889 xsltTransformError(NULL
, style
, node
,
890 "No namespace bound to prefix '%s'.\n",
894 xsltGenericError(xsltGenericErrorContext
,
895 "%s : no namespace bound to prefix %s\n",
902 *name
= xmlDictLookup(style
->dict
, (*name
)+len
+1, -1);
907 /************************************************************************
911 ************************************************************************/
914 * xsltDocumentSortFunction:
915 * @list: the node set
917 * reorder the current node list @list accordingly to the document order
918 * This function is slow, obsolete and should not be used anymore.
921 xsltDocumentSortFunction(xmlNodeSetPtr list
) {
931 /* TODO: sort is really not optimized, does it needs to ? */
932 for (i
= 0;i
< len
-1;i
++) {
933 for (j
= i
+ 1; j
< len
; j
++) {
934 tst
= xmlXPathCmpNodes(list
->nodeTab
[i
], list
->nodeTab
[j
]);
936 node
= list
->nodeTab
[i
];
937 list
->nodeTab
[i
] = list
->nodeTab
[j
];
938 list
->nodeTab
[j
] = node
;
945 * xsltComputeSortResult:
946 * @ctxt: a XSLT process context
949 * reorder the current node list accordingly to the set of sorting
950 * requirement provided by the array of nodes.
952 * Returns a ordered XPath nodeset or NULL in case of error.
955 xsltComputeSortResult(xsltTransformContextPtr ctxt
, xmlNodePtr sort
) {
956 #ifdef XSLT_REFACTORED
957 xsltStyleItemSortPtr comp
;
959 xsltStylePreCompPtr comp
;
961 xmlXPathObjectPtr
*results
= NULL
;
962 xmlNodeSetPtr list
= NULL
;
963 xmlXPathObjectPtr res
;
968 int oldPos
, oldSize
;
970 xmlNsPtr
*oldNamespaces
;
974 xsltGenericError(xsltGenericErrorContext
,
975 "xsl:sort : compilation failed\n");
979 if ((comp
->select
== NULL
) || (comp
->comp
== NULL
))
982 list
= ctxt
->nodeList
;
983 if ((list
== NULL
) || (list
->nodeNr
<= 1))
988 /* TODO: xsl:sort lang attribute */
989 /* TODO: xsl:sort case-order attribute */
992 results
= xmlMalloc(len
* sizeof(xmlXPathObjectPtr
));
993 if (results
== NULL
) {
994 xsltGenericError(xsltGenericErrorContext
,
995 "xsltComputeSortResult: memory allocation failure\n");
999 oldNode
= ctxt
->node
;
1000 oldInst
= ctxt
->inst
;
1001 oldPos
= ctxt
->xpathCtxt
->proximityPosition
;
1002 oldSize
= ctxt
->xpathCtxt
->contextSize
;
1003 oldNsNr
= ctxt
->xpathCtxt
->nsNr
;
1004 oldNamespaces
= ctxt
->xpathCtxt
->namespaces
;
1005 for (i
= 0;i
< len
;i
++) {
1007 ctxt
->xpathCtxt
->contextSize
= len
;
1008 ctxt
->xpathCtxt
->proximityPosition
= i
+ 1;
1009 ctxt
->node
= list
->nodeTab
[i
];
1010 ctxt
->xpathCtxt
->node
= ctxt
->node
;
1011 #ifdef XSLT_REFACTORED
1012 if (comp
->inScopeNs
!= NULL
) {
1013 ctxt
->xpathCtxt
->namespaces
= comp
->inScopeNs
->list
;
1014 ctxt
->xpathCtxt
->nsNr
= comp
->inScopeNs
->xpathNumber
;
1016 ctxt
->xpathCtxt
->namespaces
= NULL
;
1017 ctxt
->xpathCtxt
->nsNr
= 0;
1020 ctxt
->xpathCtxt
->namespaces
= comp
->nsList
;
1021 ctxt
->xpathCtxt
->nsNr
= comp
->nsNr
;
1023 res
= xmlXPathCompiledEval(comp
->comp
, ctxt
->xpathCtxt
);
1025 if (res
->type
!= XPATH_STRING
)
1026 res
= xmlXPathConvertString(res
);
1028 res
= xmlXPathConvertNumber(res
);
1029 res
->index
= i
; /* Save original pos for dupl resolv */
1031 if (res
->type
== XPATH_NUMBER
) {
1034 #ifdef WITH_XSLT_DEBUG_PROCESS
1035 xsltGenericDebug(xsltGenericDebugContext
,
1036 "xsltComputeSortResult: select didn't evaluate to a number\n");
1041 if (res
->type
== XPATH_STRING
) {
1042 if (comp
->locale
!= (xsltLocale
)0) {
1043 xmlChar
*str
= res
->stringval
;
1044 res
->stringval
= (xmlChar
*) xsltStrxfrm(comp
->locale
, str
);
1050 #ifdef WITH_XSLT_DEBUG_PROCESS
1051 xsltGenericDebug(xsltGenericDebugContext
,
1052 "xsltComputeSortResult: select didn't evaluate to a string\n");
1058 ctxt
->state
= XSLT_STATE_STOPPED
;
1062 ctxt
->node
= oldNode
;
1063 ctxt
->inst
= oldInst
;
1064 ctxt
->xpathCtxt
->contextSize
= oldSize
;
1065 ctxt
->xpathCtxt
->proximityPosition
= oldPos
;
1066 ctxt
->xpathCtxt
->nsNr
= oldNsNr
;
1067 ctxt
->xpathCtxt
->namespaces
= oldNamespaces
;
1073 * xsltDefaultSortFunction:
1074 * @ctxt: a XSLT process context
1075 * @sorts: array of sort nodes
1076 * @nbsorts: the number of sorts in the array
1078 * reorder the current node list accordingly to the set of sorting
1079 * requirement provided by the arry of nodes.
1082 xsltDefaultSortFunction(xsltTransformContextPtr ctxt
, xmlNodePtr
*sorts
,
1084 #ifdef XSLT_REFACTORED
1085 xsltStyleItemSortPtr comp
;
1087 xsltStylePreCompPtr comp
;
1089 xmlXPathObjectPtr
*resultsTab
[XSLT_MAX_SORT
];
1090 xmlXPathObjectPtr
*results
= NULL
, *res
;
1091 xmlNodeSetPtr list
= NULL
;
1092 int descending
, number
, desc
, numb
;
1098 xmlXPathObjectPtr tmp
;
1099 int tempstype
[XSLT_MAX_SORT
], temporder
[XSLT_MAX_SORT
];
1101 if ((ctxt
== NULL
) || (sorts
== NULL
) || (nbsorts
<= 0) ||
1102 (nbsorts
>= XSLT_MAX_SORT
))
1104 if (sorts
[0] == NULL
)
1106 comp
= sorts
[0]->psvi
;
1110 list
= ctxt
->nodeList
;
1111 if ((list
== NULL
) || (list
->nodeNr
<= 1))
1112 return; /* nothing to do */
1114 for (j
= 0; j
< nbsorts
; j
++) {
1115 comp
= sorts
[j
]->psvi
;
1117 if ((comp
->stype
== NULL
) && (comp
->has_stype
!= 0)) {
1119 xsltEvalAttrValueTemplate(ctxt
, sorts
[j
],
1120 (const xmlChar
*) "data-type",
1122 if (comp
->stype
!= NULL
) {
1124 if (xmlStrEqual(comp
->stype
, (const xmlChar
*) "text"))
1126 else if (xmlStrEqual(comp
->stype
, (const xmlChar
*) "number"))
1129 xsltTransformError(ctxt
, NULL
, sorts
[j
],
1130 "xsltDoSortFunction: no support for data-type = %s\n",
1132 comp
->number
= 0; /* use default */
1137 if ((comp
->order
== NULL
) && (comp
->has_order
!= 0)) {
1138 comp
->order
= xsltEvalAttrValueTemplate(ctxt
, sorts
[j
],
1139 (const xmlChar
*) "order",
1141 if (comp
->order
!= NULL
) {
1143 if (xmlStrEqual(comp
->order
, (const xmlChar
*) "ascending"))
1144 comp
->descending
= 0;
1145 else if (xmlStrEqual(comp
->order
,
1146 (const xmlChar
*) "descending"))
1147 comp
->descending
= 1;
1149 xsltTransformError(ctxt
, NULL
, sorts
[j
],
1150 "xsltDoSortFunction: invalid value %s for order\n",
1152 comp
->descending
= 0; /* use default */
1160 resultsTab
[0] = xsltComputeSortResult(ctxt
, sorts
[0]);
1161 for (i
= 1;i
< XSLT_MAX_SORT
;i
++)
1162 resultsTab
[i
] = NULL
;
1164 results
= resultsTab
[0];
1166 comp
= sorts
[0]->psvi
;
1167 descending
= comp
->descending
;
1168 number
= comp
->number
;
1169 if (results
== NULL
)
1172 /* Shell's sort of node-set */
1173 for (incr
= len
/ 2; incr
> 0; incr
/= 2) {
1174 for (i
= incr
; i
< len
; i
++) {
1176 if (results
[i
] == NULL
)
1180 if (results
[j
] == NULL
)
1184 /* We make NaN smaller than number in accordance
1186 if (xmlXPathIsNaN(results
[j
]->floatval
)) {
1187 if (xmlXPathIsNaN(results
[j
+ incr
]->floatval
))
1191 } else if (xmlXPathIsNaN(results
[j
+ incr
]->floatval
))
1193 else if (results
[j
]->floatval
==
1194 results
[j
+ incr
]->floatval
)
1196 else if (results
[j
]->floatval
>
1197 results
[j
+ incr
]->floatval
)
1200 } else if(comp
->locale
!= (xsltLocale
)0) {
1201 tst
= xsltLocaleStrcmp(
1203 (xsltLocaleChar
*) results
[j
]->stringval
,
1204 (xsltLocaleChar
*) results
[j
+ incr
]->stringval
);
1206 tst
= xmlStrcmp(results
[j
]->stringval
,
1207 results
[j
+ incr
]->stringval
);
1214 * Okay we need to use multi level sorts
1217 while (depth
< nbsorts
) {
1218 if (sorts
[depth
] == NULL
)
1220 comp
= sorts
[depth
]->psvi
;
1223 desc
= comp
->descending
;
1224 numb
= comp
->number
;
1227 * Compute the result of the next level for the
1228 * full set, this might be optimized ... or not
1230 if (resultsTab
[depth
] == NULL
)
1231 resultsTab
[depth
] = xsltComputeSortResult(ctxt
,
1233 res
= resultsTab
[depth
];
1236 if (res
[j
] == NULL
) {
1237 if (res
[j
+incr
] != NULL
)
1241 /* We make NaN smaller than number in
1242 accordance with XSLT spec */
1243 if (xmlXPathIsNaN(res
[j
]->floatval
)) {
1244 if (xmlXPathIsNaN(res
[j
+
1249 } else if (xmlXPathIsNaN(res
[j
+ incr
]->
1252 else if (res
[j
]->floatval
== res
[j
+ incr
]->
1255 else if (res
[j
]->floatval
>
1256 res
[j
+ incr
]->floatval
)
1259 } else if(comp
->locale
!= (xsltLocale
)0) {
1260 tst
= xsltLocaleStrcmp(
1262 (xsltLocaleChar
*) res
[j
]->stringval
,
1263 (xsltLocaleChar
*) res
[j
+ incr
]->stringval
);
1265 tst
= xmlStrcmp(res
[j
]->stringval
,
1266 res
[j
+ incr
]->stringval
);
1273 * if we still can't differenciate at this level
1274 * try one level deeper.
1282 tst
= results
[j
]->index
> results
[j
+ incr
]->index
;
1286 results
[j
] = results
[j
+ incr
];
1287 results
[j
+ incr
] = tmp
;
1288 node
= list
->nodeTab
[j
];
1289 list
->nodeTab
[j
] = list
->nodeTab
[j
+ incr
];
1290 list
->nodeTab
[j
+ incr
] = node
;
1292 while (depth
< nbsorts
) {
1293 if (sorts
[depth
] == NULL
)
1295 if (resultsTab
[depth
] == NULL
)
1297 res
= resultsTab
[depth
];
1299 res
[j
] = res
[j
+ incr
];
1300 res
[j
+ incr
] = tmp
;
1310 for (j
= 0; j
< nbsorts
; j
++) {
1311 comp
= sorts
[j
]->psvi
;
1312 if (tempstype
[j
] == 1) {
1313 /* The data-type needs to be recomputed each time */
1314 xmlFree((void *)(comp
->stype
));
1317 if (temporder
[j
] == 1) {
1318 /* The order needs to be recomputed each time */
1319 xmlFree((void *)(comp
->order
));
1322 if (resultsTab
[j
] != NULL
) {
1323 for (i
= 0;i
< len
;i
++)
1324 xmlXPathFreeObject(resultsTab
[j
][i
]);
1325 xmlFree(resultsTab
[j
]);
1331 static xsltSortFunc xsltSortFunction
= xsltDefaultSortFunction
;
1334 * xsltDoSortFunction:
1335 * @ctxt: a XSLT process context
1336 * @sorts: array of sort nodes
1337 * @nbsorts: the number of sorts in the array
1339 * reorder the current node list accordingly to the set of sorting
1340 * requirement provided by the arry of nodes.
1341 * This is a wrapper function, the actual function used is specified
1342 * using xsltSetCtxtSortFunc() to set the context specific sort function,
1343 * or xsltSetSortFunc() to set the global sort function.
1344 * If a sort function is set on the context, this will get called.
1345 * Otherwise the global sort function is called.
1348 xsltDoSortFunction(xsltTransformContextPtr ctxt
, xmlNodePtr
* sorts
,
1351 if (ctxt
->sortfunc
!= NULL
)
1352 (ctxt
->sortfunc
)(ctxt
, sorts
, nbsorts
);
1353 else if (xsltSortFunction
!= NULL
)
1354 xsltSortFunction(ctxt
, sorts
, nbsorts
);
1359 * @handler: the new handler function
1361 * Function to reset the global handler for XSLT sorting.
1362 * If the handler is NULL, the default sort function will be used.
1365 xsltSetSortFunc(xsltSortFunc handler
) {
1366 if (handler
!= NULL
)
1367 xsltSortFunction
= handler
;
1369 xsltSortFunction
= xsltDefaultSortFunction
;
1373 * xsltSetCtxtSortFunc:
1374 * @ctxt: a XSLT process context
1375 * @handler: the new handler function
1377 * Function to set the handler for XSLT sorting
1378 * for the specified context.
1379 * If the handler is NULL, then the global
1380 * sort function will be called
1383 xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt
, xsltSortFunc handler
) {
1384 ctxt
->sortfunc
= handler
;
1387 /************************************************************************
1391 ************************************************************************/
1394 * xsltSetCtxtParseOptions:
1395 * @ctxt: a XSLT process context
1396 * @options: a combination of libxml2 xmlParserOption
1398 * Change the default parser option passed by the XSLT engine to the
1399 * parser when using document() loading.
1401 * Returns the previous options or -1 in case of error
1404 xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt
, int options
)
1410 oldopts
= ctxt
->parserOptions
;
1412 oldopts
|= XML_PARSE_XINCLUDE
;
1413 ctxt
->parserOptions
= options
;
1414 if (options
& XML_PARSE_XINCLUDE
)
1421 /************************************************************************
1425 ************************************************************************/
1429 * @buf: an output buffer
1430 * @result: the result xmlDocPtr
1431 * @style: the stylesheet
1433 * Save the result @result obtained by applying the @style stylesheet
1434 * to an I/O output channel @buf
1436 * Returns the number of byte written or -1 in case of failure.
1439 xsltSaveResultTo(xmlOutputBufferPtr buf
, xmlDocPtr result
,
1440 xsltStylesheetPtr style
) {
1441 const xmlChar
*encoding
;
1443 const xmlChar
*method
;
1446 if ((buf
== NULL
) || (result
== NULL
) || (style
== NULL
))
1448 if ((result
->children
== NULL
) ||
1449 ((result
->children
->type
== XML_DTD_NODE
) &&
1450 (result
->children
->next
== NULL
)))
1453 if ((style
->methodURI
!= NULL
) &&
1454 ((style
->method
== NULL
) ||
1455 (!xmlStrEqual(style
->method
, (const xmlChar
*) "xhtml")))) {
1456 xsltGenericError(xsltGenericErrorContext
,
1457 "xsltSaveResultTo : unknown ouput method\n");
1461 base
= buf
->written
;
1463 XSLT_GET_IMPORT_PTR(method
, style
, method
)
1464 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1465 XSLT_GET_IMPORT_INT(indent
, style
, indent
);
1467 if ((method
== NULL
) && (result
->type
== XML_HTML_DOCUMENT_NODE
))
1468 method
= (const xmlChar
*) "html";
1470 if ((method
!= NULL
) &&
1471 (xmlStrEqual(method
, (const xmlChar
*) "html"))) {
1472 if (encoding
!= NULL
) {
1473 htmlSetMetaEncoding(result
, (const xmlChar
*) encoding
);
1475 htmlSetMetaEncoding(result
, (const xmlChar
*) "UTF-8");
1479 htmlDocContentDumpFormatOutput(buf
, result
, (const char *) encoding
,
1481 xmlOutputBufferFlush(buf
);
1482 } else if ((method
!= NULL
) &&
1483 (xmlStrEqual(method
, (const xmlChar
*) "xhtml"))) {
1484 if (encoding
!= NULL
) {
1485 htmlSetMetaEncoding(result
, (const xmlChar
*) encoding
);
1487 htmlSetMetaEncoding(result
, (const xmlChar
*) "UTF-8");
1489 htmlDocContentDumpOutput(buf
, result
, (const char *) encoding
);
1490 xmlOutputBufferFlush(buf
);
1491 } else if ((method
!= NULL
) &&
1492 (xmlStrEqual(method
, (const xmlChar
*) "text"))) {
1495 cur
= result
->children
;
1496 while (cur
!= NULL
) {
1497 if (cur
->type
== XML_TEXT_NODE
)
1498 xmlOutputBufferWriteString(buf
, (const char *) cur
->content
);
1503 if (cur
->children
!= NULL
) {
1504 if ((cur
->children
->type
!= XML_ENTITY_DECL
) &&
1505 (cur
->children
->type
!= XML_ENTITY_REF_NODE
) &&
1506 (cur
->children
->type
!= XML_ENTITY_NODE
)) {
1507 cur
= cur
->children
;
1511 if (cur
->next
!= NULL
) {
1520 if (cur
== (xmlNodePtr
) style
->doc
) {
1524 if (cur
->next
!= NULL
) {
1528 } while (cur
!= NULL
);
1530 xmlOutputBufferFlush(buf
);
1535 XSLT_GET_IMPORT_INT(omitXmlDecl
, style
, omitXmlDeclaration
);
1536 XSLT_GET_IMPORT_INT(standalone
, style
, standalone
);
1538 if (omitXmlDecl
!= 1) {
1539 xmlOutputBufferWriteString(buf
, "<?xml version=");
1540 if (result
->version
!= NULL
)
1541 xmlBufferWriteQuotedString(buf
->buffer
, result
->version
);
1543 xmlOutputBufferWriteString(buf
, "\"1.0\"");
1544 if (encoding
== NULL
) {
1545 if (result
->encoding
!= NULL
)
1546 encoding
= result
->encoding
;
1547 else if (result
->charset
!= XML_CHAR_ENCODING_UTF8
)
1548 encoding
= (const xmlChar
*)
1549 xmlGetCharEncodingName((xmlCharEncoding
)
1552 if (encoding
!= NULL
) {
1553 xmlOutputBufferWriteString(buf
, " encoding=");
1554 xmlBufferWriteQuotedString(buf
->buffer
, (xmlChar
*) encoding
);
1556 switch (standalone
) {
1558 xmlOutputBufferWriteString(buf
, " standalone=\"no\"");
1561 xmlOutputBufferWriteString(buf
, " standalone=\"yes\"");
1566 xmlOutputBufferWriteString(buf
, "?>\n");
1568 if (result
->children
!= NULL
) {
1569 xmlNodePtr child
= result
->children
;
1571 while (child
!= NULL
) {
1572 xmlNodeDumpOutput(buf
, result
, child
, 0, (indent
== 1),
1573 (const char *) encoding
);
1574 if ((child
->type
== XML_DTD_NODE
) ||
1575 ((child
->type
== XML_COMMENT_NODE
) &&
1576 (child
->next
!= NULL
)))
1577 xmlOutputBufferWriteString(buf
, "\n");
1578 child
= child
->next
;
1580 xmlOutputBufferWriteString(buf
, "\n");
1582 xmlOutputBufferFlush(buf
);
1584 return(buf
->written
- base
);
1588 * xsltSaveResultToFilename:
1589 * @URL: a filename or URL
1590 * @result: the result xmlDocPtr
1591 * @style: the stylesheet
1592 * @compression: the compression factor (0 - 9 included)
1594 * Save the result @result obtained by applying the @style stylesheet
1597 * Returns the number of byte written or -1 in case of failure.
1600 xsltSaveResultToFilename(const char *URL
, xmlDocPtr result
,
1601 xsltStylesheetPtr style
, int compression
) {
1602 xmlOutputBufferPtr buf
;
1603 const xmlChar
*encoding
;
1606 if ((URL
== NULL
) || (result
== NULL
) || (style
== NULL
))
1608 if (result
->children
== NULL
)
1611 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1612 if (encoding
!= NULL
) {
1613 xmlCharEncodingHandlerPtr encoder
;
1615 encoder
= xmlFindCharEncodingHandler((char *)encoding
);
1616 if ((encoder
!= NULL
) &&
1617 (xmlStrEqual((const xmlChar
*)encoder
->name
,
1618 (const xmlChar
*) "UTF-8")))
1620 buf
= xmlOutputBufferCreateFilename(URL
, encoder
, compression
);
1622 buf
= xmlOutputBufferCreateFilename(URL
, NULL
, compression
);
1626 xsltSaveResultTo(buf
, result
, style
);
1627 ret
= xmlOutputBufferClose(buf
);
1632 * xsltSaveResultToFile:
1633 * @file: a FILE * I/O
1634 * @result: the result xmlDocPtr
1635 * @style: the stylesheet
1637 * Save the result @result obtained by applying the @style stylesheet
1638 * to an open FILE * I/O.
1639 * This does not close the FILE @file
1641 * Returns the number of bytes written or -1 in case of failure.
1644 xsltSaveResultToFile(FILE *file
, xmlDocPtr result
, xsltStylesheetPtr style
) {
1645 xmlOutputBufferPtr buf
;
1646 const xmlChar
*encoding
;
1649 if ((file
== NULL
) || (result
== NULL
) || (style
== NULL
))
1651 if (result
->children
== NULL
)
1654 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1655 if (encoding
!= NULL
) {
1656 xmlCharEncodingHandlerPtr encoder
;
1658 encoder
= xmlFindCharEncodingHandler((char *)encoding
);
1659 if ((encoder
!= NULL
) &&
1660 (xmlStrEqual((const xmlChar
*)encoder
->name
,
1661 (const xmlChar
*) "UTF-8")))
1663 buf
= xmlOutputBufferCreateFile(file
, encoder
);
1665 buf
= xmlOutputBufferCreateFile(file
, NULL
);
1670 xsltSaveResultTo(buf
, result
, style
);
1671 ret
= xmlOutputBufferClose(buf
);
1676 * xsltSaveResultToFd:
1677 * @fd: a file descriptor
1678 * @result: the result xmlDocPtr
1679 * @style: the stylesheet
1681 * Save the result @result obtained by applying the @style stylesheet
1682 * to an open file descriptor
1683 * This does not close the descriptor.
1685 * Returns the number of bytes written or -1 in case of failure.
1688 xsltSaveResultToFd(int fd
, xmlDocPtr result
, xsltStylesheetPtr style
) {
1689 xmlOutputBufferPtr buf
;
1690 const xmlChar
*encoding
;
1693 if ((fd
< 0) || (result
== NULL
) || (style
== NULL
))
1695 if (result
->children
== NULL
)
1698 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1699 if (encoding
!= NULL
) {
1700 xmlCharEncodingHandlerPtr encoder
;
1702 encoder
= xmlFindCharEncodingHandler((char *)encoding
);
1703 if ((encoder
!= NULL
) &&
1704 (xmlStrEqual((const xmlChar
*)encoder
->name
,
1705 (const xmlChar
*) "UTF-8")))
1707 buf
= xmlOutputBufferCreateFd(fd
, encoder
);
1709 buf
= xmlOutputBufferCreateFd(fd
, NULL
);
1713 xsltSaveResultTo(buf
, result
, style
);
1714 ret
= xmlOutputBufferClose(buf
);
1719 * xsltSaveResultToString:
1720 * @doc_txt_ptr: Memory pointer for allocated XML text
1721 * @doc_txt_len: Length of the generated XML text
1722 * @result: the result xmlDocPtr
1723 * @style: the stylesheet
1725 * Save the result @result obtained by applying the @style stylesheet
1726 * to a new allocated string.
1728 * Returns 0 in case of success and -1 in case of error
1731 xsltSaveResultToString(xmlChar
**doc_txt_ptr
, int * doc_txt_len
,
1732 xmlDocPtr result
, xsltStylesheetPtr style
) {
1733 xmlOutputBufferPtr buf
;
1734 const xmlChar
*encoding
;
1736 *doc_txt_ptr
= NULL
;
1738 if (result
->children
== NULL
)
1741 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1742 if (encoding
!= NULL
) {
1743 xmlCharEncodingHandlerPtr encoder
;
1745 encoder
= xmlFindCharEncodingHandler((char *)encoding
);
1746 if ((encoder
!= NULL
) &&
1747 (xmlStrEqual((const xmlChar
*)encoder
->name
,
1748 (const xmlChar
*) "UTF-8")))
1750 buf
= xmlAllocOutputBuffer(encoder
);
1752 buf
= xmlAllocOutputBuffer(NULL
);
1756 xsltSaveResultTo(buf
, result
, style
);
1757 if (buf
->conv
!= NULL
) {
1758 *doc_txt_len
= buf
->conv
->use
;
1759 *doc_txt_ptr
= xmlStrndup(buf
->conv
->content
, *doc_txt_len
);
1761 *doc_txt_len
= buf
->buffer
->use
;
1762 *doc_txt_ptr
= xmlStrndup(buf
->buffer
->content
, *doc_txt_len
);
1764 (void)xmlOutputBufferClose(buf
);
1768 /************************************************************************
1770 * Generating profiling informations *
1772 ************************************************************************/
1774 static long calibration
= -1;
1777 * xsltCalibrateTimestamps:
1779 * Used for to calibrate the xsltTimestamp() function
1780 * Should work if launched at startup and we don't loose our quantum :-)
1782 * Returns the number of milliseconds used by xsltTimestamp()
1785 xsltCalibrateTimestamps(void) {
1788 for (i
= 0;i
< 999;i
++)
1790 return(xsltTimestamp() / 1000);
1794 * xsltCalibrateAdjust:
1795 * @delta: a negative dealy value found
1797 * Used for to correct the calibration for xsltTimestamp()
1800 xsltCalibrateAdjust(long delta
) {
1801 calibration
+= delta
;
1807 * Used for gathering profiling data
1809 * Returns the number of tenth of milliseconds since the beginning of the
1815 #ifdef XSLT_WIN32_PERFORMANCE_COUNTER
1817 LARGE_INTEGER performanceCount
;
1818 LARGE_INTEGER performanceFrequency
;
1821 static LONGLONG startupQuadCount
= 0;
1822 static LONGLONG startupQuadFreq
= 0;
1824 ok
= QueryPerformanceCounter(&performanceCount
);
1827 quadCount
= performanceCount
.QuadPart
;
1828 if (calibration
< 0) {
1830 ok
= QueryPerformanceFrequency(&performanceFrequency
);
1833 startupQuadFreq
= performanceFrequency
.QuadPart
;
1834 startupQuadCount
= quadCount
;
1837 if (startupQuadFreq
== 0)
1839 seconds
= (quadCount
- startupQuadCount
) / (double) startupQuadFreq
;
1840 return (long) (seconds
* XSLT_TIMESTAMP_TICS_PER_SEC
);
1842 #else /* XSLT_WIN32_PERFORMANCE_COUNTER */
1843 #ifdef HAVE_GETTIMEOFDAY
1844 static struct timeval startup
;
1848 if (calibration
< 0) {
1849 gettimeofday(&startup
, NULL
);
1851 calibration
= xsltCalibrateTimestamps();
1852 gettimeofday(&startup
, NULL
);
1856 gettimeofday(&cur
, NULL
);
1857 tics
= (cur
.tv_sec
- startup
.tv_sec
) * XSLT_TIMESTAMP_TICS_PER_SEC
;
1858 tics
+= (cur
.tv_usec
- startup
.tv_usec
) /
1859 (1000000l / XSLT_TIMESTAMP_TICS_PER_SEC
);
1861 tics
-= calibration
;
1865 /* Neither gettimeofday() nor Win32 performance counter available */
1869 #endif /* HAVE_GETTIMEOFDAY */
1870 #endif /* XSLT_WIN32_PERFORMANCE_COUNTER */
1873 #define MAX_TEMPLATES 10000
1876 * xsltSaveProfiling:
1877 * @ctxt: an XSLT context
1878 * @output: a FILE * for saving the informations
1880 * Save the profiling informations on @output
1883 xsltSaveProfiling(xsltTransformContextPtr ctxt
, FILE *output
) {
1888 xsltTemplatePtr
*templates
;
1889 xsltStylesheetPtr style
;
1890 xsltTemplatePtr
template;
1892 if ((output
== NULL
) || (ctxt
== NULL
))
1894 if (ctxt
->profile
== 0)
1898 max
= MAX_TEMPLATES
;
1899 templates
= xmlMalloc(max
* sizeof(xsltTemplatePtr
));
1900 if (templates
== NULL
)
1903 style
= ctxt
->style
;
1904 while (style
!= NULL
) {
1905 template = style
->templates
;
1906 while (template != NULL
) {
1910 if (template->nbCalls
> 0)
1911 templates
[nb
++] = template;
1912 template = template->next
;
1915 style
= xsltNextImport(style
);
1918 for (i
= 0;i
< nb
-1;i
++) {
1919 for (j
= i
+ 1; j
< nb
; j
++) {
1920 if ((templates
[i
]->time
<= templates
[j
]->time
) ||
1921 ((templates
[i
]->time
== templates
[j
]->time
) &&
1922 (templates
[i
]->nbCalls
<= templates
[j
]->nbCalls
))) {
1923 template = templates
[j
];
1924 templates
[j
] = templates
[i
];
1925 templates
[i
] = template;
1930 fprintf(output
, "%6s%20s%20s%10s Calls Tot 100us Avg\n\n",
1931 "number", "match", "name", "mode");
1934 for (i
= 0;i
< nb
;i
++) {
1935 fprintf(output
, "%5d ", i
);
1936 if (templates
[i
]->match
!= NULL
) {
1937 if (xmlStrlen(templates
[i
]->match
) > 20)
1938 fprintf(output
, "%s\n%26s", templates
[i
]->match
, "");
1940 fprintf(output
, "%20s", templates
[i
]->match
);
1942 fprintf(output
, "%20s", "");
1944 if (templates
[i
]->name
!= NULL
) {
1945 if (xmlStrlen(templates
[i
]->name
) > 20)
1946 fprintf(output
, "%s\n%46s", templates
[i
]->name
, "");
1948 fprintf(output
, "%20s", templates
[i
]->name
);
1950 fprintf(output
, "%20s", "");
1952 if (templates
[i
]->mode
!= NULL
) {
1953 if (xmlStrlen(templates
[i
]->mode
) > 10)
1954 fprintf(output
, "%s\n%56s", templates
[i
]->mode
, "");
1956 fprintf(output
, "%10s", templates
[i
]->mode
);
1958 fprintf(output
, "%10s", "");
1960 fprintf(output
, " %6d", templates
[i
]->nbCalls
);
1961 fprintf(output
, " %6ld %6ld\n", templates
[i
]->time
,
1962 templates
[i
]->time
/ templates
[i
]->nbCalls
);
1963 total
+= templates
[i
]->nbCalls
;
1964 totalt
+= templates
[i
]->time
;
1966 fprintf(output
, "\n%30s%26s %6d %6ld\n", "Total", "", total
, totalt
);
1971 /************************************************************************
1973 * Fetching profiling informations *
1975 ************************************************************************/
1978 * xsltGetProfileInformation:
1979 * @ctxt: a transformation context
1981 * This function should be called after the transformation completed
1982 * to extract template processing profiling informations if availble.
1983 * The informations are returned as an XML document tree like
1984 * <?xml version="1.0"?>
1986 * <template rank="1" match="*" name=""
1987 * mode="" calls="6" time="48" average="8"/>
1988 * <template rank="2" match="item2|item3" name=""
1989 * mode="" calls="10" time="30" average="3"/>
1990 * <template rank="3" match="item1" name=""
1991 * mode="" calls="5" time="17" average="3"/>
1993 * The caller will need to free up the returned tree with xmlFreeDoc()
1995 * Returns the xmlDocPtr corresponding to the result or NULL if not available.
1999 xsltGetProfileInformation(xsltTransformContextPtr ctxt
)
2001 xmlDocPtr ret
= NULL
;
2002 xmlNodePtr root
, child
;
2005 xsltStylesheetPtr style
;
2006 xsltTemplatePtr
*templates
;
2007 xsltTemplatePtr templ
;
2008 int nb
= 0, max
= 0, i
, j
;
2019 (xsltTemplatePtr
*) xmlMalloc(max
* sizeof(xsltTemplatePtr
));
2020 if (templates
== NULL
)
2024 * collect all the templates in an array
2026 style
= ctxt
->style
;
2027 while (style
!= NULL
) {
2028 templ
= style
->templates
;
2029 while (templ
!= NULL
) {
2033 if (templ
->nbCalls
> 0)
2034 templates
[nb
++] = templ
;
2035 templ
= templ
->next
;
2038 style
= (xsltStylesheetPtr
) xsltNextImport(style
);
2042 * Sort the array by time spent
2044 for (i
= 0; i
< nb
- 1; i
++) {
2045 for (j
= i
+ 1; j
< nb
; j
++) {
2046 if ((templates
[i
]->time
<= templates
[j
]->time
) ||
2047 ((templates
[i
]->time
== templates
[j
]->time
) &&
2048 (templates
[i
]->nbCalls
<= templates
[j
]->nbCalls
))) {
2049 templ
= templates
[j
];
2050 templates
[j
] = templates
[i
];
2051 templates
[i
] = templ
;
2057 * Generate a document corresponding to the results.
2059 ret
= xmlNewDoc(BAD_CAST
"1.0");
2060 root
= xmlNewDocNode(ret
, NULL
, BAD_CAST
"profile", NULL
);
2061 xmlDocSetRootElement(ret
, root
);
2063 for (i
= 0; i
< nb
; i
++) {
2064 child
= xmlNewChild(root
, NULL
, BAD_CAST
"template", NULL
);
2065 sprintf(buf
, "%d", i
+ 1);
2066 xmlSetProp(child
, BAD_CAST
"rank", BAD_CAST buf
);
2067 xmlSetProp(child
, BAD_CAST
"match", BAD_CAST templates
[i
]->match
);
2068 xmlSetProp(child
, BAD_CAST
"name", BAD_CAST templates
[i
]->name
);
2069 xmlSetProp(child
, BAD_CAST
"mode", BAD_CAST templates
[i
]->mode
);
2071 sprintf(buf
, "%d", templates
[i
]->nbCalls
);
2072 xmlSetProp(child
, BAD_CAST
"calls", BAD_CAST buf
);
2074 sprintf(buf
, "%ld", templates
[i
]->time
);
2075 xmlSetProp(child
, BAD_CAST
"time", BAD_CAST buf
);
2077 sprintf(buf
, "%ld", templates
[i
]->time
/ templates
[i
]->nbCalls
);
2078 xmlSetProp(child
, BAD_CAST
"average", BAD_CAST buf
);
2086 /************************************************************************
2088 * Hooks for libxml2 XPath *
2090 ************************************************************************/
2094 * @style: the stylesheet
2095 * @str: the XPath expression
2097 * Compile an XPath expression
2099 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2100 * the caller has to free the object.
2103 xsltXPathCompile(xsltStylesheetPtr style
, const xmlChar
*str
) {
2104 xmlXPathContextPtr xpathCtxt
;
2105 xmlXPathCompExprPtr ret
;
2107 if (style
!= NULL
) {
2108 #ifdef XSLT_REFACTORED_XPATHCOMP
2109 if (XSLT_CCTXT(style
)) {
2111 * Proposed by Jerome Pesenti
2112 * --------------------------
2113 * For better efficiency we'll reuse the compilation
2114 * context's XPath context. For the common stylesheet using
2115 * XPath expressions this will reduce compilation time to
2118 * See http://mail.gnome.org/archives/xslt/2006-April/msg00037.html
2120 xpathCtxt
= XSLT_CCTXT(style
)->xpathCtxt
;
2121 xpathCtxt
->doc
= style
->doc
;
2123 xpathCtxt
= xmlXPathNewContext(style
->doc
);
2125 xpathCtxt
= xmlXPathNewContext(style
->doc
);
2127 if (xpathCtxt
== NULL
)
2129 xpathCtxt
->dict
= style
->dict
;
2131 xpathCtxt
= xmlXPathNewContext(NULL
);
2132 if (xpathCtxt
== NULL
)
2136 * Compile the expression.
2138 ret
= xmlXPathCtxtCompile(xpathCtxt
, str
);
2140 #ifdef XSLT_REFACTORED_XPATHCOMP
2141 if ((style
== NULL
) || (! XSLT_CCTXT(style
))) {
2142 xmlXPathFreeContext(xpathCtxt
);
2145 xmlXPathFreeContext(xpathCtxt
);
2148 * TODO: there is a lot of optimizations which should be possible
2149 * like variable slot precomputations, function precomputations, etc.
2155 /************************************************************************
2157 * Hooks for the debugger *
2159 ************************************************************************/
2162 * There is currently only 3 debugging callback defined
2163 * Debugger callbacks are disabled by default
2165 #define XSLT_CALLBACK_NUMBER 3
2167 typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks
;
2168 typedef xsltDebuggerCallbacks
*xsltDebuggerCallbacksPtr
;
2169 struct _xsltDebuggerCallbacks
{
2170 xsltHandleDebuggerCallback handler
;
2171 xsltAddCallCallback add
;
2172 xsltDropCallCallback drop
;
2175 static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks
= {
2184 * xsltSetDebuggerStatus:
2185 * @value : the value to be set
2187 * This function sets the value of xslDebugStatus.
2190 xsltSetDebuggerStatus(int value
)
2192 xslDebugStatus
= value
;
2196 * xsltGetDebuggerStatus:
2198 * Get xslDebugStatus.
2200 * Returns the value of xslDebugStatus.
2203 xsltGetDebuggerStatus(void)
2205 return(xslDebugStatus
);
2209 * xsltSetDebuggerCallbacks:
2210 * @no : number of callbacks
2211 * @block : the block of callbacks
2213 * This function allow to plug a debugger into the XSLT library
2214 * @block points to a block of memory containing the address of @no
2215 * callback routines.
2217 * Returns 0 in case of success and -1 in case of error
2220 xsltSetDebuggerCallbacks(int no
, void *block
)
2222 xsltDebuggerCallbacksPtr callbacks
;
2224 if ((block
== NULL
) || (no
!= XSLT_CALLBACK_NUMBER
))
2227 callbacks
= (xsltDebuggerCallbacksPtr
) block
;
2228 xsltDebuggerCurrentCallbacks
.handler
= callbacks
->handler
;
2229 xsltDebuggerCurrentCallbacks
.add
= callbacks
->add
;
2230 xsltDebuggerCurrentCallbacks
.drop
= callbacks
->drop
;
2235 * xslHandleDebugger:
2236 * @cur : source node being executed
2237 * @node : data node being processed
2238 * @templ : temlate that applies to node
2239 * @ctxt : the xslt transform context
2241 * If either cur or node are a breakpoint, or xslDebugStatus in state
2242 * where debugging must occcur at this time then transfer control
2243 * to the xslDebugBreak function
2246 xslHandleDebugger(xmlNodePtr cur
, xmlNodePtr node
, xsltTemplatePtr templ
,
2247 xsltTransformContextPtr ctxt
)
2249 if (xsltDebuggerCurrentCallbacks
.handler
!= NULL
)
2250 xsltDebuggerCurrentCallbacks
.handler(cur
, node
, templ
, ctxt
);
2255 * @templ : current template being applied
2256 * @source : the source node being processed
2258 * Add template "call" to call stack
2259 * Returns : 1 on sucess 0 otherwise an error may be printed if
2260 * WITH_XSLT_DEBUG_BREAKPOINTS is defined
2263 xslAddCall(xsltTemplatePtr templ
, xmlNodePtr source
)
2265 if (xsltDebuggerCurrentCallbacks
.add
!= NULL
)
2266 return(xsltDebuggerCurrentCallbacks
.add(templ
, source
));
2273 * Drop the topmost item off the call stack
2278 if (xsltDebuggerCurrentCallbacks
.drop
!= NULL
)
2279 xsltDebuggerCurrentCallbacks
.drop();