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.
14 #ifdef HAVE_SYS_TIME_H
21 #if defined(_WIN32) && !defined(__CYGWIN__)
22 #define XSLT_WIN32_PERFORMANCE_COUNTER
25 /************************************************************************
27 * Convenience function *
29 ************************************************************************/
33 * @style: the stylesheet
35 * @name: the attribute name
36 * @nameSpace: the URI of the namespace
38 * Similar to xmlGetNsProp() but with a slightly different semantic
40 * Search and get the value of an attribute associated to a node
41 * This attribute has to be anchored in the namespace specified,
42 * or has no namespace and the element is in that namespace.
44 * This does the entity substitution.
45 * This function looks in DTD attribute declaration for #FIXED or
46 * default declaration values unless DTD use has been turned off.
48 * Returns the attribute value or NULL if not found. The string is allocated
49 * in the stylesheet dictionary.
52 xsltGetCNsProp(xsltStylesheetPtr style
, xmlNodePtr node
,
53 const xmlChar
*name
, const xmlChar
*nameSpace
) {
60 if ((node
== NULL
) || (style
== NULL
) || (style
->dict
== NULL
))
63 if (nameSpace
== NULL
)
64 return xmlGetProp(node
, name
);
66 if (node
->type
== XML_NAMESPACE_DECL
)
68 if (node
->type
== XML_ELEMENT_NODE
)
69 prop
= node
->properties
;
72 while (prop
!= NULL
) {
75 * - same attribute names
76 * - and the attribute carrying that namespace
78 if ((xmlStrEqual(prop
->name
, name
)) &&
79 (((prop
->ns
== NULL
) && (node
->ns
!= NULL
) &&
80 (xmlStrEqual(node
->ns
->href
, nameSpace
))) ||
81 ((prop
->ns
!= NULL
) &&
82 (xmlStrEqual(prop
->ns
->href
, nameSpace
))))) {
84 tmp
= xmlNodeListGetString(node
->doc
, prop
->children
, 1);
86 ret
= xmlDictLookup(style
->dict
, BAD_CAST
"", 0);
88 ret
= xmlDictLookup(style
->dict
, tmp
, -1);
97 * Check if there is a default declaration in the internal
102 if (doc
->intSubset
!= NULL
) {
103 xmlAttributePtr attrDecl
;
105 attrDecl
= xmlGetDtdAttrDesc(doc
->intSubset
, node
->name
, name
);
106 if ((attrDecl
== NULL
) && (doc
->extSubset
!= NULL
))
107 attrDecl
= xmlGetDtdAttrDesc(doc
->extSubset
, node
->name
, name
);
109 if ((attrDecl
!= NULL
) && (attrDecl
->prefix
!= NULL
)) {
111 * The DTD declaration only allows a prefix search
113 ns
= xmlSearchNs(doc
, node
, attrDecl
->prefix
);
114 if ((ns
!= NULL
) && (xmlStrEqual(ns
->href
, nameSpace
)))
115 return(xmlDictLookup(style
->dict
,
116 attrDecl
->defaultValue
, -1));
125 * @name: the attribute name
126 * @nameSpace: the URI of the namespace
128 * Similar to xmlGetNsProp() but with a slightly different semantic
130 * Search and get the value of an attribute associated to a node
131 * This attribute has to be anchored in the namespace specified,
132 * or has no namespace and the element is in that namespace.
134 * This does the entity substitution.
135 * This function looks in DTD attribute declaration for #FIXED or
136 * default declaration values unless DTD use has been turned off.
138 * Returns the attribute value or NULL if not found.
139 * It's up to the caller to free the memory.
142 xsltGetNsProp(xmlNodePtr node
, const xmlChar
*name
, const xmlChar
*nameSpace
) {
150 if (nameSpace
== NULL
)
151 return xmlGetProp(node
, name
);
153 if (node
->type
== XML_NAMESPACE_DECL
)
155 if (node
->type
== XML_ELEMENT_NODE
)
156 prop
= node
->properties
;
160 * TODO: Substitute xmlGetProp() for xmlGetNsProp(), since the former
161 * is not namespace-aware and will return an attribute with equal
162 * name regardless of its namespace.
164 * <xsl:element foo:name="myName"/>
165 * So this would return "myName" even if an attribute @name
166 * in the XSLT was requested.
168 while (prop
!= NULL
) {
171 * - same attribute names
172 * - and the attribute carrying that namespace
174 if ((xmlStrEqual(prop
->name
, name
)) &&
175 (((prop
->ns
== NULL
) && (node
->ns
!= NULL
) &&
176 (xmlStrEqual(node
->ns
->href
, nameSpace
))) ||
177 ((prop
->ns
!= NULL
) &&
178 (xmlStrEqual(prop
->ns
->href
, nameSpace
))))) {
181 ret
= xmlNodeListGetString(node
->doc
, prop
->children
, 1);
182 if (ret
== NULL
) return(xmlStrdup((xmlChar
*)""));
189 * Check if there is a default declaration in the internal
190 * or external subsets
194 if (doc
->intSubset
!= NULL
) {
195 xmlAttributePtr attrDecl
;
197 attrDecl
= xmlGetDtdAttrDesc(doc
->intSubset
, node
->name
, name
);
198 if ((attrDecl
== NULL
) && (doc
->extSubset
!= NULL
))
199 attrDecl
= xmlGetDtdAttrDesc(doc
->extSubset
, node
->name
, name
);
201 if ((attrDecl
!= NULL
) && (attrDecl
->prefix
!= NULL
)) {
203 * The DTD declaration only allows a prefix search
205 ns
= xmlSearchNs(doc
, node
, attrDecl
->prefix
);
206 if ((ns
!= NULL
) && (xmlStrEqual(ns
->href
, nameSpace
)))
207 return(xmlStrdup(attrDecl
->defaultValue
));
216 * @utf: a sequence of UTF-8 encoded bytes
217 * @len: a pointer to @bytes len
219 * Read one UTF8 Char from @utf
220 * Function copied from libxml2 xmlGetUTF8Char() ... to discard ultimately
221 * and use the original API
223 * Returns the char value or -1 in case of error and update @len with the
224 * number of bytes used
227 xsltGetUTF8Char(const unsigned char *utf
, int *len
) {
241 if ((utf
[1] & 0xc0) != 0x80)
243 if ((c
& 0xe0) == 0xe0) {
246 if ((utf
[2] & 0xc0) != 0x80)
248 if ((c
& 0xf0) == 0xf0) {
251 if ((c
& 0xf8) != 0xf0 || (utf
[3] & 0xc0) != 0x80)
255 c
= (utf
[0] & 0x7) << 18;
256 c
|= (utf
[1] & 0x3f) << 12;
257 c
|= (utf
[2] & 0x3f) << 6;
262 c
= (utf
[0] & 0xf) << 12;
263 c
|= (utf
[1] & 0x3f) << 6;
269 c
= (utf
[0] & 0x1f) << 6;
284 #ifdef XSLT_REFACTORED
287 * xsltPointerListAddSize:
288 * @list: the pointer list structure
289 * @item: the item to be stored
290 * @initialSize: the initial size of the list
292 * Adds an item to the list.
294 * Returns the position of the added item in the list or
295 * -1 in case of an error.
298 xsltPointerListAddSize(xsltPointerListPtr list
,
302 if (list
->items
== NULL
) {
303 if (initialSize
<= 0)
305 list
->items
= (void **) xmlMalloc(
306 initialSize
* sizeof(void *));
307 if (list
->items
== NULL
) {
308 xsltGenericError(xsltGenericErrorContext
,
309 "xsltPointerListAddSize: memory allocation failure.\n");
313 list
->size
= initialSize
;
314 } else if (list
->size
<= list
->number
) {
316 list
->items
= (void **) xmlRealloc(list
->items
,
317 list
->size
* sizeof(void *));
318 if (list
->items
== NULL
) {
319 xsltGenericError(xsltGenericErrorContext
,
320 "xsltPointerListAddSize: memory re-allocation failure.\n");
325 list
->items
[list
->number
++] = item
;
330 * xsltPointerListCreate:
331 * @initialSize: the initial size for the list
333 * Creates an xsltPointerList structure.
335 * Returns a xsltPointerList structure or NULL in case of an error.
338 xsltPointerListCreate(int initialSize
)
340 xsltPointerListPtr ret
;
342 ret
= xmlMalloc(sizeof(xsltPointerList
));
344 xsltGenericError(xsltGenericErrorContext
,
345 "xsltPointerListCreate: memory allocation failure.\n");
348 memset(ret
, 0, sizeof(xsltPointerList
));
349 if (initialSize
> 0) {
350 xsltPointerListAddSize(ret
, NULL
, initialSize
);
357 * xsltPointerListFree:
358 * @list: pointer to the list to be freed
360 * Frees the xsltPointerList structure. This does not free
361 * the content of the list.
364 xsltPointerListFree(xsltPointerListPtr list
)
368 if (list
->items
!= NULL
)
369 xmlFree(list
->items
);
374 * xsltPointerListClear:
375 * @list: pointer to the list to be cleared
377 * Resets the list, but does not free the allocated array
378 * and does not free the content of the list.
381 xsltPointerListClear(xsltPointerListPtr list
)
383 if (list
->items
!= NULL
) {
384 xmlFree(list
->items
);
391 #endif /* XSLT_REFACTORED */
393 /************************************************************************
395 * Handling of XSLT stylesheets messages *
397 ************************************************************************/
401 * @ctxt: an XSLT processing context
402 * @node: The current node
403 * @inst: The node containing the message instruction
405 * Process and xsl:message construct
408 xsltMessage(xsltTransformContextPtr ctxt
, xmlNodePtr node
, xmlNodePtr inst
) {
409 xmlGenericErrorFunc error
= xsltGenericError
;
410 void *errctx
= xsltGenericErrorContext
;
411 xmlChar
*prop
, *message
;
414 if ((ctxt
== NULL
) || (inst
== NULL
))
417 if (ctxt
->error
!= NULL
) {
419 errctx
= ctxt
->errctx
;
422 prop
= xmlGetNsProp(inst
, (const xmlChar
*)"terminate", NULL
);
424 if (xmlStrEqual(prop
, (const xmlChar
*)"yes")) {
426 } else if (xmlStrEqual(prop
, (const xmlChar
*)"no")) {
429 xsltTransformError(ctxt
, NULL
, inst
,
430 "xsl:message : terminate expecting 'yes' or 'no'\n");
434 message
= xsltEvalTemplateString(ctxt
, node
, inst
);
435 if (message
!= NULL
) {
436 int len
= xmlStrlen(message
);
438 error(errctx
, "%s", (const char *)message
);
439 if ((len
> 0) && (message
[len
- 1] != '\n'))
444 ctxt
->state
= XSLT_STATE_STOPPED
;
447 /************************************************************************
449 * Handling of out of context errors *
451 ************************************************************************/
453 #define XSLT_GET_VAR_STR(msg, str) { \
459 str = (char *) xmlMalloc(150); \
465 while (size < 64000) { \
467 chars = vsnprintf(str, size, msg, ap); \
469 if ((chars > -1) && (chars < size)) \
475 if ((larger = (char *) xmlRealloc(str, size)) == NULL) {\
483 * xsltGenericErrorDefaultFunc:
484 * @ctx: an error context
485 * @msg: the message to display/transmit
486 * @...: extra parameters for the message display
488 * Default handler for out of context error messages.
490 static void LIBXSLT_ATTR_FORMAT(2,3)
491 xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED
, const char *msg
, ...) {
494 if (xsltGenericErrorContext
== NULL
)
495 xsltGenericErrorContext
= (void *) stderr
;
498 vfprintf((FILE *)xsltGenericErrorContext
, msg
, args
);
502 xmlGenericErrorFunc xsltGenericError
= xsltGenericErrorDefaultFunc
;
503 void *xsltGenericErrorContext
= NULL
;
507 * xsltSetGenericErrorFunc:
508 * @ctx: the new error handling context
509 * @handler: the new handler function
511 * Function to reset the handler and the error context for out of
512 * context error messages.
513 * This simply means that @handler will be called for subsequent
514 * error messages while not parsing nor validating. And @ctx will
515 * be passed as first argument to @handler
516 * One can simply force messages to be emitted to another FILE * than
517 * stderr by setting @ctx to this file handle and @handler to NULL.
520 xsltSetGenericErrorFunc(void *ctx
, xmlGenericErrorFunc handler
) {
521 xsltGenericErrorContext
= ctx
;
523 xsltGenericError
= handler
;
525 xsltGenericError
= xsltGenericErrorDefaultFunc
;
529 * xsltGenericDebugDefaultFunc:
530 * @ctx: an error context
531 * @msg: the message to display/transmit
532 * @...: extra parameters for the message display
534 * Default handler for out of context error messages.
536 static void LIBXSLT_ATTR_FORMAT(2,3)
537 xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED
, const char *msg
, ...) {
540 if (xsltGenericDebugContext
== NULL
)
544 vfprintf((FILE *)xsltGenericDebugContext
, msg
, args
);
548 xmlGenericErrorFunc xsltGenericDebug
= xsltGenericDebugDefaultFunc
;
549 void *xsltGenericDebugContext
= NULL
;
553 * xsltSetGenericDebugFunc:
554 * @ctx: the new error handling context
555 * @handler: the new handler function
557 * Function to reset the handler and the error context for out of
558 * context error messages.
559 * This simply means that @handler will be called for subsequent
560 * error messages while not parsing or validating. And @ctx will
561 * be passed as first argument to @handler
562 * One can simply force messages to be emitted to another FILE * than
563 * stderr by setting @ctx to this file handle and @handler to NULL.
566 xsltSetGenericDebugFunc(void *ctx
, xmlGenericErrorFunc handler
) {
567 xsltGenericDebugContext
= ctx
;
569 xsltGenericDebug
= handler
;
571 xsltGenericDebug
= xsltGenericDebugDefaultFunc
;
575 * xsltPrintErrorContext:
576 * @ctxt: the transformation context
577 * @style: the stylesheet
578 * @node: the current node being processed
580 * Display the context of an error.
583 xsltPrintErrorContext(xsltTransformContextPtr ctxt
,
584 xsltStylesheetPtr style
, xmlNodePtr node
) {
586 const xmlChar
*file
= NULL
;
587 const xmlChar
*name
= NULL
;
588 const char *type
= "error";
589 xmlGenericErrorFunc error
= xsltGenericError
;
590 void *errctx
= xsltGenericErrorContext
;
593 if (ctxt
->state
== XSLT_STATE_OK
)
594 ctxt
->state
= XSLT_STATE_ERROR
;
595 if (ctxt
->error
!= NULL
) {
597 errctx
= ctxt
->errctx
;
600 if ((node
== NULL
) && (ctxt
!= NULL
))
604 if ((node
->type
== XML_DOCUMENT_NODE
) ||
605 (node
->type
== XML_HTML_DOCUMENT_NODE
)) {
606 xmlDocPtr doc
= (xmlDocPtr
) node
;
610 line
= xmlGetLineNo(node
);
611 if ((node
->doc
!= NULL
) && (node
->doc
->URL
!= NULL
))
612 file
= node
->doc
->URL
;
613 if (node
->name
!= NULL
)
619 type
= "runtime error";
620 else if (style
!= NULL
) {
621 #ifdef XSLT_REFACTORED
622 if (XSLT_CCTXT(style
)->errSeverity
== XSLT_ERROR_SEVERITY_WARNING
)
623 type
= "compilation warning";
625 type
= "compilation error";
627 type
= "compilation error";
631 if ((file
!= NULL
) && (line
!= 0) && (name
!= NULL
))
632 error(errctx
, "%s: file %s line %d element %s\n",
633 type
, file
, line
, name
);
634 else if ((file
!= NULL
) && (name
!= NULL
))
635 error(errctx
, "%s: file %s element %s\n", type
, file
, name
);
636 else if ((file
!= NULL
) && (line
!= 0))
637 error(errctx
, "%s: file %s line %d\n", type
, file
, line
);
638 else if (file
!= NULL
)
639 error(errctx
, "%s: file %s\n", type
, file
);
640 else if (name
!= NULL
)
641 error(errctx
, "%s: element %s\n", type
, name
);
643 error(errctx
, "%s\n", type
);
647 * xsltSetTransformErrorFunc:
648 * @ctxt: the XSLT transformation context
649 * @ctx: the new error handling context
650 * @handler: the new handler function
652 * Function to reset the handler and the error context for out of
653 * context error messages specific to a given XSLT transromation.
655 * This simply means that @handler will be called for subsequent
656 * error messages while running the transformation.
659 xsltSetTransformErrorFunc(xsltTransformContextPtr ctxt
,
660 void *ctx
, xmlGenericErrorFunc handler
)
662 ctxt
->error
= handler
;
667 * xsltTransformError:
668 * @ctxt: an XSLT transformation context
669 * @style: the XSLT stylesheet used
670 * @node: the current node in the stylesheet
671 * @msg: the message to display/transmit
672 * @...: extra parameters for the message display
674 * Display and format an error messages, gives file, line, position and
675 * extra parameters, will use the specific transformation context if available
678 xsltTransformError(xsltTransformContextPtr ctxt
,
679 xsltStylesheetPtr style
,
681 const char *msg
, ...) {
682 xmlGenericErrorFunc error
= xsltGenericError
;
683 void *errctx
= xsltGenericErrorContext
;
687 if (ctxt
->state
== XSLT_STATE_OK
)
688 ctxt
->state
= XSLT_STATE_ERROR
;
689 if (ctxt
->error
!= NULL
) {
691 errctx
= ctxt
->errctx
;
694 if ((node
== NULL
) && (ctxt
!= NULL
))
696 xsltPrintErrorContext(ctxt
, style
, node
);
697 XSLT_GET_VAR_STR(msg
, str
);
698 error(errctx
, "%s", str
);
703 /************************************************************************
707 ************************************************************************/
711 * @dict: a dictionary
712 * @name: the full QName
713 * @prefix: the return value
715 * Split QNames into prefix and local names, both allocated from a dictionary.
717 * Returns: the localname or NULL in case of error.
720 xsltSplitQName(xmlDictPtr dict
, const xmlChar
*name
, const xmlChar
**prefix
) {
722 const xmlChar
*ret
= NULL
;
725 if ((name
== NULL
) || (dict
== NULL
)) return(NULL
);
727 return(xmlDictLookup(dict
, name
, -1));
728 while ((name
[len
] != 0) && (name
[len
] != ':')) len
++;
729 if (name
[len
] == 0) return(xmlDictLookup(dict
, name
, -1));
730 *prefix
= xmlDictLookup(dict
, name
, len
);
731 ret
= xmlDictLookup(dict
, &name
[len
+ 1], -1);
737 * @node: the node holding the QName
738 * @name: pointer to the initial QName value
740 * This function analyzes @name, if the name contains a prefix,
741 * the function seaches the associated namespace in scope for it.
742 * It will also replace @name value with the NCName, the old value being
744 * Errors in the prefix lookup are signalled by setting @name to NULL.
746 * NOTE: the namespace returned is a pointer to the place where it is
747 * defined and hence has the same lifespan as the document holding it.
749 * Returns the namespace URI if there is a prefix, or NULL if @name is
753 xsltGetQNameURI(xmlNodePtr node
, xmlChar
** name
)
762 if ((qname
== NULL
) || (*qname
== 0))
765 xsltGenericError(xsltGenericErrorContext
,
766 "QName: no element for namespace lookup %s\n",
773 /* nasty but valid */
778 * we are not trying to validate but just to cut, and yes it will
779 * work even if this is a set of UTF-8 encoded chars
781 while ((qname
[len
] != 0) && (qname
[len
] != ':'))
788 * handle xml: separately, this one is magical
790 if ((qname
[0] == 'x') && (qname
[1] == 'm') &&
791 (qname
[2] == 'l') && (qname
[3] == ':')) {
794 *name
= xmlStrdup(&qname
[4]);
796 return(XML_XML_NAMESPACE
);
800 ns
= xmlSearchNs(node
->doc
, node
, qname
);
802 xsltGenericError(xsltGenericErrorContext
,
803 "%s:%s : no namespace bound to prefix %s\n",
804 qname
, &qname
[len
+ 1], qname
);
809 *name
= xmlStrdup(&qname
[len
+ 1]);
816 * @style: stylesheet pointer
817 * @node: the node holding the QName
818 * @name: pointer to the initial QName value
820 * This function is similar to xsltGetQNameURI, but is used when
821 * @name is a dictionary entry.
823 * Returns the namespace URI if there is a prefix, or NULL if @name is
827 xsltGetQNameURI2(xsltStylesheetPtr style
, xmlNodePtr node
,
828 const xmlChar
**name
) {
835 qname
= (xmlChar
*)*name
;
836 if ((qname
== NULL
) || (*qname
== 0))
839 xsltGenericError(xsltGenericErrorContext
,
840 "QName: no element for namespace lookup %s\n",
847 * we are not trying to validate but just to cut, and yes it will
848 * work even if this is a set of UTF-8 encoded chars
850 while ((qname
[len
] != 0) && (qname
[len
] != ':'))
857 * handle xml: separately, this one is magical
859 if ((qname
[0] == 'x') && (qname
[1] == 'm') &&
860 (qname
[2] == 'l') && (qname
[3] == ':')) {
863 *name
= xmlDictLookup(style
->dict
, &qname
[4], -1);
864 return(XML_XML_NAMESPACE
);
867 qname
= xmlStrndup(*name
, len
);
868 ns
= xmlSearchNs(node
->doc
, node
, qname
);
871 xsltTransformError(NULL
, style
, node
,
872 "No namespace bound to prefix '%s'.\n",
876 xsltGenericError(xsltGenericErrorContext
,
877 "%s : no namespace bound to prefix %s\n",
884 *name
= xmlDictLookup(style
->dict
, (*name
)+len
+1, -1);
889 /************************************************************************
893 ************************************************************************/
896 * xsltDocumentSortFunction:
897 * @list: the node set
899 * reorder the current node list @list accordingly to the document order
900 * This function is slow, obsolete and should not be used anymore.
903 xsltDocumentSortFunction(xmlNodeSetPtr list
) {
913 /* TODO: sort is really not optimized, does it needs to ? */
914 for (i
= 0;i
< len
-1;i
++) {
915 for (j
= i
+ 1; j
< len
; j
++) {
916 tst
= xmlXPathCmpNodes(list
->nodeTab
[i
], list
->nodeTab
[j
]);
918 node
= list
->nodeTab
[i
];
919 list
->nodeTab
[i
] = list
->nodeTab
[j
];
920 list
->nodeTab
[j
] = node
;
927 * xsltComputeSortResult:
928 * @ctxt: a XSLT process context
931 * reorder the current node list accordingly to the set of sorting
932 * requirement provided by the array of nodes.
934 * Returns a ordered XPath nodeset or NULL in case of error.
937 xsltComputeSortResult(xsltTransformContextPtr ctxt
, xmlNodePtr sort
) {
938 #ifdef XSLT_REFACTORED
939 xsltStyleItemSortPtr comp
;
941 xsltStylePreCompPtr comp
;
943 xmlXPathObjectPtr
*results
= NULL
;
944 xmlNodeSetPtr list
= NULL
;
945 xmlXPathObjectPtr res
;
950 int oldPos
, oldSize
;
952 xmlNsPtr
*oldNamespaces
;
956 xsltGenericError(xsltGenericErrorContext
,
957 "xsl:sort : compilation failed\n");
961 if ((comp
->select
== NULL
) || (comp
->comp
== NULL
))
964 list
= ctxt
->nodeList
;
965 if ((list
== NULL
) || (list
->nodeNr
<= 1))
970 /* TODO: xsl:sort lang attribute */
971 /* TODO: xsl:sort case-order attribute */
974 results
= xmlMalloc(len
* sizeof(xmlXPathObjectPtr
));
975 if (results
== NULL
) {
976 xsltGenericError(xsltGenericErrorContext
,
977 "xsltComputeSortResult: memory allocation failure\n");
981 oldNode
= ctxt
->node
;
982 oldInst
= ctxt
->inst
;
983 oldPos
= ctxt
->xpathCtxt
->proximityPosition
;
984 oldSize
= ctxt
->xpathCtxt
->contextSize
;
985 oldNsNr
= ctxt
->xpathCtxt
->nsNr
;
986 oldNamespaces
= ctxt
->xpathCtxt
->namespaces
;
987 for (i
= 0;i
< len
;i
++) {
989 ctxt
->xpathCtxt
->contextSize
= len
;
990 ctxt
->xpathCtxt
->proximityPosition
= i
+ 1;
991 ctxt
->node
= list
->nodeTab
[i
];
992 ctxt
->xpathCtxt
->node
= ctxt
->node
;
993 #ifdef XSLT_REFACTORED
994 if (comp
->inScopeNs
!= NULL
) {
995 ctxt
->xpathCtxt
->namespaces
= comp
->inScopeNs
->list
;
996 ctxt
->xpathCtxt
->nsNr
= comp
->inScopeNs
->xpathNumber
;
998 ctxt
->xpathCtxt
->namespaces
= NULL
;
999 ctxt
->xpathCtxt
->nsNr
= 0;
1002 ctxt
->xpathCtxt
->namespaces
= comp
->nsList
;
1003 ctxt
->xpathCtxt
->nsNr
= comp
->nsNr
;
1005 res
= xmlXPathCompiledEval(comp
->comp
, ctxt
->xpathCtxt
);
1007 if (res
->type
!= XPATH_STRING
)
1008 res
= xmlXPathConvertString(res
);
1010 res
= xmlXPathConvertNumber(res
);
1011 res
->index
= i
; /* Save original pos for dupl resolv */
1013 if (res
->type
== XPATH_NUMBER
) {
1016 #ifdef WITH_XSLT_DEBUG_PROCESS
1017 xsltGenericDebug(xsltGenericDebugContext
,
1018 "xsltComputeSortResult: select didn't evaluate to a number\n");
1023 if (res
->type
== XPATH_STRING
) {
1024 if (comp
->locale
!= (xsltLocale
)0) {
1025 xmlChar
*str
= res
->stringval
;
1026 res
->stringval
= (xmlChar
*) xsltStrxfrm(comp
->locale
, str
);
1032 #ifdef WITH_XSLT_DEBUG_PROCESS
1033 xsltGenericDebug(xsltGenericDebugContext
,
1034 "xsltComputeSortResult: select didn't evaluate to a string\n");
1040 ctxt
->state
= XSLT_STATE_STOPPED
;
1044 ctxt
->node
= oldNode
;
1045 ctxt
->inst
= oldInst
;
1046 ctxt
->xpathCtxt
->contextSize
= oldSize
;
1047 ctxt
->xpathCtxt
->proximityPosition
= oldPos
;
1048 ctxt
->xpathCtxt
->nsNr
= oldNsNr
;
1049 ctxt
->xpathCtxt
->namespaces
= oldNamespaces
;
1055 * xsltDefaultSortFunction:
1056 * @ctxt: a XSLT process context
1057 * @sorts: array of sort nodes
1058 * @nbsorts: the number of sorts in the array
1060 * reorder the current node list accordingly to the set of sorting
1061 * requirement provided by the arry of nodes.
1064 xsltDefaultSortFunction(xsltTransformContextPtr ctxt
, xmlNodePtr
*sorts
,
1066 #ifdef XSLT_REFACTORED
1067 xsltStyleItemSortPtr comp
;
1069 xsltStylePreCompPtr comp
;
1071 xmlXPathObjectPtr
*resultsTab
[XSLT_MAX_SORT
];
1072 xmlXPathObjectPtr
*results
= NULL
, *res
;
1073 xmlNodeSetPtr list
= NULL
;
1074 int descending
, number
, desc
, numb
;
1080 xmlXPathObjectPtr tmp
;
1081 int tempstype
[XSLT_MAX_SORT
], temporder
[XSLT_MAX_SORT
];
1083 if ((ctxt
== NULL
) || (sorts
== NULL
) || (nbsorts
<= 0) ||
1084 (nbsorts
>= XSLT_MAX_SORT
))
1086 if (sorts
[0] == NULL
)
1088 comp
= sorts
[0]->psvi
;
1092 list
= ctxt
->nodeList
;
1093 if ((list
== NULL
) || (list
->nodeNr
<= 1))
1094 return; /* nothing to do */
1096 for (j
= 0; j
< nbsorts
; j
++) {
1097 comp
= sorts
[j
]->psvi
;
1099 if ((comp
->stype
== NULL
) && (comp
->has_stype
!= 0)) {
1101 xsltEvalAttrValueTemplate(ctxt
, sorts
[j
],
1102 (const xmlChar
*) "data-type",
1104 if (comp
->stype
!= NULL
) {
1106 if (xmlStrEqual(comp
->stype
, (const xmlChar
*) "text"))
1108 else if (xmlStrEqual(comp
->stype
, (const xmlChar
*) "number"))
1111 xsltTransformError(ctxt
, NULL
, sorts
[j
],
1112 "xsltDoSortFunction: no support for data-type = %s\n",
1114 comp
->number
= 0; /* use default */
1119 if ((comp
->order
== NULL
) && (comp
->has_order
!= 0)) {
1120 comp
->order
= xsltEvalAttrValueTemplate(ctxt
, sorts
[j
],
1121 (const xmlChar
*) "order",
1123 if (comp
->order
!= NULL
) {
1125 if (xmlStrEqual(comp
->order
, (const xmlChar
*) "ascending"))
1126 comp
->descending
= 0;
1127 else if (xmlStrEqual(comp
->order
,
1128 (const xmlChar
*) "descending"))
1129 comp
->descending
= 1;
1131 xsltTransformError(ctxt
, NULL
, sorts
[j
],
1132 "xsltDoSortFunction: invalid value %s for order\n",
1134 comp
->descending
= 0; /* use default */
1142 resultsTab
[0] = xsltComputeSortResult(ctxt
, sorts
[0]);
1143 for (i
= 1;i
< XSLT_MAX_SORT
;i
++)
1144 resultsTab
[i
] = NULL
;
1146 results
= resultsTab
[0];
1148 comp
= sorts
[0]->psvi
;
1149 descending
= comp
->descending
;
1150 number
= comp
->number
;
1151 if (results
== NULL
)
1154 /* Shell's sort of node-set */
1155 for (incr
= len
/ 2; incr
> 0; incr
/= 2) {
1156 for (i
= incr
; i
< len
; i
++) {
1158 if (results
[i
] == NULL
)
1162 if (results
[j
] == NULL
)
1166 /* We make NaN smaller than number in accordance
1168 if (xmlXPathIsNaN(results
[j
]->floatval
)) {
1169 if (xmlXPathIsNaN(results
[j
+ incr
]->floatval
))
1173 } else if (xmlXPathIsNaN(results
[j
+ incr
]->floatval
))
1175 else if (results
[j
]->floatval
==
1176 results
[j
+ incr
]->floatval
)
1178 else if (results
[j
]->floatval
>
1179 results
[j
+ incr
]->floatval
)
1182 } else if(comp
->locale
!= (xsltLocale
)0) {
1183 tst
= xsltLocaleStrcmp(
1185 (xsltLocaleChar
*) results
[j
]->stringval
,
1186 (xsltLocaleChar
*) results
[j
+ incr
]->stringval
);
1188 tst
= xmlStrcmp(results
[j
]->stringval
,
1189 results
[j
+ incr
]->stringval
);
1196 * Okay we need to use multi level sorts
1199 while (depth
< nbsorts
) {
1200 if (sorts
[depth
] == NULL
)
1202 comp
= sorts
[depth
]->psvi
;
1205 desc
= comp
->descending
;
1206 numb
= comp
->number
;
1209 * Compute the result of the next level for the
1210 * full set, this might be optimized ... or not
1212 if (resultsTab
[depth
] == NULL
)
1213 resultsTab
[depth
] = xsltComputeSortResult(ctxt
,
1215 res
= resultsTab
[depth
];
1218 if (res
[j
] == NULL
) {
1219 if (res
[j
+incr
] != NULL
)
1221 } else if (res
[j
+incr
] == NULL
) {
1225 /* We make NaN smaller than number in
1226 accordance with XSLT spec */
1227 if (xmlXPathIsNaN(res
[j
]->floatval
)) {
1228 if (xmlXPathIsNaN(res
[j
+
1233 } else if (xmlXPathIsNaN(res
[j
+ incr
]->
1236 else if (res
[j
]->floatval
== res
[j
+ incr
]->
1239 else if (res
[j
]->floatval
>
1240 res
[j
+ incr
]->floatval
)
1243 } else if(comp
->locale
!= (xsltLocale
)0) {
1244 tst
= xsltLocaleStrcmp(
1246 (xsltLocaleChar
*) res
[j
]->stringval
,
1247 (xsltLocaleChar
*) res
[j
+ incr
]->stringval
);
1249 tst
= xmlStrcmp(res
[j
]->stringval
,
1250 res
[j
+ incr
]->stringval
);
1257 * if we still can't differenciate at this level
1258 * try one level deeper.
1266 tst
= results
[j
]->index
> results
[j
+ incr
]->index
;
1270 results
[j
] = results
[j
+ incr
];
1271 results
[j
+ incr
] = tmp
;
1272 node
= list
->nodeTab
[j
];
1273 list
->nodeTab
[j
] = list
->nodeTab
[j
+ incr
];
1274 list
->nodeTab
[j
+ incr
] = node
;
1276 while (depth
< nbsorts
) {
1277 if (sorts
[depth
] == NULL
)
1279 if (resultsTab
[depth
] == NULL
)
1281 res
= resultsTab
[depth
];
1283 res
[j
] = res
[j
+ incr
];
1284 res
[j
+ incr
] = tmp
;
1294 for (j
= 0; j
< nbsorts
; j
++) {
1295 comp
= sorts
[j
]->psvi
;
1296 if (tempstype
[j
] == 1) {
1297 /* The data-type needs to be recomputed each time */
1298 xmlFree((void *)(comp
->stype
));
1301 if (temporder
[j
] == 1) {
1302 /* The order needs to be recomputed each time */
1303 xmlFree((void *)(comp
->order
));
1306 if (resultsTab
[j
] != NULL
) {
1307 for (i
= 0;i
< len
;i
++)
1308 xmlXPathFreeObject(resultsTab
[j
][i
]);
1309 xmlFree(resultsTab
[j
]);
1315 static xsltSortFunc xsltSortFunction
= xsltDefaultSortFunction
;
1318 * xsltDoSortFunction:
1319 * @ctxt: a XSLT process context
1320 * @sorts: array of sort nodes
1321 * @nbsorts: the number of sorts in the array
1323 * reorder the current node list accordingly to the set of sorting
1324 * requirement provided by the arry of nodes.
1325 * This is a wrapper function, the actual function used is specified
1326 * using xsltSetCtxtSortFunc() to set the context specific sort function,
1327 * or xsltSetSortFunc() to set the global sort function.
1328 * If a sort function is set on the context, this will get called.
1329 * Otherwise the global sort function is called.
1332 xsltDoSortFunction(xsltTransformContextPtr ctxt
, xmlNodePtr
* sorts
,
1335 if (ctxt
->sortfunc
!= NULL
)
1336 (ctxt
->sortfunc
)(ctxt
, sorts
, nbsorts
);
1337 else if (xsltSortFunction
!= NULL
)
1338 xsltSortFunction(ctxt
, sorts
, nbsorts
);
1343 * @handler: the new handler function
1345 * Function to reset the global handler for XSLT sorting.
1346 * If the handler is NULL, the default sort function will be used.
1349 xsltSetSortFunc(xsltSortFunc handler
) {
1350 if (handler
!= NULL
)
1351 xsltSortFunction
= handler
;
1353 xsltSortFunction
= xsltDefaultSortFunction
;
1357 * xsltSetCtxtSortFunc:
1358 * @ctxt: a XSLT process context
1359 * @handler: the new handler function
1361 * Function to set the handler for XSLT sorting
1362 * for the specified context.
1363 * If the handler is NULL, then the global
1364 * sort function will be called
1367 xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt
, xsltSortFunc handler
) {
1368 ctxt
->sortfunc
= handler
;
1371 /************************************************************************
1375 ************************************************************************/
1378 * xsltSetCtxtParseOptions:
1379 * @ctxt: a XSLT process context
1380 * @options: a combination of libxml2 xmlParserOption
1382 * Change the default parser option passed by the XSLT engine to the
1383 * parser when using document() loading.
1385 * Returns the previous options or -1 in case of error
1388 xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt
, int options
)
1394 oldopts
= ctxt
->parserOptions
;
1396 oldopts
|= XML_PARSE_XINCLUDE
;
1397 ctxt
->parserOptions
= options
;
1398 if (options
& XML_PARSE_XINCLUDE
)
1405 /************************************************************************
1409 ************************************************************************/
1413 * @buf: an output buffer
1414 * @result: the result xmlDocPtr
1415 * @style: the stylesheet
1417 * Save the result @result obtained by applying the @style stylesheet
1418 * to an I/O output channel @buf
1420 * Returns the number of byte written or -1 in case of failure.
1423 xsltSaveResultTo(xmlOutputBufferPtr buf
, xmlDocPtr result
,
1424 xsltStylesheetPtr style
) {
1425 const xmlChar
*encoding
;
1427 const xmlChar
*method
;
1430 if ((buf
== NULL
) || (result
== NULL
) || (style
== NULL
))
1432 if ((result
->children
== NULL
) ||
1433 ((result
->children
->type
== XML_DTD_NODE
) &&
1434 (result
->children
->next
== NULL
)))
1437 if ((style
->methodURI
!= NULL
) &&
1438 ((style
->method
== NULL
) ||
1439 (!xmlStrEqual(style
->method
, (const xmlChar
*) "xhtml")))) {
1440 xsltGenericError(xsltGenericErrorContext
,
1441 "xsltSaveResultTo : unknown ouput method\n");
1445 base
= buf
->written
;
1447 XSLT_GET_IMPORT_PTR(method
, style
, method
)
1448 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1449 XSLT_GET_IMPORT_INT(indent
, style
, indent
);
1451 if ((method
== NULL
) && (result
->type
== XML_HTML_DOCUMENT_NODE
))
1452 method
= (const xmlChar
*) "html";
1454 if ((method
!= NULL
) &&
1455 (xmlStrEqual(method
, (const xmlChar
*) "html"))) {
1456 if (encoding
!= NULL
) {
1457 htmlSetMetaEncoding(result
, (const xmlChar
*) encoding
);
1459 htmlSetMetaEncoding(result
, (const xmlChar
*) "UTF-8");
1463 htmlDocContentDumpFormatOutput(buf
, result
, (const char *) encoding
,
1465 xmlOutputBufferFlush(buf
);
1466 } else if ((method
!= NULL
) &&
1467 (xmlStrEqual(method
, (const xmlChar
*) "xhtml"))) {
1468 if (encoding
!= NULL
) {
1469 htmlSetMetaEncoding(result
, (const xmlChar
*) encoding
);
1471 htmlSetMetaEncoding(result
, (const xmlChar
*) "UTF-8");
1473 htmlDocContentDumpOutput(buf
, result
, (const char *) encoding
);
1474 xmlOutputBufferFlush(buf
);
1475 } else if ((method
!= NULL
) &&
1476 (xmlStrEqual(method
, (const xmlChar
*) "text"))) {
1479 cur
= result
->children
;
1480 while (cur
!= NULL
) {
1481 if (cur
->type
== XML_TEXT_NODE
)
1482 xmlOutputBufferWriteString(buf
, (const char *) cur
->content
);
1487 if (cur
->children
!= NULL
) {
1488 if ((cur
->children
->type
!= XML_ENTITY_DECL
) &&
1489 (cur
->children
->type
!= XML_ENTITY_REF_NODE
) &&
1490 (cur
->children
->type
!= XML_ENTITY_NODE
)) {
1491 cur
= cur
->children
;
1495 if (cur
->next
!= NULL
) {
1504 if (cur
== (xmlNodePtr
) style
->doc
) {
1508 if (cur
->next
!= NULL
) {
1512 } while (cur
!= NULL
);
1514 xmlOutputBufferFlush(buf
);
1519 XSLT_GET_IMPORT_INT(omitXmlDecl
, style
, omitXmlDeclaration
);
1520 XSLT_GET_IMPORT_INT(standalone
, style
, standalone
);
1522 if (omitXmlDecl
!= 1) {
1523 xmlOutputBufferWriteString(buf
, "<?xml version=");
1524 if (result
->version
!= NULL
) {
1525 xmlOutputBufferWriteString(buf
, "\"");
1526 xmlOutputBufferWriteString(buf
, (const char *)result
->version
);
1527 xmlOutputBufferWriteString(buf
, "\"");
1529 xmlOutputBufferWriteString(buf
, "\"1.0\"");
1530 if (encoding
== NULL
) {
1531 if (result
->encoding
!= NULL
)
1532 encoding
= result
->encoding
;
1533 else if (result
->charset
!= XML_CHAR_ENCODING_UTF8
)
1534 encoding
= (const xmlChar
*)
1535 xmlGetCharEncodingName((xmlCharEncoding
)
1538 if (encoding
!= NULL
) {
1539 xmlOutputBufferWriteString(buf
, " encoding=");
1540 xmlOutputBufferWriteString(buf
, "\"");
1541 xmlOutputBufferWriteString(buf
, (const char *) encoding
);
1542 xmlOutputBufferWriteString(buf
, "\"");
1544 switch (standalone
) {
1546 xmlOutputBufferWriteString(buf
, " standalone=\"no\"");
1549 xmlOutputBufferWriteString(buf
, " standalone=\"yes\"");
1554 xmlOutputBufferWriteString(buf
, "?>\n");
1556 if (result
->children
!= NULL
) {
1557 xmlNodePtr child
= result
->children
;
1559 while (child
!= NULL
) {
1560 xmlNodeDumpOutput(buf
, result
, child
, 0, (indent
== 1),
1561 (const char *) encoding
);
1562 if (indent
&& ((child
->type
== XML_DTD_NODE
) ||
1563 ((child
->type
== XML_COMMENT_NODE
) &&
1564 (child
->next
!= NULL
))))
1565 xmlOutputBufferWriteString(buf
, "\n");
1566 child
= child
->next
;
1569 xmlOutputBufferWriteString(buf
, "\n");
1571 xmlOutputBufferFlush(buf
);
1573 return(buf
->written
- base
);
1577 * xsltSaveResultToFilename:
1578 * @URL: a filename or URL
1579 * @result: the result xmlDocPtr
1580 * @style: the stylesheet
1581 * @compression: the compression factor (0 - 9 included)
1583 * Save the result @result obtained by applying the @style stylesheet
1586 * Returns the number of byte written or -1 in case of failure.
1589 xsltSaveResultToFilename(const char *URL
, xmlDocPtr result
,
1590 xsltStylesheetPtr style
, int compression
) {
1591 xmlOutputBufferPtr buf
;
1592 const xmlChar
*encoding
;
1595 if ((URL
== NULL
) || (result
== NULL
) || (style
== NULL
))
1597 if (result
->children
== NULL
)
1600 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1601 if (encoding
!= NULL
) {
1602 xmlCharEncodingHandlerPtr encoder
;
1604 encoder
= xmlFindCharEncodingHandler((char *)encoding
);
1605 if ((encoder
!= NULL
) &&
1606 (xmlStrEqual((const xmlChar
*)encoder
->name
,
1607 (const xmlChar
*) "UTF-8")))
1609 buf
= xmlOutputBufferCreateFilename(URL
, encoder
, compression
);
1611 buf
= xmlOutputBufferCreateFilename(URL
, NULL
, compression
);
1615 xsltSaveResultTo(buf
, result
, style
);
1616 ret
= xmlOutputBufferClose(buf
);
1621 * xsltSaveResultToFile:
1622 * @file: a FILE * I/O
1623 * @result: the result xmlDocPtr
1624 * @style: the stylesheet
1626 * Save the result @result obtained by applying the @style stylesheet
1627 * to an open FILE * I/O.
1628 * This does not close the FILE @file
1630 * Returns the number of bytes written or -1 in case of failure.
1633 xsltSaveResultToFile(FILE *file
, xmlDocPtr result
, xsltStylesheetPtr style
) {
1634 xmlOutputBufferPtr buf
;
1635 const xmlChar
*encoding
;
1638 if ((file
== NULL
) || (result
== NULL
) || (style
== NULL
))
1640 if (result
->children
== NULL
)
1643 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1644 if (encoding
!= NULL
) {
1645 xmlCharEncodingHandlerPtr encoder
;
1647 encoder
= xmlFindCharEncodingHandler((char *)encoding
);
1648 if ((encoder
!= NULL
) &&
1649 (xmlStrEqual((const xmlChar
*)encoder
->name
,
1650 (const xmlChar
*) "UTF-8")))
1652 buf
= xmlOutputBufferCreateFile(file
, encoder
);
1654 buf
= xmlOutputBufferCreateFile(file
, NULL
);
1659 xsltSaveResultTo(buf
, result
, style
);
1660 ret
= xmlOutputBufferClose(buf
);
1665 * xsltSaveResultToFd:
1666 * @fd: a file descriptor
1667 * @result: the result xmlDocPtr
1668 * @style: the stylesheet
1670 * Save the result @result obtained by applying the @style stylesheet
1671 * to an open file descriptor
1672 * This does not close the descriptor.
1674 * Returns the number of bytes written or -1 in case of failure.
1677 xsltSaveResultToFd(int fd
, xmlDocPtr result
, xsltStylesheetPtr style
) {
1678 xmlOutputBufferPtr buf
;
1679 const xmlChar
*encoding
;
1682 if ((fd
< 0) || (result
== NULL
) || (style
== NULL
))
1684 if (result
->children
== NULL
)
1687 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1688 if (encoding
!= NULL
) {
1689 xmlCharEncodingHandlerPtr encoder
;
1691 encoder
= xmlFindCharEncodingHandler((char *)encoding
);
1692 if ((encoder
!= NULL
) &&
1693 (xmlStrEqual((const xmlChar
*)encoder
->name
,
1694 (const xmlChar
*) "UTF-8")))
1696 buf
= xmlOutputBufferCreateFd(fd
, encoder
);
1698 buf
= xmlOutputBufferCreateFd(fd
, NULL
);
1702 xsltSaveResultTo(buf
, result
, style
);
1703 ret
= xmlOutputBufferClose(buf
);
1708 * xsltSaveResultToString:
1709 * @doc_txt_ptr: Memory pointer for allocated XML text
1710 * @doc_txt_len: Length of the generated XML text
1711 * @result: the result xmlDocPtr
1712 * @style: the stylesheet
1714 * Save the result @result obtained by applying the @style stylesheet
1715 * to a new allocated string.
1717 * Returns 0 in case of success and -1 in case of error
1720 xsltSaveResultToString(xmlChar
**doc_txt_ptr
, int * doc_txt_len
,
1721 xmlDocPtr result
, xsltStylesheetPtr style
) {
1722 xmlOutputBufferPtr buf
;
1723 const xmlChar
*encoding
;
1725 *doc_txt_ptr
= NULL
;
1727 if (result
->children
== NULL
)
1730 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1731 if (encoding
!= NULL
) {
1732 xmlCharEncodingHandlerPtr encoder
;
1734 encoder
= xmlFindCharEncodingHandler((char *)encoding
);
1735 if ((encoder
!= NULL
) &&
1736 (xmlStrEqual((const xmlChar
*)encoder
->name
,
1737 (const xmlChar
*) "UTF-8")))
1739 buf
= xmlAllocOutputBuffer(encoder
);
1741 buf
= xmlAllocOutputBuffer(NULL
);
1745 xsltSaveResultTo(buf
, result
, style
);
1746 #ifdef LIBXML2_NEW_BUFFER
1747 if (buf
->conv
!= NULL
) {
1748 *doc_txt_len
= xmlBufUse(buf
->conv
);
1749 *doc_txt_ptr
= xmlStrndup(xmlBufContent(buf
->conv
), *doc_txt_len
);
1751 *doc_txt_len
= xmlBufUse(buf
->buffer
);
1752 *doc_txt_ptr
= xmlStrndup(xmlBufContent(buf
->buffer
), *doc_txt_len
);
1755 if (buf
->conv
!= NULL
) {
1756 *doc_txt_len
= buf
->conv
->use
;
1757 *doc_txt_ptr
= xmlStrndup(buf
->conv
->content
, *doc_txt_len
);
1759 *doc_txt_len
= buf
->buffer
->use
;
1760 *doc_txt_ptr
= xmlStrndup(buf
->buffer
->content
, *doc_txt_len
);
1763 (void)xmlOutputBufferClose(buf
);
1767 /************************************************************************
1769 * Generating profiling informations *
1771 ************************************************************************/
1773 static long calibration
= -1;
1776 * xsltCalibrateTimestamps:
1778 * Used for to calibrate the xsltTimestamp() function
1779 * Should work if launched at startup and we don't loose our quantum :-)
1781 * Returns the number of milliseconds used by xsltTimestamp()
1783 #if !defined(XSLT_WIN32_PERFORMANCE_COUNTER) && \
1784 (defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY))
1786 xsltCalibrateTimestamps(void) {
1789 for (i
= 0;i
< 999;i
++)
1791 return(xsltTimestamp() / 1000);
1796 * xsltCalibrateAdjust:
1797 * @delta: a negative dealy value found
1799 * Used for to correct the calibration for xsltTimestamp()
1802 xsltCalibrateAdjust(long delta
) {
1803 calibration
+= delta
;
1809 * Used for gathering profiling data
1811 * Returns the number of tenth of milliseconds since the beginning of the
1817 #ifdef XSLT_WIN32_PERFORMANCE_COUNTER
1819 LARGE_INTEGER performanceCount
;
1820 LARGE_INTEGER performanceFrequency
;
1823 static LONGLONG startupQuadCount
= 0;
1824 static LONGLONG startupQuadFreq
= 0;
1826 ok
= QueryPerformanceCounter(&performanceCount
);
1829 quadCount
= performanceCount
.QuadPart
;
1830 if (calibration
< 0) {
1832 ok
= QueryPerformanceFrequency(&performanceFrequency
);
1835 startupQuadFreq
= performanceFrequency
.QuadPart
;
1836 startupQuadCount
= quadCount
;
1839 if (startupQuadFreq
== 0)
1841 seconds
= (quadCount
- startupQuadCount
) / (double) startupQuadFreq
;
1842 return (long) (seconds
* XSLT_TIMESTAMP_TICS_PER_SEC
);
1844 #else /* XSLT_WIN32_PERFORMANCE_COUNTER */
1845 #ifdef HAVE_CLOCK_GETTIME
1846 # if defined(CLOCK_MONOTONIC)
1847 # define XSLT_CLOCK CLOCK_MONOTONIC
1848 # elif defined(CLOCK_HIGHRES)
1849 # define XSLT_CLOCK CLOCK_HIGHRES
1851 # define XSLT_CLOCK CLOCK_REALTIME
1853 static struct timespec startup
;
1854 struct timespec cur
;
1857 if (calibration
< 0) {
1858 clock_gettime(XSLT_CLOCK
, &startup
);
1860 calibration
= xsltCalibrateTimestamps();
1861 clock_gettime(XSLT_CLOCK
, &startup
);
1865 clock_gettime(XSLT_CLOCK
, &cur
);
1866 tics
= (cur
.tv_sec
- startup
.tv_sec
) * XSLT_TIMESTAMP_TICS_PER_SEC
;
1867 tics
+= (cur
.tv_nsec
- startup
.tv_nsec
) /
1868 (1000000000l / XSLT_TIMESTAMP_TICS_PER_SEC
);
1870 tics
-= calibration
;
1873 #elif HAVE_GETTIMEOFDAY
1874 static struct timeval startup
;
1878 if (calibration
< 0) {
1879 gettimeofday(&startup
, NULL
);
1881 calibration
= xsltCalibrateTimestamps();
1882 gettimeofday(&startup
, NULL
);
1886 gettimeofday(&cur
, NULL
);
1887 tics
= (cur
.tv_sec
- startup
.tv_sec
) * XSLT_TIMESTAMP_TICS_PER_SEC
;
1888 tics
+= (cur
.tv_usec
- startup
.tv_usec
) /
1889 (1000000l / XSLT_TIMESTAMP_TICS_PER_SEC
);
1891 tics
-= calibration
;
1895 /* Neither gettimeofday() nor Win32 performance counter available */
1899 #endif /* HAVE_GETTIMEOFDAY */
1900 #endif /* XSLT_WIN32_PERFORMANCE_COUNTER */
1904 pretty_templ_match(xsltTemplatePtr templ
) {
1905 static char dst
[1001];
1906 char *src
= (char *)templ
->match
;
1909 /* strip white spaces */
1910 for (j
=0; i
<1000 && src
[j
]; i
++,j
++) {
1911 for(;src
[j
]==' ';j
++);
1914 if(i
<998 && templ
->mode
) {
1917 src
=(char *)templ
->mode
;
1918 for (j
=0; i
<999 && src
[j
]; i
++,j
++) {
1927 #define MAX_TEMPLATES 10000
1930 * xsltSaveProfiling:
1931 * @ctxt: an XSLT context
1932 * @output: a FILE * for saving the informations
1934 * Save the profiling informations on @output
1937 xsltSaveProfiling(xsltTransformContextPtr ctxt
, FILE *output
) {
1941 unsigned long totalt
;
1942 xsltTemplatePtr
*templates
;
1943 xsltStylesheetPtr style
;
1944 xsltTemplatePtr templ1
,templ2
;
1947 if ((output
== NULL
) || (ctxt
== NULL
))
1949 if (ctxt
->profile
== 0)
1953 max
= MAX_TEMPLATES
;
1954 templates
= xmlMalloc(max
* sizeof(xsltTemplatePtr
));
1955 if (templates
== NULL
)
1958 style
= ctxt
->style
;
1959 while (style
!= NULL
) {
1960 templ1
= style
->templates
;
1961 while (templ1
!= NULL
) {
1965 if (templ1
->nbCalls
> 0)
1966 templates
[nb
++] = templ1
;
1967 templ1
= templ1
->next
;
1970 style
= xsltNextImport(style
);
1973 for (i
= 0;i
< nb
-1;i
++) {
1974 for (j
= i
+ 1; j
< nb
; j
++) {
1975 if ((templates
[i
]->time
<= templates
[j
]->time
) ||
1976 ((templates
[i
]->time
== templates
[j
]->time
) &&
1977 (templates
[i
]->nbCalls
<= templates
[j
]->nbCalls
))) {
1978 templ1
= templates
[j
];
1979 templates
[j
] = templates
[i
];
1980 templates
[i
] = templ1
;
1986 /* print flat profile */
1988 fprintf(output
, "%6s%20s%20s%10s Calls Tot 100us Avg\n\n",
1989 "number", "match", "name", "mode");
1992 for (i
= 0;i
< nb
;i
++) {
1993 templ1
= templates
[i
];
1994 fprintf(output
, "%5d ", i
);
1995 if (templ1
->match
!= NULL
) {
1996 if (xmlStrlen(templ1
->match
) > 20)
1997 fprintf(output
, "%s\n%26s", templ1
->match
, "");
1999 fprintf(output
, "%20s", templ1
->match
);
2001 fprintf(output
, "%20s", "");
2003 if (templ1
->name
!= NULL
) {
2004 if (xmlStrlen(templ1
->name
) > 20)
2005 fprintf(output
, "%s\n%46s", templ1
->name
, "");
2007 fprintf(output
, "%20s", templ1
->name
);
2009 fprintf(output
, "%20s", "");
2011 if (templ1
->mode
!= NULL
) {
2012 if (xmlStrlen(templ1
->mode
) > 10)
2013 fprintf(output
, "%s\n%56s", templ1
->mode
, "");
2015 fprintf(output
, "%10s", templ1
->mode
);
2017 fprintf(output
, "%10s", "");
2019 fprintf(output
, " %6d", templ1
->nbCalls
);
2020 fprintf(output
, " %6ld %6ld\n", templ1
->time
,
2021 templ1
->time
/ templ1
->nbCalls
);
2022 total
+= templ1
->nbCalls
;
2023 totalt
+= templ1
->time
;
2025 fprintf(output
, "\n%30s%26s %6d %6ld\n", "Total", "", total
, totalt
);
2028 /* print call graph */
2030 childt
= xmlMalloc((nb
+ 1) * sizeof(int));
2034 /* precalculate children times */
2035 for (i
= 0; i
< nb
; i
++) {
2036 templ1
= templates
[i
];
2039 for (k
= 0; k
< nb
; k
++) {
2040 templ2
= templates
[k
];
2041 for (l
= 0; l
< templ2
->templNr
; l
++) {
2042 if (templ2
->templCalledTab
[l
] == templ1
) {
2043 childt
[i
] +=templ2
->time
;
2050 fprintf(output
, "\nindex %% time self children called name\n");
2052 for (i
= 0; i
< nb
; i
++) {
2053 char ix_str
[20], timep_str
[20], times_str
[20], timec_str
[20], called_str
[20];
2056 templ1
= templates
[i
];
2058 for (j
= 0; j
< templ1
->templNr
; j
++) {
2059 templ2
= templ1
->templCalledTab
[j
];
2060 for (k
= 0; k
< nb
; k
++) {
2061 if (templates
[k
] == templ2
)
2064 t
=templ2
?templ2
->time
:totalt
;
2065 snprintf(times_str
,sizeof(times_str
),"%8.3f",(float)t
/XSLT_TIMESTAMP_TICS_PER_SEC
);
2066 snprintf(timec_str
,sizeof(timec_str
),"%8.3f",(float)childt
[k
]/XSLT_TIMESTAMP_TICS_PER_SEC
);
2067 snprintf(called_str
,sizeof(called_str
),"%6d/%d",
2068 templ1
->templCountTab
[j
], /* number of times caller calls 'this' */
2069 templ1
->nbCalls
); /* total number of calls to 'this' */
2071 fprintf(output
, " %-8s %-8s %-12s %s [%d]\n",
2072 times_str
,timec_str
,called_str
,
2073 (templ2
?(templ2
->name
?(char *)templ2
->name
:pretty_templ_match(templ2
)):"-"),k
);
2076 snprintf(ix_str
,sizeof(ix_str
),"[%d]",i
);
2077 snprintf(timep_str
,sizeof(timep_str
),"%6.2f",(float)templ1
->time
*100.0/totalt
);
2078 snprintf(times_str
,sizeof(times_str
),"%8.3f",(float)templ1
->time
/XSLT_TIMESTAMP_TICS_PER_SEC
);
2079 snprintf(timec_str
,sizeof(timec_str
),"%8.3f",(float)childt
[i
]/XSLT_TIMESTAMP_TICS_PER_SEC
);
2080 fprintf(output
, "%-5s %-6s %-8s %-8s %6d %s [%d]\n",
2081 ix_str
, timep_str
,times_str
,timec_str
,
2083 templ1
->name
?(char *)templ1
->name
:pretty_templ_match(templ1
),i
);
2085 * - go over templates[0..nb] and their templCalledTab[]
2086 * - print those where we in the the call-stack
2089 for (k
= 0; k
< nb
; k
++) {
2090 templ2
= templates
[k
];
2091 for (l
= 0; l
< templ2
->templNr
; l
++) {
2092 if (templ2
->templCalledTab
[l
] == templ1
) {
2093 total
+=templ2
->templCountTab
[l
];
2097 for (k
= 0; k
< nb
; k
++) {
2098 templ2
= templates
[k
];
2099 for (l
= 0; l
< templ2
->templNr
; l
++) {
2100 if (templ2
->templCalledTab
[l
] == templ1
) {
2101 snprintf(times_str
,sizeof(times_str
),"%8.3f",(float)templ2
->time
/XSLT_TIMESTAMP_TICS_PER_SEC
);
2102 snprintf(timec_str
,sizeof(timec_str
),"%8.3f",(float)childt
[k
]/XSLT_TIMESTAMP_TICS_PER_SEC
);
2103 snprintf(called_str
,sizeof(called_str
),"%6d/%d",
2104 templ2
->templCountTab
[l
], /* number of times 'this' calls callee */
2105 total
); /* total number of calls from 'this' */
2106 fprintf(output
, " %-8s %-8s %-12s %s [%d]\n",
2107 times_str
,timec_str
,called_str
,
2108 templ2
->name
?(char *)templ2
->name
:pretty_templ_match(templ2
),k
);
2112 fprintf(output
, "-----------------------------------------------\n");
2115 fprintf(output
, "\f\nIndex by function name\n");
2116 for (i
= 0; i
< nb
; i
++) {
2117 templ1
= templates
[i
];
2118 fprintf(output
, "[%d] %s (%s:%d)\n",
2119 i
, templ1
->name
?(char *)templ1
->name
:pretty_templ_match(templ1
),
2120 templ1
->style
->doc
->URL
,templ1
->elem
->line
);
2123 fprintf(output
, "\f\n");
2129 /************************************************************************
2131 * Fetching profiling informations *
2133 ************************************************************************/
2136 * xsltGetProfileInformation:
2137 * @ctxt: a transformation context
2139 * This function should be called after the transformation completed
2140 * to extract template processing profiling informations if availble.
2141 * The informations are returned as an XML document tree like
2142 * <?xml version="1.0"?>
2144 * <template rank="1" match="*" name=""
2145 * mode="" calls="6" time="48" average="8"/>
2146 * <template rank="2" match="item2|item3" name=""
2147 * mode="" calls="10" time="30" average="3"/>
2148 * <template rank="3" match="item1" name=""
2149 * mode="" calls="5" time="17" average="3"/>
2151 * The caller will need to free up the returned tree with xmlFreeDoc()
2153 * Returns the xmlDocPtr corresponding to the result or NULL if not available.
2157 xsltGetProfileInformation(xsltTransformContextPtr ctxt
)
2159 xmlDocPtr ret
= NULL
;
2160 xmlNodePtr root
, child
;
2163 xsltStylesheetPtr style
;
2164 xsltTemplatePtr
*templates
;
2165 xsltTemplatePtr templ
;
2166 int nb
= 0, max
= 0, i
, j
;
2177 (xsltTemplatePtr
*) xmlMalloc(max
* sizeof(xsltTemplatePtr
));
2178 if (templates
== NULL
)
2182 * collect all the templates in an array
2184 style
= ctxt
->style
;
2185 while (style
!= NULL
) {
2186 templ
= style
->templates
;
2187 while (templ
!= NULL
) {
2191 if (templ
->nbCalls
> 0)
2192 templates
[nb
++] = templ
;
2193 templ
= templ
->next
;
2196 style
= (xsltStylesheetPtr
) xsltNextImport(style
);
2200 * Sort the array by time spent
2202 for (i
= 0; i
< nb
- 1; i
++) {
2203 for (j
= i
+ 1; j
< nb
; j
++) {
2204 if ((templates
[i
]->time
<= templates
[j
]->time
) ||
2205 ((templates
[i
]->time
== templates
[j
]->time
) &&
2206 (templates
[i
]->nbCalls
<= templates
[j
]->nbCalls
))) {
2207 templ
= templates
[j
];
2208 templates
[j
] = templates
[i
];
2209 templates
[i
] = templ
;
2215 * Generate a document corresponding to the results.
2217 ret
= xmlNewDoc(BAD_CAST
"1.0");
2218 root
= xmlNewDocNode(ret
, NULL
, BAD_CAST
"profile", NULL
);
2219 xmlDocSetRootElement(ret
, root
);
2221 for (i
= 0; i
< nb
; i
++) {
2222 child
= xmlNewChild(root
, NULL
, BAD_CAST
"template", NULL
);
2223 snprintf(buf
, sizeof(buf
), "%d", i
+ 1);
2224 xmlSetProp(child
, BAD_CAST
"rank", BAD_CAST buf
);
2225 xmlSetProp(child
, BAD_CAST
"match", BAD_CAST templates
[i
]->match
);
2226 xmlSetProp(child
, BAD_CAST
"name", BAD_CAST templates
[i
]->name
);
2227 xmlSetProp(child
, BAD_CAST
"mode", BAD_CAST templates
[i
]->mode
);
2229 snprintf(buf
, sizeof(buf
), "%d", templates
[i
]->nbCalls
);
2230 xmlSetProp(child
, BAD_CAST
"calls", BAD_CAST buf
);
2232 snprintf(buf
, sizeof(buf
), "%ld", templates
[i
]->time
);
2233 xmlSetProp(child
, BAD_CAST
"time", BAD_CAST buf
);
2235 snprintf(buf
, sizeof(buf
), "%ld", templates
[i
]->time
/ templates
[i
]->nbCalls
);
2236 xmlSetProp(child
, BAD_CAST
"average", BAD_CAST buf
);
2244 /************************************************************************
2246 * Hooks for libxml2 XPath *
2248 ************************************************************************/
2251 * xsltXPathCompileFlags:
2252 * @style: the stylesheet
2253 * @str: the XPath expression
2254 * @flags: extra compilation flags to pass down to libxml2 XPath
2256 * Compile an XPath expression
2258 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2259 * the caller has to free the object.
2262 xsltXPathCompileFlags(xsltStylesheetPtr style
, const xmlChar
*str
, int flags
) {
2263 xmlXPathContextPtr xpathCtxt
;
2264 xmlXPathCompExprPtr ret
;
2266 if (style
!= NULL
) {
2267 #ifdef XSLT_REFACTORED_XPATHCOMP
2268 if (XSLT_CCTXT(style
)) {
2270 * Proposed by Jerome Pesenti
2271 * --------------------------
2272 * For better efficiency we'll reuse the compilation
2273 * context's XPath context. For the common stylesheet using
2274 * XPath expressions this will reduce compilation time to
2277 * See http://mail.gnome.org/archives/xslt/2006-April/msg00037.html
2279 xpathCtxt
= XSLT_CCTXT(style
)->xpathCtxt
;
2280 xpathCtxt
->doc
= style
->doc
;
2282 xpathCtxt
= xmlXPathNewContext(style
->doc
);
2284 xpathCtxt
= xmlXPathNewContext(style
->doc
);
2286 if (xpathCtxt
== NULL
)
2288 xpathCtxt
->dict
= style
->dict
;
2290 xpathCtxt
= xmlXPathNewContext(NULL
);
2291 if (xpathCtxt
== NULL
)
2294 xpathCtxt
->flags
= flags
;
2297 * Compile the expression.
2299 ret
= xmlXPathCtxtCompile(xpathCtxt
, str
);
2301 #ifdef XSLT_REFACTORED_XPATHCOMP
2302 if ((style
== NULL
) || (! XSLT_CCTXT(style
))) {
2303 xmlXPathFreeContext(xpathCtxt
);
2306 xmlXPathFreeContext(xpathCtxt
);
2309 * TODO: there is a lot of optimizations which should be possible
2310 * like variable slot precomputations, function precomputations, etc.
2318 * @style: the stylesheet
2319 * @str: the XPath expression
2321 * Compile an XPath expression
2323 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2324 * the caller has to free the object.
2327 xsltXPathCompile(xsltStylesheetPtr style
, const xmlChar
*str
) {
2328 return(xsltXPathCompileFlags(style
, str
, 0));
2331 /************************************************************************
2333 * Hooks for the debugger *
2335 ************************************************************************/
2338 * There is currently only 3 debugging callback defined
2339 * Debugger callbacks are disabled by default
2341 #define XSLT_CALLBACK_NUMBER 3
2343 typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks
;
2344 typedef xsltDebuggerCallbacks
*xsltDebuggerCallbacksPtr
;
2345 struct _xsltDebuggerCallbacks
{
2346 xsltHandleDebuggerCallback handler
;
2347 xsltAddCallCallback add
;
2348 xsltDropCallCallback drop
;
2351 static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks
= {
2360 * xsltSetDebuggerStatus:
2361 * @value : the value to be set
2363 * This function sets the value of xslDebugStatus.
2366 xsltSetDebuggerStatus(int value
)
2368 xslDebugStatus
= value
;
2372 * xsltGetDebuggerStatus:
2374 * Get xslDebugStatus.
2376 * Returns the value of xslDebugStatus.
2379 xsltGetDebuggerStatus(void)
2381 return(xslDebugStatus
);
2385 * xsltSetDebuggerCallbacks:
2386 * @no : number of callbacks
2387 * @block : the block of callbacks
2389 * This function allow to plug a debugger into the XSLT library
2390 * @block points to a block of memory containing the address of @no
2391 * callback routines.
2393 * Returns 0 in case of success and -1 in case of error
2396 xsltSetDebuggerCallbacks(int no
, void *block
)
2398 xsltDebuggerCallbacksPtr callbacks
;
2400 if ((block
== NULL
) || (no
!= XSLT_CALLBACK_NUMBER
))
2403 callbacks
= (xsltDebuggerCallbacksPtr
) block
;
2404 xsltDebuggerCurrentCallbacks
.handler
= callbacks
->handler
;
2405 xsltDebuggerCurrentCallbacks
.add
= callbacks
->add
;
2406 xsltDebuggerCurrentCallbacks
.drop
= callbacks
->drop
;
2411 * xslHandleDebugger:
2412 * @cur : source node being executed
2413 * @node : data node being processed
2414 * @templ : temlate that applies to node
2415 * @ctxt : the xslt transform context
2417 * If either cur or node are a breakpoint, or xslDebugStatus in state
2418 * where debugging must occcur at this time then transfer control
2419 * to the xslDebugBreak function
2422 xslHandleDebugger(xmlNodePtr cur
, xmlNodePtr node
, xsltTemplatePtr templ
,
2423 xsltTransformContextPtr ctxt
)
2425 if (xsltDebuggerCurrentCallbacks
.handler
!= NULL
)
2426 xsltDebuggerCurrentCallbacks
.handler(cur
, node
, templ
, ctxt
);
2431 * @templ : current template being applied
2432 * @source : the source node being processed
2434 * Add template "call" to call stack
2435 * Returns : 1 on sucess 0 otherwise an error may be printed if
2436 * WITH_XSLT_DEBUG_BREAKPOINTS is defined
2439 xslAddCall(xsltTemplatePtr templ
, xmlNodePtr source
)
2441 if (xsltDebuggerCurrentCallbacks
.add
!= NULL
)
2442 return(xsltDebuggerCurrentCallbacks
.add(templ
, source
));
2449 * Drop the topmost item off the call stack
2454 if (xsltDebuggerCurrentCallbacks
.drop
!= NULL
)
2455 xsltDebuggerCurrentCallbacks
.drop();