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();