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
26 #ifdef HAVE_SYS_TIME_H
33 #include <libxml/xmlmemory.h>
34 #include <libxml/tree.h>
35 #include <libxml/HTMLtree.h>
36 #include <libxml/xmlerror.h>
37 #include <libxml/xmlIO.h>
38 #include "xsltutils.h"
39 #include "templates.h"
40 #include "xsltInternals.h"
42 #include "transform.h"
46 #define XSLT_WIN32_PERFORMANCE_COUNTER
49 /************************************************************************
51 * Convenience function *
53 ************************************************************************/
57 * @style: the stylesheet
59 * @name: the attribute name
60 * @nameSpace: the URI of the namespace
62 * Similar to xmlGetNsProp() but with a slightly different semantic
64 * Search and get the value of an attribute associated to a node
65 * This attribute has to be anchored in the namespace specified,
66 * or has no namespace and the element is in that namespace.
68 * This does the entity substitution.
69 * This function looks in DTD attribute declaration for #FIXED or
70 * default declaration values unless DTD use has been turned off.
72 * Returns the attribute value or NULL if not found. The string is allocated
73 * in the stylesheet dictionary.
76 xsltGetCNsProp(xsltStylesheetPtr style
, xmlNodePtr node
,
77 const xmlChar
*name
, const xmlChar
*nameSpace
) {
84 if ((node
== NULL
) || (style
== NULL
) || (style
->dict
== NULL
))
87 if (nameSpace
== NULL
)
88 return xmlGetProp(node
, name
);
90 if (node
->type
== XML_NAMESPACE_DECL
)
92 if (node
->type
== XML_ELEMENT_NODE
)
93 prop
= node
->properties
;
96 while (prop
!= NULL
) {
99 * - same attribute names
100 * - and the attribute carrying that namespace
102 if ((xmlStrEqual(prop
->name
, name
)) &&
103 (((prop
->ns
== NULL
) && (node
->ns
!= NULL
) &&
104 (xmlStrEqual(node
->ns
->href
, nameSpace
))) ||
105 ((prop
->ns
!= NULL
) &&
106 (xmlStrEqual(prop
->ns
->href
, nameSpace
))))) {
108 tmp
= xmlNodeListGetString(node
->doc
, prop
->children
, 1);
110 ret
= xmlDictLookup(style
->dict
, BAD_CAST
"", 0);
112 ret
= xmlDictLookup(style
->dict
, tmp
, -1);
121 * Check if there is a default declaration in the internal
122 * or external subsets
126 if (doc
->intSubset
!= NULL
) {
127 xmlAttributePtr attrDecl
;
129 attrDecl
= xmlGetDtdAttrDesc(doc
->intSubset
, node
->name
, name
);
130 if ((attrDecl
== NULL
) && (doc
->extSubset
!= NULL
))
131 attrDecl
= xmlGetDtdAttrDesc(doc
->extSubset
, node
->name
, name
);
133 if ((attrDecl
!= NULL
) && (attrDecl
->prefix
!= NULL
)) {
135 * The DTD declaration only allows a prefix search
137 ns
= xmlSearchNs(doc
, node
, attrDecl
->prefix
);
138 if ((ns
!= NULL
) && (xmlStrEqual(ns
->href
, nameSpace
)))
139 return(xmlDictLookup(style
->dict
,
140 attrDecl
->defaultValue
, -1));
149 * @name: the attribute name
150 * @nameSpace: the URI of the namespace
152 * Similar to xmlGetNsProp() but with a slightly different semantic
154 * Search and get the value of an attribute associated to a node
155 * This attribute has to be anchored in the namespace specified,
156 * or has no namespace and the element is in that namespace.
158 * This does the entity substitution.
159 * This function looks in DTD attribute declaration for #FIXED or
160 * default declaration values unless DTD use has been turned off.
162 * Returns the attribute value or NULL if not found.
163 * It's up to the caller to free the memory.
166 xsltGetNsProp(xmlNodePtr node
, const xmlChar
*name
, const xmlChar
*nameSpace
) {
174 if (nameSpace
== NULL
)
175 return xmlGetProp(node
, name
);
177 if (node
->type
== XML_NAMESPACE_DECL
)
179 if (node
->type
== XML_ELEMENT_NODE
)
180 prop
= node
->properties
;
184 * TODO: Substitute xmlGetProp() for xmlGetNsProp(), since the former
185 * is not namespace-aware and will return an attribute with equal
186 * name regardless of its namespace.
188 * <xsl:element foo:name="myName"/>
189 * So this would return "myName" even if an attribute @name
190 * in the XSLT was requested.
192 while (prop
!= NULL
) {
195 * - same attribute names
196 * - and the attribute carrying that namespace
198 if ((xmlStrEqual(prop
->name
, name
)) &&
199 (((prop
->ns
== NULL
) && (node
->ns
!= NULL
) &&
200 (xmlStrEqual(node
->ns
->href
, nameSpace
))) ||
201 ((prop
->ns
!= NULL
) &&
202 (xmlStrEqual(prop
->ns
->href
, nameSpace
))))) {
205 ret
= xmlNodeListGetString(node
->doc
, prop
->children
, 1);
206 if (ret
== NULL
) return(xmlStrdup((xmlChar
*)""));
213 * Check if there is a default declaration in the internal
214 * or external subsets
218 if (doc
->intSubset
!= NULL
) {
219 xmlAttributePtr attrDecl
;
221 attrDecl
= xmlGetDtdAttrDesc(doc
->intSubset
, node
->name
, name
);
222 if ((attrDecl
== NULL
) && (doc
->extSubset
!= NULL
))
223 attrDecl
= xmlGetDtdAttrDesc(doc
->extSubset
, node
->name
, name
);
225 if ((attrDecl
!= NULL
) && (attrDecl
->prefix
!= NULL
)) {
227 * The DTD declaration only allows a prefix search
229 ns
= xmlSearchNs(doc
, node
, attrDecl
->prefix
);
230 if ((ns
!= NULL
) && (xmlStrEqual(ns
->href
, nameSpace
)))
231 return(xmlStrdup(attrDecl
->defaultValue
));
240 * @utf: a sequence of UTF-8 encoded bytes
241 * @len: a pointer to @bytes len
243 * Read one UTF8 Char from @utf
244 * Function copied from libxml2 xmlGetUTF8Char() ... to discard ultimately
245 * and use the original API
247 * Returns the char value or -1 in case of error and update @len with the
248 * number of bytes used
251 xsltGetUTF8Char(const unsigned char *utf
, int *len
) {
265 if ((utf
[1] & 0xc0) != 0x80)
267 if ((c
& 0xe0) == 0xe0) {
270 if ((utf
[2] & 0xc0) != 0x80)
272 if ((c
& 0xf0) == 0xf0) {
275 if ((c
& 0xf8) != 0xf0 || (utf
[3] & 0xc0) != 0x80)
279 c
= (utf
[0] & 0x7) << 18;
280 c
|= (utf
[1] & 0x3f) << 12;
281 c
|= (utf
[2] & 0x3f) << 6;
286 c
= (utf
[0] & 0xf) << 12;
287 c
|= (utf
[1] & 0x3f) << 6;
293 c
= (utf
[0] & 0x1f) << 6;
310 * @utf: a sequence of UTF-8 encoded bytes
311 * @len: a pointer to @bytes len
313 * Read one UTF8 Char from a null-terminated string.
315 * Returns the char value or -1 in case of error and update @len with the
316 * number of bytes used
319 xsltGetUTF8CharZ(const unsigned char *utf
, int *len
) {
329 if ((utf
[1] & 0xc0) != 0x80)
331 if ((c
& 0xe0) == 0xe0) {
332 if ((utf
[2] & 0xc0) != 0x80)
334 if ((c
& 0xf0) == 0xf0) {
335 if ((c
& 0xf8) != 0xf0 || (utf
[3] & 0xc0) != 0x80)
339 c
= (utf
[0] & 0x7) << 18;
340 c
|= (utf
[1] & 0x3f) << 12;
341 c
|= (utf
[2] & 0x3f) << 6;
346 c
= (utf
[0] & 0xf) << 12;
347 c
|= (utf
[1] & 0x3f) << 6;
353 c
= (utf
[0] & 0x1f) << 6;
368 #ifdef XSLT_REFACTORED
371 * xsltPointerListAddSize:
372 * @list: the pointer list structure
373 * @item: the item to be stored
374 * @initialSize: the initial size of the list
376 * Adds an item to the list.
378 * Returns the position of the added item in the list or
379 * -1 in case of an error.
382 xsltPointerListAddSize(xsltPointerListPtr list
,
386 if (list
->items
== NULL
) {
387 if (initialSize
<= 0)
389 list
->items
= (void **) xmlMalloc(
390 initialSize
* sizeof(void *));
391 if (list
->items
== NULL
) {
392 xsltGenericError(xsltGenericErrorContext
,
393 "xsltPointerListAddSize: memory allocation failure.\n");
397 list
->size
= initialSize
;
398 } else if (list
->size
<= list
->number
) {
400 list
->items
= (void **) xmlRealloc(list
->items
,
401 list
->size
* sizeof(void *));
402 if (list
->items
== NULL
) {
403 xsltGenericError(xsltGenericErrorContext
,
404 "xsltPointerListAddSize: memory re-allocation failure.\n");
409 list
->items
[list
->number
++] = item
;
414 * xsltPointerListCreate:
415 * @initialSize: the initial size for the list
417 * Creates an xsltPointerList structure.
419 * Returns a xsltPointerList structure or NULL in case of an error.
422 xsltPointerListCreate(int initialSize
)
424 xsltPointerListPtr ret
;
426 ret
= xmlMalloc(sizeof(xsltPointerList
));
428 xsltGenericError(xsltGenericErrorContext
,
429 "xsltPointerListCreate: memory allocation failure.\n");
432 memset(ret
, 0, sizeof(xsltPointerList
));
433 if (initialSize
> 0) {
434 xsltPointerListAddSize(ret
, NULL
, initialSize
);
441 * xsltPointerListFree:
442 * @list: pointer to the list to be freed
444 * Frees the xsltPointerList structure. This does not free
445 * the content of the list.
448 xsltPointerListFree(xsltPointerListPtr list
)
452 if (list
->items
!= NULL
)
453 xmlFree(list
->items
);
458 * xsltPointerListClear:
459 * @list: pointer to the list to be cleared
461 * Resets the list, but does not free the allocated array
462 * and does not free the content of the list.
465 xsltPointerListClear(xsltPointerListPtr list
)
467 if (list
->items
!= NULL
) {
468 xmlFree(list
->items
);
475 #endif /* XSLT_REFACTORED */
477 /************************************************************************
479 * Handling of XSLT stylesheets messages *
481 ************************************************************************/
485 * @ctxt: an XSLT processing context
486 * @node: The current node
487 * @inst: The node containing the message instruction
489 * Process and xsl:message construct
492 xsltMessage(xsltTransformContextPtr ctxt
, xmlNodePtr node
, xmlNodePtr inst
) {
493 xmlGenericErrorFunc error
= xsltGenericError
;
494 void *errctx
= xsltGenericErrorContext
;
495 xmlChar
*prop
, *message
;
498 if ((ctxt
== NULL
) || (inst
== NULL
))
501 if (ctxt
->error
!= NULL
) {
503 errctx
= ctxt
->errctx
;
506 prop
= xmlGetNsProp(inst
, (const xmlChar
*)"terminate", NULL
);
508 if (xmlStrEqual(prop
, (const xmlChar
*)"yes")) {
510 } else if (xmlStrEqual(prop
, (const xmlChar
*)"no")) {
513 xsltTransformError(ctxt
, NULL
, inst
,
514 "xsl:message : terminate expecting 'yes' or 'no'\n");
518 message
= xsltEvalTemplateString(ctxt
, node
, inst
);
519 if (message
!= NULL
) {
520 int len
= xmlStrlen(message
);
522 error(errctx
, "%s", (const char *)message
);
523 if ((len
> 0) && (message
[len
- 1] != '\n'))
528 ctxt
->state
= XSLT_STATE_STOPPED
;
531 /************************************************************************
533 * Handling of out of context errors *
535 ************************************************************************/
537 #define XSLT_GET_VAR_STR(msg, str) { \
543 str = (char *) xmlMalloc(150); \
549 while (size < 64000) { \
551 chars = vsnprintf(str, size, msg, ap); \
553 if ((chars > -1) && (chars < size)) \
559 if ((larger = (char *) xmlRealloc(str, size)) == NULL) {\
567 * xsltGenericErrorDefaultFunc:
568 * @ctx: an error context
569 * @msg: the message to display/transmit
570 * @...: extra parameters for the message display
572 * Default handler for out of context error messages.
574 static void LIBXSLT_ATTR_FORMAT(2,3)
575 xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED
, const char *msg
, ...) {
578 if (xsltGenericErrorContext
== NULL
)
579 xsltGenericErrorContext
= (void *) stderr
;
582 vfprintf((FILE *)xsltGenericErrorContext
, msg
, args
);
586 xmlGenericErrorFunc xsltGenericError
= xsltGenericErrorDefaultFunc
;
587 void *xsltGenericErrorContext
= NULL
;
591 * xsltSetGenericErrorFunc:
592 * @ctx: the new error handling context
593 * @handler: the new handler function
595 * Function to reset the handler and the error context for out of
596 * context error messages.
597 * This simply means that @handler will be called for subsequent
598 * error messages while not parsing nor validating. And @ctx will
599 * be passed as first argument to @handler
600 * One can simply force messages to be emitted to another FILE * than
601 * stderr by setting @ctx to this file handle and @handler to NULL.
604 xsltSetGenericErrorFunc(void *ctx
, xmlGenericErrorFunc handler
) {
605 xsltGenericErrorContext
= ctx
;
607 xsltGenericError
= handler
;
609 xsltGenericError
= xsltGenericErrorDefaultFunc
;
613 * xsltGenericDebugDefaultFunc:
614 * @ctx: an error context
615 * @msg: the message to display/transmit
616 * @...: extra parameters for the message display
618 * Default handler for out of context error messages.
620 static void LIBXSLT_ATTR_FORMAT(2,3)
621 xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED
, const char *msg
, ...) {
624 if (xsltGenericDebugContext
== NULL
)
628 vfprintf((FILE *)xsltGenericDebugContext
, msg
, args
);
632 xmlGenericErrorFunc xsltGenericDebug
= xsltGenericDebugDefaultFunc
;
633 void *xsltGenericDebugContext
= NULL
;
637 * xsltSetGenericDebugFunc:
638 * @ctx: the new error handling context
639 * @handler: the new handler function
641 * Function to reset the handler and the error context for out of
642 * context error messages.
643 * This simply means that @handler will be called for subsequent
644 * error messages while not parsing or validating. And @ctx will
645 * be passed as first argument to @handler
646 * One can simply force messages to be emitted to another FILE * than
647 * stderr by setting @ctx to this file handle and @handler to NULL.
650 xsltSetGenericDebugFunc(void *ctx
, xmlGenericErrorFunc handler
) {
651 xsltGenericDebugContext
= ctx
;
653 xsltGenericDebug
= handler
;
655 xsltGenericDebug
= xsltGenericDebugDefaultFunc
;
659 * xsltPrintErrorContext:
660 * @ctxt: the transformation context
661 * @style: the stylesheet
662 * @node: the current node being processed
664 * Display the context of an error.
667 xsltPrintErrorContext(xsltTransformContextPtr ctxt
,
668 xsltStylesheetPtr style
, xmlNodePtr node
) {
670 const xmlChar
*file
= NULL
;
671 const xmlChar
*name
= NULL
;
672 const char *type
= "error";
673 xmlGenericErrorFunc error
= xsltGenericError
;
674 void *errctx
= xsltGenericErrorContext
;
677 if (ctxt
->state
== XSLT_STATE_OK
)
678 ctxt
->state
= XSLT_STATE_ERROR
;
679 if (ctxt
->error
!= NULL
) {
681 errctx
= ctxt
->errctx
;
684 if ((node
== NULL
) && (ctxt
!= NULL
))
688 if ((node
->type
== XML_DOCUMENT_NODE
) ||
689 (node
->type
== XML_HTML_DOCUMENT_NODE
)) {
690 xmlDocPtr doc
= (xmlDocPtr
) node
;
694 line
= xmlGetLineNo(node
);
695 if ((node
->doc
!= NULL
) && (node
->doc
->URL
!= NULL
))
696 file
= node
->doc
->URL
;
697 if (node
->name
!= NULL
)
703 type
= "runtime error";
704 else if (style
!= NULL
) {
705 #ifdef XSLT_REFACTORED
706 if (XSLT_CCTXT(style
)->errSeverity
== XSLT_ERROR_SEVERITY_WARNING
)
707 type
= "compilation warning";
709 type
= "compilation error";
711 type
= "compilation error";
715 if ((file
!= NULL
) && (line
!= 0) && (name
!= NULL
))
716 error(errctx
, "%s: file %s line %d element %s\n",
717 type
, file
, line
, name
);
718 else if ((file
!= NULL
) && (name
!= NULL
))
719 error(errctx
, "%s: file %s element %s\n", type
, file
, name
);
720 else if ((file
!= NULL
) && (line
!= 0))
721 error(errctx
, "%s: file %s line %d\n", type
, file
, line
);
722 else if (file
!= NULL
)
723 error(errctx
, "%s: file %s\n", type
, file
);
724 else if (name
!= NULL
)
725 error(errctx
, "%s: element %s\n", type
, name
);
727 error(errctx
, "%s\n", type
);
731 * xsltSetTransformErrorFunc:
732 * @ctxt: the XSLT transformation context
733 * @ctx: the new error handling context
734 * @handler: the new handler function
736 * Function to reset the handler and the error context for out of
737 * context error messages specific to a given XSLT transromation.
739 * This simply means that @handler will be called for subsequent
740 * error messages while running the transformation.
743 xsltSetTransformErrorFunc(xsltTransformContextPtr ctxt
,
744 void *ctx
, xmlGenericErrorFunc handler
)
746 ctxt
->error
= handler
;
751 * xsltTransformError:
752 * @ctxt: an XSLT transformation context
753 * @style: the XSLT stylesheet used
754 * @node: the current node in the stylesheet
755 * @msg: the message to display/transmit
756 * @...: extra parameters for the message display
758 * Display and format an error messages, gives file, line, position and
759 * extra parameters, will use the specific transformation context if available
762 xsltTransformError(xsltTransformContextPtr ctxt
,
763 xsltStylesheetPtr style
,
765 const char *msg
, ...) {
766 xmlGenericErrorFunc error
= xsltGenericError
;
767 void *errctx
= xsltGenericErrorContext
;
771 if (ctxt
->state
== XSLT_STATE_OK
)
772 ctxt
->state
= XSLT_STATE_ERROR
;
773 if (ctxt
->error
!= NULL
) {
775 errctx
= ctxt
->errctx
;
778 if ((node
== NULL
) && (ctxt
!= NULL
))
780 xsltPrintErrorContext(ctxt
, style
, node
);
781 XSLT_GET_VAR_STR(msg
, str
);
782 error(errctx
, "%s", str
);
787 /************************************************************************
791 ************************************************************************/
795 * @dict: a dictionary
796 * @name: the full QName
797 * @prefix: the return value
799 * Split QNames into prefix and local names, both allocated from a dictionary.
801 * Returns: the localname or NULL in case of error.
804 xsltSplitQName(xmlDictPtr dict
, const xmlChar
*name
, const xmlChar
**prefix
) {
806 const xmlChar
*ret
= NULL
;
809 if ((name
== NULL
) || (dict
== NULL
)) return(NULL
);
811 return(xmlDictLookup(dict
, name
, -1));
812 while ((name
[len
] != 0) && (name
[len
] != ':')) len
++;
813 if (name
[len
] == 0) return(xmlDictLookup(dict
, name
, -1));
814 *prefix
= xmlDictLookup(dict
, name
, len
);
815 ret
= xmlDictLookup(dict
, &name
[len
+ 1], -1);
821 * @node: the node holding the QName
822 * @name: pointer to the initial QName value
824 * This function analyzes @name, if the name contains a prefix,
825 * the function seaches the associated namespace in scope for it.
826 * It will also replace @name value with the NCName, the old value being
828 * Errors in the prefix lookup are signalled by setting @name to NULL.
830 * NOTE: the namespace returned is a pointer to the place where it is
831 * defined and hence has the same lifespan as the document holding it.
833 * Returns the namespace URI if there is a prefix, or NULL if @name is
837 xsltGetQNameURI(xmlNodePtr node
, xmlChar
** name
)
846 if ((qname
== NULL
) || (*qname
== 0))
849 xsltGenericError(xsltGenericErrorContext
,
850 "QName: no element for namespace lookup %s\n",
857 /* nasty but valid */
862 * we are not trying to validate but just to cut, and yes it will
863 * work even if this is a set of UTF-8 encoded chars
865 while ((qname
[len
] != 0) && (qname
[len
] != ':'))
872 * handle xml: separately, this one is magical
874 if ((qname
[0] == 'x') && (qname
[1] == 'm') &&
875 (qname
[2] == 'l') && (qname
[3] == ':')) {
878 *name
= xmlStrdup(&qname
[4]);
880 return(XML_XML_NAMESPACE
);
884 ns
= xmlSearchNs(node
->doc
, node
, qname
);
886 xsltGenericError(xsltGenericErrorContext
,
887 "%s:%s : no namespace bound to prefix %s\n",
888 qname
, &qname
[len
+ 1], qname
);
893 *name
= xmlStrdup(&qname
[len
+ 1]);
900 * @style: stylesheet pointer
901 * @node: the node holding the QName
902 * @name: pointer to the initial QName value
904 * This function is similar to xsltGetQNameURI, but is used when
905 * @name is a dictionary entry.
907 * Returns the namespace URI if there is a prefix, or NULL if @name is
911 xsltGetQNameURI2(xsltStylesheetPtr style
, xmlNodePtr node
,
912 const xmlChar
**name
) {
919 qname
= (xmlChar
*)*name
;
920 if ((qname
== NULL
) || (*qname
== 0))
923 xsltGenericError(xsltGenericErrorContext
,
924 "QName: no element for namespace lookup %s\n",
931 * we are not trying to validate but just to cut, and yes it will
932 * work even if this is a set of UTF-8 encoded chars
934 while ((qname
[len
] != 0) && (qname
[len
] != ':'))
941 * handle xml: separately, this one is magical
943 if ((qname
[0] == 'x') && (qname
[1] == 'm') &&
944 (qname
[2] == 'l') && (qname
[3] == ':')) {
947 *name
= xmlDictLookup(style
->dict
, &qname
[4], -1);
948 return(XML_XML_NAMESPACE
);
951 qname
= xmlStrndup(*name
, len
);
952 ns
= xmlSearchNs(node
->doc
, node
, qname
);
955 xsltTransformError(NULL
, style
, node
,
956 "No namespace bound to prefix '%s'.\n",
960 xsltGenericError(xsltGenericErrorContext
,
961 "%s : no namespace bound to prefix %s\n",
968 *name
= xmlDictLookup(style
->dict
, (*name
)+len
+1, -1);
973 /************************************************************************
977 ************************************************************************/
980 * xsltDocumentSortFunction:
981 * @list: the node set
983 * reorder the current node list @list accordingly to the document order
984 * This function is slow, obsolete and should not be used anymore.
987 xsltDocumentSortFunction(xmlNodeSetPtr list
) {
997 /* TODO: sort is really not optimized, does it needs to ? */
998 for (i
= 0;i
< len
-1;i
++) {
999 for (j
= i
+ 1; j
< len
; j
++) {
1000 tst
= xmlXPathCmpNodes(list
->nodeTab
[i
], list
->nodeTab
[j
]);
1002 node
= list
->nodeTab
[i
];
1003 list
->nodeTab
[i
] = list
->nodeTab
[j
];
1004 list
->nodeTab
[j
] = node
;
1011 * xsltComputeSortResultInternal:
1012 * @ctxt: a XSLT process context
1013 * @sort: xsl:sort node
1014 * @number: data-type is number
1015 * @locale: transform strings according to locale
1017 * reorder the current node list accordingly to the set of sorting
1018 * requirement provided by the array of nodes.
1020 * Returns a ordered XPath nodeset or NULL in case of error.
1022 static xmlXPathObjectPtr
*
1023 xsltComputeSortResultInternal(xsltTransformContextPtr ctxt
, xmlNodePtr sort
,
1024 int number
, void *locale
) {
1025 #ifdef XSLT_REFACTORED
1026 xsltStyleItemSortPtr comp
;
1028 const xsltStylePreComp
*comp
;
1030 xmlXPathObjectPtr
*results
= NULL
;
1031 xmlNodeSetPtr list
= NULL
;
1032 xmlXPathObjectPtr res
;
1037 int oldPos
, oldSize
;
1039 xmlNsPtr
*oldNamespaces
;
1043 xsltGenericError(xsltGenericErrorContext
,
1044 "xsl:sort : compilation failed\n");
1048 if ((comp
->select
== NULL
) || (comp
->comp
== NULL
))
1051 list
= ctxt
->nodeList
;
1052 if ((list
== NULL
) || (list
->nodeNr
<= 1))
1057 /* TODO: xsl:sort lang attribute */
1058 /* TODO: xsl:sort case-order attribute */
1061 results
= xmlMalloc(len
* sizeof(xmlXPathObjectPtr
));
1062 if (results
== NULL
) {
1063 xsltGenericError(xsltGenericErrorContext
,
1064 "xsltComputeSortResult: memory allocation failure\n");
1068 oldNode
= ctxt
->node
;
1069 oldInst
= ctxt
->inst
;
1070 oldPos
= ctxt
->xpathCtxt
->proximityPosition
;
1071 oldSize
= ctxt
->xpathCtxt
->contextSize
;
1072 oldNsNr
= ctxt
->xpathCtxt
->nsNr
;
1073 oldNamespaces
= ctxt
->xpathCtxt
->namespaces
;
1074 for (i
= 0;i
< len
;i
++) {
1076 ctxt
->xpathCtxt
->contextSize
= len
;
1077 ctxt
->xpathCtxt
->proximityPosition
= i
+ 1;
1078 ctxt
->node
= list
->nodeTab
[i
];
1079 ctxt
->xpathCtxt
->node
= ctxt
->node
;
1080 #ifdef XSLT_REFACTORED
1081 if (comp
->inScopeNs
!= NULL
) {
1082 ctxt
->xpathCtxt
->namespaces
= comp
->inScopeNs
->list
;
1083 ctxt
->xpathCtxt
->nsNr
= comp
->inScopeNs
->xpathNumber
;
1085 ctxt
->xpathCtxt
->namespaces
= NULL
;
1086 ctxt
->xpathCtxt
->nsNr
= 0;
1089 ctxt
->xpathCtxt
->namespaces
= comp
->nsList
;
1090 ctxt
->xpathCtxt
->nsNr
= comp
->nsNr
;
1092 res
= xmlXPathCompiledEval(comp
->comp
, ctxt
->xpathCtxt
);
1094 if (res
->type
!= XPATH_STRING
)
1095 res
= xmlXPathConvertString(res
);
1097 res
= xmlXPathConvertNumber(res
);
1100 res
->index
= i
; /* Save original pos for dupl resolv */
1102 if (res
->type
== XPATH_NUMBER
) {
1105 #ifdef WITH_XSLT_DEBUG_PROCESS
1106 xsltGenericDebug(xsltGenericDebugContext
,
1107 "xsltComputeSortResult: select didn't evaluate to a number\n");
1112 if (res
->type
== XPATH_STRING
) {
1113 if (locale
!= NULL
) {
1114 xmlChar
*str
= res
->stringval
;
1115 xmlChar
*sortKey
= ctxt
->genSortKey(locale
, str
);
1117 if (sortKey
== NULL
) {
1118 xsltTransformError(ctxt
, NULL
, sort
,
1119 "xsltComputeSortResult: sort key is null\n");
1121 res
->stringval
= sortKey
;
1128 #ifdef WITH_XSLT_DEBUG_PROCESS
1129 xsltGenericDebug(xsltGenericDebugContext
,
1130 "xsltComputeSortResult: select didn't evaluate to a string\n");
1136 ctxt
->state
= XSLT_STATE_STOPPED
;
1140 ctxt
->node
= oldNode
;
1141 ctxt
->inst
= oldInst
;
1142 ctxt
->xpathCtxt
->contextSize
= oldSize
;
1143 ctxt
->xpathCtxt
->proximityPosition
= oldPos
;
1144 ctxt
->xpathCtxt
->nsNr
= oldNsNr
;
1145 ctxt
->xpathCtxt
->namespaces
= oldNamespaces
;
1151 * xsltComputeSortResult:
1152 * @ctxt: a XSLT process context
1155 * reorder the current node list accordingly to the set of sorting
1156 * requirement provided by the array of nodes.
1158 * Returns a ordered XPath nodeset or NULL in case of error.
1161 xsltComputeSortResult(xsltTransformContextPtr ctxt
, xmlNodePtr sort
) {
1162 const xsltStylePreComp
*comp
= sort
->psvi
;
1166 number
= comp
->number
;
1167 return xsltComputeSortResultInternal(ctxt
, sort
, number
,
1172 * xsltDefaultSortFunction:
1173 * @ctxt: a XSLT process context
1174 * @sorts: array of sort nodes
1175 * @nbsorts: the number of sorts in the array
1177 * reorder the current node list accordingly to the set of sorting
1178 * requirement provided by the arry of nodes.
1181 xsltDefaultSortFunction(xsltTransformContextPtr ctxt
, xmlNodePtr
*sorts
,
1183 #ifdef XSLT_REFACTORED
1184 xsltStyleItemSortPtr comp
;
1186 const xsltStylePreComp
*comp
;
1188 xmlXPathObjectPtr
*resultsTab
[XSLT_MAX_SORT
];
1189 xmlXPathObjectPtr
*results
= NULL
, *res
;
1190 xmlNodeSetPtr list
= NULL
;
1196 xmlXPathObjectPtr tmp
;
1197 int number
[XSLT_MAX_SORT
], desc
[XSLT_MAX_SORT
];
1198 void *locale
[XSLT_MAX_SORT
];
1200 if ((ctxt
== NULL
) || (sorts
== NULL
) || (nbsorts
<= 0) ||
1201 (nbsorts
>= XSLT_MAX_SORT
))
1203 if (sorts
[0] == NULL
)
1205 comp
= sorts
[0]->psvi
;
1209 list
= ctxt
->nodeList
;
1210 if ((list
== NULL
) || (list
->nodeNr
<= 1))
1211 return; /* nothing to do */
1213 for (j
= 0; j
< nbsorts
; j
++) {
1216 comp
= sorts
[j
]->psvi
;
1217 if ((comp
->stype
== NULL
) && (comp
->has_stype
!= 0)) {
1219 xsltEvalAttrValueTemplate(ctxt
, sorts
[j
],
1220 BAD_CAST
"data-type", NULL
);
1222 if (stype
!= NULL
) {
1223 if (xmlStrEqual(stype
, (const xmlChar
*) "text"))
1225 else if (xmlStrEqual(stype
, (const xmlChar
*) "number"))
1228 xsltTransformError(ctxt
, NULL
, sorts
[j
],
1229 "xsltDoSortFunction: no support for data-type = %s\n",
1235 number
[j
] = comp
->number
;
1237 if ((comp
->order
== NULL
) && (comp
->has_order
!= 0)) {
1238 xmlChar
*order
= xsltEvalAttrValueTemplate(ctxt
, sorts
[j
],
1239 BAD_CAST
"order", NULL
);
1241 if (order
!= NULL
) {
1242 if (xmlStrEqual(order
, (const xmlChar
*) "ascending"))
1244 else if (xmlStrEqual(order
, (const xmlChar
*) "descending"))
1247 xsltTransformError(ctxt
, NULL
, sorts
[j
],
1248 "xsltDoSortFunction: invalid value %s for order\n",
1254 desc
[j
] = comp
->descending
;
1256 if ((comp
->lang
== NULL
) && (comp
->has_lang
!= 0)) {
1257 lang
= xsltEvalAttrValueTemplate(ctxt
, sorts
[j
],
1261 lang
= (xmlChar
*) comp
->lang
;
1264 locale
[j
] = ctxt
->newLocale(lang
, comp
->lower_first
);
1265 if (lang
!= comp
->lang
)
1274 resultsTab
[0] = xsltComputeSortResultInternal(ctxt
, sorts
[0], number
[0],
1276 for (i
= 1;i
< XSLT_MAX_SORT
;i
++)
1277 resultsTab
[i
] = NULL
;
1279 results
= resultsTab
[0];
1281 comp
= sorts
[0]->psvi
;
1282 if (results
== NULL
)
1285 /* Shell's sort of node-set */
1286 for (incr
= len
/ 2; incr
> 0; incr
/= 2) {
1287 for (i
= incr
; i
< len
; i
++) {
1289 if (results
[i
] == NULL
)
1293 if (results
[j
] == NULL
)
1297 /* We make NaN smaller than number in accordance
1299 if (xmlXPathIsNaN(results
[j
]->floatval
)) {
1300 if (xmlXPathIsNaN(results
[j
+ incr
]->floatval
))
1304 } else if (xmlXPathIsNaN(results
[j
+ incr
]->floatval
))
1306 else if (results
[j
]->floatval
==
1307 results
[j
+ incr
]->floatval
)
1309 else if (results
[j
]->floatval
>
1310 results
[j
+ incr
]->floatval
)
1314 tst
= xmlStrcmp(results
[j
]->stringval
,
1315 results
[j
+ incr
]->stringval
);
1322 * Okay we need to use multi level sorts
1325 while (depth
< nbsorts
) {
1326 if (sorts
[depth
] == NULL
)
1328 comp
= sorts
[depth
]->psvi
;
1333 * Compute the result of the next level for the
1334 * full set, this might be optimized ... or not
1336 if (resultsTab
[depth
] == NULL
)
1338 xsltComputeSortResultInternal(ctxt
,
1342 res
= resultsTab
[depth
];
1345 if (res
[j
] == NULL
) {
1346 if (res
[j
+incr
] != NULL
)
1348 } else if (res
[j
+incr
] == NULL
) {
1351 if (number
[depth
]) {
1352 /* We make NaN smaller than number in
1353 accordance with XSLT spec */
1354 if (xmlXPathIsNaN(res
[j
]->floatval
)) {
1355 if (xmlXPathIsNaN(res
[j
+
1360 } else if (xmlXPathIsNaN(res
[j
+ incr
]->
1363 else if (res
[j
]->floatval
== res
[j
+ incr
]->
1366 else if (res
[j
]->floatval
>
1367 res
[j
+ incr
]->floatval
)
1371 tst
= xmlStrcmp(res
[j
]->stringval
,
1372 res
[j
+ incr
]->stringval
);
1379 * if we still can't differenciate at this level
1380 * try one level deeper.
1388 tst
= results
[j
]->index
> results
[j
+ incr
]->index
;
1392 results
[j
] = results
[j
+ incr
];
1393 results
[j
+ incr
] = tmp
;
1394 node
= list
->nodeTab
[j
];
1395 list
->nodeTab
[j
] = list
->nodeTab
[j
+ incr
];
1396 list
->nodeTab
[j
+ incr
] = node
;
1398 while (depth
< nbsorts
) {
1399 if (sorts
[depth
] == NULL
)
1401 if (resultsTab
[depth
] == NULL
)
1403 res
= resultsTab
[depth
];
1405 res
[j
] = res
[j
+ incr
];
1406 res
[j
+ incr
] = tmp
;
1417 for (j
= 0; j
< nbsorts
; j
++) {
1418 if (locale
[j
] != NULL
) {
1419 ctxt
->freeLocale(locale
[j
]);
1421 if (resultsTab
[j
] != NULL
) {
1422 for (i
= 0;i
< len
;i
++)
1423 xmlXPathFreeObject(resultsTab
[j
][i
]);
1424 xmlFree(resultsTab
[j
]);
1430 static xsltSortFunc xsltSortFunction
= xsltDefaultSortFunction
;
1433 * xsltDoSortFunction:
1434 * @ctxt: a XSLT process context
1435 * @sorts: array of sort nodes
1436 * @nbsorts: the number of sorts in the array
1438 * reorder the current node list accordingly to the set of sorting
1439 * requirement provided by the arry of nodes.
1440 * This is a wrapper function, the actual function used is specified
1441 * using xsltSetCtxtSortFunc() to set the context specific sort function,
1442 * or xsltSetSortFunc() to set the global sort function.
1443 * If a sort function is set on the context, this will get called.
1444 * Otherwise the global sort function is called.
1447 xsltDoSortFunction(xsltTransformContextPtr ctxt
, xmlNodePtr
* sorts
,
1450 if (ctxt
->sortfunc
!= NULL
)
1451 (ctxt
->sortfunc
)(ctxt
, sorts
, nbsorts
);
1452 else if (xsltSortFunction
!= NULL
)
1453 xsltSortFunction(ctxt
, sorts
, nbsorts
);
1458 * @handler: the new handler function
1460 * DEPRECATED: Use xsltSetCtxtLocaleHandlers.
1462 * Function to reset the global handler for XSLT sorting.
1463 * If the handler is NULL, the default sort function will be used.
1466 xsltSetSortFunc(xsltSortFunc handler
) {
1467 if (handler
!= NULL
)
1468 xsltSortFunction
= handler
;
1470 xsltSortFunction
= xsltDefaultSortFunction
;
1474 * xsltSetCtxtSortFunc:
1475 * @ctxt: a XSLT process context
1476 * @handler: the new handler function
1478 * DEPRECATED: Use xsltSetCtxtLocaleHandlers.
1480 * Function to set the handler for XSLT sorting
1481 * for the specified context.
1482 * If the handler is NULL, then the global
1483 * sort function will be called
1486 xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt
, xsltSortFunc handler
) {
1487 ctxt
->sortfunc
= handler
;
1491 * xsltSetCtxtLocaleHandlers:
1492 * @ctxt: an XSLT transform context
1493 * @newLocale: locale constructor
1494 * @freeLocale: locale destructor
1495 * @genSortKey: sort key generator
1497 * Set the locale handlers.
1500 xsltSetCtxtLocaleHandlers(xsltTransformContextPtr ctxt
,
1501 xsltNewLocaleFunc newLocale
,
1502 xsltFreeLocaleFunc freeLocale
,
1503 xsltGenSortKeyFunc genSortKey
) {
1507 ctxt
->newLocale
= newLocale
;
1508 ctxt
->freeLocale
= freeLocale
;
1509 ctxt
->genSortKey
= genSortKey
;
1512 /************************************************************************
1516 ************************************************************************/
1519 * xsltSetCtxtParseOptions:
1520 * @ctxt: a XSLT process context
1521 * @options: a combination of libxml2 xmlParserOption
1523 * Change the default parser option passed by the XSLT engine to the
1524 * parser when using document() loading.
1526 * Returns the previous options or -1 in case of error
1529 xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt
, int options
)
1535 oldopts
= ctxt
->parserOptions
;
1537 oldopts
|= XML_PARSE_XINCLUDE
;
1538 ctxt
->parserOptions
= options
;
1539 if (options
& XML_PARSE_XINCLUDE
)
1546 /************************************************************************
1550 ************************************************************************/
1554 * @buf: an output buffer
1555 * @result: the result xmlDocPtr
1556 * @style: the stylesheet
1558 * Save the result @result obtained by applying the @style stylesheet
1559 * to an I/O output channel @buf
1561 * Returns the number of byte written or -1 in case of failure.
1564 xsltSaveResultTo(xmlOutputBufferPtr buf
, xmlDocPtr result
,
1565 xsltStylesheetPtr style
) {
1566 const xmlChar
*encoding
;
1568 const xmlChar
*method
;
1571 if ((buf
== NULL
) || (result
== NULL
) || (style
== NULL
))
1573 if ((result
->children
== NULL
) ||
1574 ((result
->children
->type
== XML_DTD_NODE
) &&
1575 (result
->children
->next
== NULL
)))
1578 if ((style
->methodURI
!= NULL
) &&
1579 ((style
->method
== NULL
) ||
1580 (!xmlStrEqual(style
->method
, (const xmlChar
*) "xhtml")))) {
1581 xsltGenericError(xsltGenericErrorContext
,
1582 "xsltSaveResultTo : unknown output method\n");
1586 base
= buf
->written
;
1588 XSLT_GET_IMPORT_PTR(method
, style
, method
)
1589 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1590 XSLT_GET_IMPORT_INT(indent
, style
, indent
);
1592 if ((method
== NULL
) && (result
->type
== XML_HTML_DOCUMENT_NODE
))
1593 method
= (const xmlChar
*) "html";
1595 if ((method
!= NULL
) &&
1596 (xmlStrEqual(method
, (const xmlChar
*) "html"))) {
1597 if (encoding
!= NULL
) {
1598 htmlSetMetaEncoding(result
, (const xmlChar
*) encoding
);
1600 htmlSetMetaEncoding(result
, (const xmlChar
*) "UTF-8");
1604 htmlDocContentDumpFormatOutput(buf
, result
, (const char *) encoding
,
1606 xmlOutputBufferFlush(buf
);
1607 } else if ((method
!= NULL
) &&
1608 (xmlStrEqual(method
, (const xmlChar
*) "xhtml"))) {
1609 if (encoding
!= NULL
) {
1610 htmlSetMetaEncoding(result
, (const xmlChar
*) encoding
);
1612 htmlSetMetaEncoding(result
, (const xmlChar
*) "UTF-8");
1614 htmlDocContentDumpOutput(buf
, result
, (const char *) encoding
);
1615 xmlOutputBufferFlush(buf
);
1616 } else if ((method
!= NULL
) &&
1617 (xmlStrEqual(method
, (const xmlChar
*) "text"))) {
1620 cur
= result
->children
;
1621 while (cur
!= NULL
) {
1622 if (cur
->type
== XML_TEXT_NODE
)
1623 xmlOutputBufferWriteString(buf
, (const char *) cur
->content
);
1628 if (cur
->children
!= NULL
) {
1629 if ((cur
->children
->type
!= XML_ENTITY_DECL
) &&
1630 (cur
->children
->type
!= XML_ENTITY_REF_NODE
) &&
1631 (cur
->children
->type
!= XML_ENTITY_NODE
)) {
1632 cur
= cur
->children
;
1636 if (cur
->next
!= NULL
) {
1645 if (cur
== (xmlNodePtr
) style
->doc
) {
1649 if (cur
->next
!= NULL
) {
1653 } while (cur
!= NULL
);
1655 xmlOutputBufferFlush(buf
);
1660 XSLT_GET_IMPORT_INT(omitXmlDecl
, style
, omitXmlDeclaration
);
1661 XSLT_GET_IMPORT_INT(standalone
, style
, standalone
);
1663 if (omitXmlDecl
!= 1) {
1664 xmlOutputBufferWriteString(buf
, "<?xml version=");
1665 if (result
->version
!= NULL
) {
1666 xmlOutputBufferWriteString(buf
, "\"");
1667 xmlOutputBufferWriteString(buf
, (const char *)result
->version
);
1668 xmlOutputBufferWriteString(buf
, "\"");
1670 xmlOutputBufferWriteString(buf
, "\"1.0\"");
1671 if (encoding
== NULL
) {
1672 if (result
->encoding
!= NULL
)
1673 encoding
= result
->encoding
;
1674 else if (result
->charset
!= XML_CHAR_ENCODING_UTF8
)
1675 encoding
= (const xmlChar
*)
1676 xmlGetCharEncodingName((xmlCharEncoding
)
1679 if (encoding
!= NULL
) {
1680 xmlOutputBufferWriteString(buf
, " encoding=");
1681 xmlOutputBufferWriteString(buf
, "\"");
1682 xmlOutputBufferWriteString(buf
, (const char *) encoding
);
1683 xmlOutputBufferWriteString(buf
, "\"");
1685 switch (standalone
) {
1687 xmlOutputBufferWriteString(buf
, " standalone=\"no\"");
1690 xmlOutputBufferWriteString(buf
, " standalone=\"yes\"");
1695 xmlOutputBufferWriteString(buf
, "?>\n");
1697 if (result
->children
!= NULL
) {
1698 xmlNodePtr children
= result
->children
;
1699 xmlNodePtr child
= children
;
1702 * Hack to avoid quadratic behavior when scanning
1703 * result->children in xmlGetIntSubset called by
1704 * xmlNodeDumpOutput.
1706 result
->children
= NULL
;
1708 while (child
!= NULL
) {
1709 xmlNodeDumpOutput(buf
, result
, child
, 0, (indent
== 1),
1710 (const char *) encoding
);
1711 if (indent
&& ((child
->type
== XML_DTD_NODE
) ||
1712 ((child
->type
== XML_COMMENT_NODE
) &&
1713 (child
->next
!= NULL
))))
1714 xmlOutputBufferWriteString(buf
, "\n");
1715 child
= child
->next
;
1718 xmlOutputBufferWriteString(buf
, "\n");
1720 result
->children
= children
;
1722 xmlOutputBufferFlush(buf
);
1724 return(buf
->written
- base
);
1728 * xsltSaveResultToFilename:
1729 * @URL: a filename or URL
1730 * @result: the result xmlDocPtr
1731 * @style: the stylesheet
1732 * @compression: the compression factor (0 - 9 included)
1734 * Save the result @result obtained by applying the @style stylesheet
1737 * Returns the number of byte written or -1 in case of failure.
1740 xsltSaveResultToFilename(const char *URL
, xmlDocPtr result
,
1741 xsltStylesheetPtr style
, int compression
) {
1742 xmlOutputBufferPtr buf
;
1743 const xmlChar
*encoding
;
1746 if ((URL
== NULL
) || (result
== NULL
) || (style
== NULL
))
1748 if (result
->children
== NULL
)
1751 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1752 if (encoding
!= NULL
) {
1753 xmlCharEncodingHandlerPtr encoder
;
1755 encoder
= xmlFindCharEncodingHandler((char *)encoding
);
1756 if ((encoder
!= NULL
) &&
1757 (xmlStrEqual((const xmlChar
*)encoder
->name
,
1758 (const xmlChar
*) "UTF-8")))
1760 buf
= xmlOutputBufferCreateFilename(URL
, encoder
, compression
);
1762 buf
= xmlOutputBufferCreateFilename(URL
, NULL
, compression
);
1766 xsltSaveResultTo(buf
, result
, style
);
1767 ret
= xmlOutputBufferClose(buf
);
1772 * xsltSaveResultToFile:
1773 * @file: a FILE * I/O
1774 * @result: the result xmlDocPtr
1775 * @style: the stylesheet
1777 * Save the result @result obtained by applying the @style stylesheet
1778 * to an open FILE * I/O.
1779 * This does not close the FILE @file
1781 * Returns the number of bytes written or -1 in case of failure.
1784 xsltSaveResultToFile(FILE *file
, xmlDocPtr result
, xsltStylesheetPtr style
) {
1785 xmlOutputBufferPtr buf
;
1786 const xmlChar
*encoding
;
1789 if ((file
== NULL
) || (result
== NULL
) || (style
== NULL
))
1791 if (result
->children
== NULL
)
1794 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1795 if (encoding
!= NULL
) {
1796 xmlCharEncodingHandlerPtr encoder
;
1798 encoder
= xmlFindCharEncodingHandler((char *)encoding
);
1799 if ((encoder
!= NULL
) &&
1800 (xmlStrEqual((const xmlChar
*)encoder
->name
,
1801 (const xmlChar
*) "UTF-8")))
1803 buf
= xmlOutputBufferCreateFile(file
, encoder
);
1805 buf
= xmlOutputBufferCreateFile(file
, NULL
);
1810 xsltSaveResultTo(buf
, result
, style
);
1811 ret
= xmlOutputBufferClose(buf
);
1816 * xsltSaveResultToFd:
1817 * @fd: a file descriptor
1818 * @result: the result xmlDocPtr
1819 * @style: the stylesheet
1821 * Save the result @result obtained by applying the @style stylesheet
1822 * to an open file descriptor
1823 * This does not close the descriptor.
1825 * Returns the number of bytes written or -1 in case of failure.
1828 xsltSaveResultToFd(int fd
, xmlDocPtr result
, xsltStylesheetPtr style
) {
1829 xmlOutputBufferPtr buf
;
1830 const xmlChar
*encoding
;
1833 if ((fd
< 0) || (result
== NULL
) || (style
== NULL
))
1835 if (result
->children
== NULL
)
1838 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1839 if (encoding
!= NULL
) {
1840 xmlCharEncodingHandlerPtr encoder
;
1842 encoder
= xmlFindCharEncodingHandler((char *)encoding
);
1843 if ((encoder
!= NULL
) &&
1844 (xmlStrEqual((const xmlChar
*)encoder
->name
,
1845 (const xmlChar
*) "UTF-8")))
1847 buf
= xmlOutputBufferCreateFd(fd
, encoder
);
1849 buf
= xmlOutputBufferCreateFd(fd
, NULL
);
1853 xsltSaveResultTo(buf
, result
, style
);
1854 ret
= xmlOutputBufferClose(buf
);
1859 * xsltSaveResultToString:
1860 * @doc_txt_ptr: Memory pointer for allocated XML text
1861 * @doc_txt_len: Length of the generated XML text
1862 * @result: the result xmlDocPtr
1863 * @style: the stylesheet
1865 * Save the result @result obtained by applying the @style stylesheet
1866 * to a new allocated string.
1868 * Returns 0 in case of success and -1 in case of error
1871 xsltSaveResultToString(xmlChar
**doc_txt_ptr
, int * doc_txt_len
,
1872 xmlDocPtr result
, xsltStylesheetPtr style
) {
1873 xmlOutputBufferPtr buf
;
1874 const xmlChar
*encoding
;
1876 *doc_txt_ptr
= NULL
;
1878 if (result
->children
== NULL
)
1881 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1882 if (encoding
!= NULL
) {
1883 xmlCharEncodingHandlerPtr encoder
;
1885 encoder
= xmlFindCharEncodingHandler((char *)encoding
);
1886 if ((encoder
!= NULL
) &&
1887 (xmlStrEqual((const xmlChar
*)encoder
->name
,
1888 (const xmlChar
*) "UTF-8")))
1890 buf
= xmlAllocOutputBuffer(encoder
);
1892 xmlCharEncCloseFunc(encoder
);
1894 buf
= xmlAllocOutputBuffer(NULL
);
1898 xsltSaveResultTo(buf
, result
, style
);
1899 #ifdef LIBXML2_NEW_BUFFER
1900 if (buf
->conv
!= NULL
) {
1901 *doc_txt_len
= xmlBufUse(buf
->conv
);
1902 *doc_txt_ptr
= xmlStrndup(xmlBufContent(buf
->conv
), *doc_txt_len
);
1904 *doc_txt_len
= xmlBufUse(buf
->buffer
);
1905 *doc_txt_ptr
= xmlStrndup(xmlBufContent(buf
->buffer
), *doc_txt_len
);
1908 if (buf
->conv
!= NULL
) {
1909 *doc_txt_len
= buf
->conv
->use
;
1910 *doc_txt_ptr
= xmlStrndup(buf
->conv
->content
, *doc_txt_len
);
1912 *doc_txt_len
= buf
->buffer
->use
;
1913 *doc_txt_ptr
= xmlStrndup(buf
->buffer
->content
, *doc_txt_len
);
1916 (void)xmlOutputBufferClose(buf
);
1921 * xsltGetSourceNodeFlags:
1922 * @node: Node from source document
1924 * Returns the flags for a source node.
1927 xsltGetSourceNodeFlags(xmlNodePtr node
) {
1929 * Squeeze the bit flags into the upper bits of
1931 * - 'int properties' member in struct _xmlDoc
1932 * - 'xmlAttributeType atype' member in struct _xmlAttr
1933 * - 'unsigned short extra' member in struct _xmlNode
1935 switch (node
->type
) {
1936 case XML_DOCUMENT_NODE
:
1937 case XML_HTML_DOCUMENT_NODE
:
1938 return ((xmlDocPtr
) node
)->properties
>> 27;
1940 case XML_ATTRIBUTE_NODE
:
1941 return ((xmlAttrPtr
) node
)->atype
>> 27;
1943 case XML_ELEMENT_NODE
:
1945 case XML_CDATA_SECTION_NODE
:
1947 case XML_COMMENT_NODE
:
1948 return node
->extra
>> 12;
1956 * xsltSetSourceNodeFlags:
1957 * @node: Node from source document
1960 * Sets the specified flags to 1.
1962 * Returns 0 on success, -1 on error.
1965 xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
1967 if (node
->doc
== ctxt
->initialContextDoc
)
1968 ctxt
->sourceDocDirty
= 1;
1970 switch (node
->type
) {
1971 case XML_DOCUMENT_NODE
:
1972 case XML_HTML_DOCUMENT_NODE
:
1973 ((xmlDocPtr
) node
)->properties
|= flags
<< 27;
1976 case XML_ATTRIBUTE_NODE
:
1977 ((xmlAttrPtr
) node
)->atype
|= flags
<< 27;
1980 case XML_ELEMENT_NODE
:
1982 case XML_CDATA_SECTION_NODE
:
1984 case XML_COMMENT_NODE
:
1985 node
->extra
|= flags
<< 12;
1994 * xsltClearSourceNodeFlags:
1995 * @node: Node from source document
1998 * Sets the specified flags to 0.
2000 * Returns 0 on success, -1 on error.
2003 xsltClearSourceNodeFlags(xmlNodePtr node
, int flags
) {
2004 switch (node
->type
) {
2005 case XML_DOCUMENT_NODE
:
2006 case XML_HTML_DOCUMENT_NODE
:
2007 ((xmlDocPtr
) node
)->properties
&= ~(flags
<< 27);
2010 case XML_ATTRIBUTE_NODE
:
2011 ((xmlAttrPtr
) node
)->atype
&= ~(flags
<< 27);
2014 case XML_ELEMENT_NODE
:
2016 case XML_CDATA_SECTION_NODE
:
2018 case XML_COMMENT_NODE
:
2019 node
->extra
&= ~(flags
<< 12);
2031 * Returns a pointer to the psvi member of a node or NULL on error.
2034 xsltGetPSVIPtr(xmlNodePtr cur
) {
2035 switch (cur
->type
) {
2036 case XML_DOCUMENT_NODE
:
2037 case XML_HTML_DOCUMENT_NODE
:
2038 return &((xmlDocPtr
) cur
)->psvi
;
2040 case XML_ATTRIBUTE_NODE
:
2041 return &((xmlAttrPtr
) cur
)->psvi
;
2043 case XML_ELEMENT_NODE
:
2045 case XML_CDATA_SECTION_NODE
:
2047 case XML_COMMENT_NODE
:
2055 #ifdef WITH_PROFILER
2057 /************************************************************************
2059 * Generating profiling information *
2061 ************************************************************************/
2063 static long calibration
= -1;
2066 * xsltCalibrateTimestamps:
2068 * Used for to calibrate the xsltTimestamp() function
2069 * Should work if launched at startup and we don't loose our quantum :-)
2071 * Returns the number of milliseconds used by xsltTimestamp()
2073 #if !defined(XSLT_WIN32_PERFORMANCE_COUNTER) && \
2074 (defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY))
2076 xsltCalibrateTimestamps(void) {
2079 for (i
= 0;i
< 999;i
++)
2081 return(xsltTimestamp() / 1000);
2086 * xsltCalibrateAdjust:
2087 * @delta: a negative dealy value found
2089 * Used for to correct the calibration for xsltTimestamp()
2092 xsltCalibrateAdjust(long delta
) {
2093 calibration
+= delta
;
2099 * Used for gathering profiling data
2101 * Returns the number of tenth of milliseconds since the beginning of the
2107 #ifdef XSLT_WIN32_PERFORMANCE_COUNTER
2109 LARGE_INTEGER performanceCount
;
2110 LARGE_INTEGER performanceFrequency
;
2113 static LONGLONG startupQuadCount
= 0;
2114 static LONGLONG startupQuadFreq
= 0;
2116 ok
= QueryPerformanceCounter(&performanceCount
);
2119 quadCount
= performanceCount
.QuadPart
;
2120 if (calibration
< 0) {
2122 ok
= QueryPerformanceFrequency(&performanceFrequency
);
2125 startupQuadFreq
= performanceFrequency
.QuadPart
;
2126 startupQuadCount
= quadCount
;
2129 if (startupQuadFreq
== 0)
2131 seconds
= (quadCount
- startupQuadCount
) / (double) startupQuadFreq
;
2132 return (long) (seconds
* XSLT_TIMESTAMP_TICS_PER_SEC
);
2134 #else /* XSLT_WIN32_PERFORMANCE_COUNTER */
2135 #ifdef HAVE_CLOCK_GETTIME
2136 # if defined(CLOCK_MONOTONIC)
2137 # define XSLT_CLOCK CLOCK_MONOTONIC
2138 # elif defined(CLOCK_HIGHRES)
2139 # define XSLT_CLOCK CLOCK_HIGHRES
2141 # define XSLT_CLOCK CLOCK_REALTIME
2143 static struct timespec startup
;
2144 struct timespec cur
;
2147 if (calibration
< 0) {
2148 clock_gettime(XSLT_CLOCK
, &startup
);
2150 calibration
= xsltCalibrateTimestamps();
2151 clock_gettime(XSLT_CLOCK
, &startup
);
2155 clock_gettime(XSLT_CLOCK
, &cur
);
2156 tics
= (cur
.tv_sec
- startup
.tv_sec
) * XSLT_TIMESTAMP_TICS_PER_SEC
;
2157 tics
+= (cur
.tv_nsec
- startup
.tv_nsec
) /
2158 (1000000000l / XSLT_TIMESTAMP_TICS_PER_SEC
);
2160 tics
-= calibration
;
2163 #elif HAVE_GETTIMEOFDAY
2164 static struct timeval startup
;
2168 if (calibration
< 0) {
2169 gettimeofday(&startup
, NULL
);
2171 calibration
= xsltCalibrateTimestamps();
2172 gettimeofday(&startup
, NULL
);
2176 gettimeofday(&cur
, NULL
);
2177 tics
= (cur
.tv_sec
- startup
.tv_sec
) * XSLT_TIMESTAMP_TICS_PER_SEC
;
2178 tics
+= (cur
.tv_usec
- startup
.tv_usec
) /
2179 (1000000l / XSLT_TIMESTAMP_TICS_PER_SEC
);
2181 tics
-= calibration
;
2185 /* Neither gettimeofday() nor Win32 performance counter available */
2189 #endif /* HAVE_GETTIMEOFDAY */
2190 #endif /* XSLT_WIN32_PERFORMANCE_COUNTER */
2194 pretty_templ_match(xsltTemplatePtr templ
) {
2195 static char dst
[1001];
2196 char *src
= (char *)templ
->match
;
2199 /* strip white spaces */
2200 for (j
=0; i
<1000 && src
[j
]; i
++,j
++) {
2201 for(;src
[j
]==' ';j
++);
2204 if(i
<998 && templ
->mode
) {
2207 src
=(char *)templ
->mode
;
2208 for (j
=0; i
<999 && src
[j
]; i
++,j
++) {
2217 #define MAX_TEMPLATES 10000
2220 * xsltSaveProfiling:
2221 * @ctxt: an XSLT context
2222 * @output: a FILE * for saving the information
2224 * Save the profiling information on @output
2227 xsltSaveProfiling(xsltTransformContextPtr ctxt
, FILE *output
) {
2231 unsigned long totalt
;
2232 xsltTemplatePtr
*templates
;
2233 xsltStylesheetPtr style
;
2234 xsltTemplatePtr templ1
,templ2
;
2237 if ((output
== NULL
) || (ctxt
== NULL
))
2239 if (ctxt
->profile
== 0)
2243 max
= MAX_TEMPLATES
;
2244 templates
= xmlMalloc(max
* sizeof(xsltTemplatePtr
));
2245 if (templates
== NULL
)
2248 style
= ctxt
->style
;
2249 while (style
!= NULL
) {
2250 templ1
= style
->templates
;
2251 while (templ1
!= NULL
) {
2255 if (templ1
->nbCalls
> 0)
2256 templates
[nb
++] = templ1
;
2257 templ1
= templ1
->next
;
2260 style
= xsltNextImport(style
);
2263 for (i
= 0;i
< nb
-1;i
++) {
2264 for (j
= i
+ 1; j
< nb
; j
++) {
2265 if ((templates
[i
]->time
<= templates
[j
]->time
) ||
2266 ((templates
[i
]->time
== templates
[j
]->time
) &&
2267 (templates
[i
]->nbCalls
<= templates
[j
]->nbCalls
))) {
2268 templ1
= templates
[j
];
2269 templates
[j
] = templates
[i
];
2270 templates
[i
] = templ1
;
2276 /* print flat profile */
2278 fprintf(output
, "%6s%20s%20s%10s Calls Tot 100us Avg\n\n",
2279 "number", "match", "name", "mode");
2282 for (i
= 0;i
< nb
;i
++) {
2283 templ1
= templates
[i
];
2284 fprintf(output
, "%5d ", i
);
2285 if (templ1
->match
!= NULL
) {
2286 if (xmlStrlen(templ1
->match
) > 20)
2287 fprintf(output
, "%s\n%26s", templ1
->match
, "");
2289 fprintf(output
, "%20s", templ1
->match
);
2291 fprintf(output
, "%20s", "");
2293 if (templ1
->name
!= NULL
) {
2294 if (xmlStrlen(templ1
->name
) > 20)
2295 fprintf(output
, "%s\n%46s", templ1
->name
, "");
2297 fprintf(output
, "%20s", templ1
->name
);
2299 fprintf(output
, "%20s", "");
2301 if (templ1
->mode
!= NULL
) {
2302 if (xmlStrlen(templ1
->mode
) > 10)
2303 fprintf(output
, "%s\n%56s", templ1
->mode
, "");
2305 fprintf(output
, "%10s", templ1
->mode
);
2307 fprintf(output
, "%10s", "");
2309 fprintf(output
, " %6d", templ1
->nbCalls
);
2310 fprintf(output
, " %6ld %6ld\n", templ1
->time
,
2311 templ1
->time
/ templ1
->nbCalls
);
2312 total
+= templ1
->nbCalls
;
2313 totalt
+= templ1
->time
;
2315 fprintf(output
, "\n%30s%26s %6d %6ld\n", "Total", "", total
, totalt
);
2318 /* print call graph */
2320 childt
= xmlMalloc((nb
+ 1) * sizeof(int));
2324 /* precalculate children times */
2325 for (i
= 0; i
< nb
; i
++) {
2326 templ1
= templates
[i
];
2329 for (k
= 0; k
< nb
; k
++) {
2330 templ2
= templates
[k
];
2331 for (l
= 0; l
< templ2
->templNr
; l
++) {
2332 if (templ2
->templCalledTab
[l
] == templ1
) {
2333 childt
[i
] +=templ2
->time
;
2340 fprintf(output
, "\nindex %% time self children called name\n");
2342 for (i
= 0; i
< nb
; i
++) {
2343 char ix_str
[20], timep_str
[20], times_str
[20], timec_str
[20], called_str
[20];
2346 templ1
= templates
[i
];
2348 for (j
= 0; j
< templ1
->templNr
; j
++) {
2349 templ2
= templ1
->templCalledTab
[j
];
2350 for (k
= 0; k
< nb
; k
++) {
2351 if (templates
[k
] == templ2
)
2354 t
=templ2
?templ2
->time
:totalt
;
2355 snprintf(times_str
,sizeof(times_str
),"%8.3f",(float)t
/XSLT_TIMESTAMP_TICS_PER_SEC
);
2356 snprintf(timec_str
,sizeof(timec_str
),"%8.3f",(float)childt
[k
]/XSLT_TIMESTAMP_TICS_PER_SEC
);
2357 snprintf(called_str
,sizeof(called_str
),"%6d/%d",
2358 templ1
->templCountTab
[j
], /* number of times caller calls 'this' */
2359 templ1
->nbCalls
); /* total number of calls to 'this' */
2361 fprintf(output
, " %-8s %-8s %-12s %s [%d]\n",
2362 times_str
,timec_str
,called_str
,
2363 (templ2
?(templ2
->name
?(char *)templ2
->name
:pretty_templ_match(templ2
)):"-"),k
);
2366 snprintf(ix_str
,sizeof(ix_str
),"[%d]",i
);
2367 snprintf(timep_str
,sizeof(timep_str
),"%6.2f",(float)templ1
->time
*100.0/totalt
);
2368 snprintf(times_str
,sizeof(times_str
),"%8.3f",(float)templ1
->time
/XSLT_TIMESTAMP_TICS_PER_SEC
);
2369 snprintf(timec_str
,sizeof(timec_str
),"%8.3f",(float)childt
[i
]/XSLT_TIMESTAMP_TICS_PER_SEC
);
2370 fprintf(output
, "%-5s %-6s %-8s %-8s %6d %s [%d]\n",
2371 ix_str
, timep_str
,times_str
,timec_str
,
2373 templ1
->name
?(char *)templ1
->name
:pretty_templ_match(templ1
),i
);
2375 * - go over templates[0..nb] and their templCalledTab[]
2376 * - print those where we in the the call-stack
2379 for (k
= 0; k
< nb
; k
++) {
2380 templ2
= templates
[k
];
2381 for (l
= 0; l
< templ2
->templNr
; l
++) {
2382 if (templ2
->templCalledTab
[l
] == templ1
) {
2383 total
+=templ2
->templCountTab
[l
];
2387 for (k
= 0; k
< nb
; k
++) {
2388 templ2
= templates
[k
];
2389 for (l
= 0; l
< templ2
->templNr
; l
++) {
2390 if (templ2
->templCalledTab
[l
] == templ1
) {
2391 snprintf(times_str
,sizeof(times_str
),"%8.3f",(float)templ2
->time
/XSLT_TIMESTAMP_TICS_PER_SEC
);
2392 snprintf(timec_str
,sizeof(timec_str
),"%8.3f",(float)childt
[k
]/XSLT_TIMESTAMP_TICS_PER_SEC
);
2393 snprintf(called_str
,sizeof(called_str
),"%6d/%d",
2394 templ2
->templCountTab
[l
], /* number of times 'this' calls callee */
2395 total
); /* total number of calls from 'this' */
2396 fprintf(output
, " %-8s %-8s %-12s %s [%d]\n",
2397 times_str
,timec_str
,called_str
,
2398 templ2
->name
?(char *)templ2
->name
:pretty_templ_match(templ2
),k
);
2402 fprintf(output
, "-----------------------------------------------\n");
2405 fprintf(output
, "\f\nIndex by function name\n");
2406 for (i
= 0; i
< nb
; i
++) {
2407 templ1
= templates
[i
];
2408 fprintf(output
, "[%d] %s (%s:%d)\n",
2409 i
, templ1
->name
?(char *)templ1
->name
:pretty_templ_match(templ1
),
2410 templ1
->style
->doc
->URL
,templ1
->elem
->line
);
2413 fprintf(output
, "\f\n");
2419 /************************************************************************
2421 * Fetching profiling information *
2423 ************************************************************************/
2426 * xsltGetProfileInformation:
2427 * @ctxt: a transformation context
2429 * This function should be called after the transformation completed
2430 * to extract template processing profiling information if available.
2431 * The information is returned as an XML document tree like
2432 * <?xml version="1.0"?>
2434 * <template rank="1" match="*" name=""
2435 * mode="" calls="6" time="48" average="8"/>
2436 * <template rank="2" match="item2|item3" name=""
2437 * mode="" calls="10" time="30" average="3"/>
2438 * <template rank="3" match="item1" name=""
2439 * mode="" calls="5" time="17" average="3"/>
2441 * The caller will need to free up the returned tree with xmlFreeDoc()
2443 * Returns the xmlDocPtr corresponding to the result or NULL if not available.
2447 xsltGetProfileInformation(xsltTransformContextPtr ctxt
)
2449 xmlDocPtr ret
= NULL
;
2450 xmlNodePtr root
, child
;
2453 xsltStylesheetPtr style
;
2454 xsltTemplatePtr
*templates
;
2455 xsltTemplatePtr templ
;
2456 int nb
= 0, max
= 0, i
, j
;
2467 (xsltTemplatePtr
*) xmlMalloc(max
* sizeof(xsltTemplatePtr
));
2468 if (templates
== NULL
)
2472 * collect all the templates in an array
2474 style
= ctxt
->style
;
2475 while (style
!= NULL
) {
2476 templ
= style
->templates
;
2477 while (templ
!= NULL
) {
2481 if (templ
->nbCalls
> 0)
2482 templates
[nb
++] = templ
;
2483 templ
= templ
->next
;
2486 style
= (xsltStylesheetPtr
) xsltNextImport(style
);
2490 * Sort the array by time spent
2492 for (i
= 0; i
< nb
- 1; i
++) {
2493 for (j
= i
+ 1; j
< nb
; j
++) {
2494 if ((templates
[i
]->time
<= templates
[j
]->time
) ||
2495 ((templates
[i
]->time
== templates
[j
]->time
) &&
2496 (templates
[i
]->nbCalls
<= templates
[j
]->nbCalls
))) {
2497 templ
= templates
[j
];
2498 templates
[j
] = templates
[i
];
2499 templates
[i
] = templ
;
2505 * Generate a document corresponding to the results.
2507 ret
= xmlNewDoc(BAD_CAST
"1.0");
2508 root
= xmlNewDocNode(ret
, NULL
, BAD_CAST
"profile", NULL
);
2509 xmlDocSetRootElement(ret
, root
);
2511 for (i
= 0; i
< nb
; i
++) {
2512 child
= xmlNewChild(root
, NULL
, BAD_CAST
"template", NULL
);
2513 snprintf(buf
, sizeof(buf
), "%d", i
+ 1);
2514 xmlSetProp(child
, BAD_CAST
"rank", BAD_CAST buf
);
2515 xmlSetProp(child
, BAD_CAST
"match", BAD_CAST templates
[i
]->match
);
2516 xmlSetProp(child
, BAD_CAST
"name", BAD_CAST templates
[i
]->name
);
2517 xmlSetProp(child
, BAD_CAST
"mode", BAD_CAST templates
[i
]->mode
);
2519 snprintf(buf
, sizeof(buf
), "%d", templates
[i
]->nbCalls
);
2520 xmlSetProp(child
, BAD_CAST
"calls", BAD_CAST buf
);
2522 snprintf(buf
, sizeof(buf
), "%ld", templates
[i
]->time
);
2523 xmlSetProp(child
, BAD_CAST
"time", BAD_CAST buf
);
2525 snprintf(buf
, sizeof(buf
), "%ld", templates
[i
]->time
/ templates
[i
]->nbCalls
);
2526 xmlSetProp(child
, BAD_CAST
"average", BAD_CAST buf
);
2534 #endif /* WITH_PROFILER */
2536 /************************************************************************
2538 * Hooks for libxml2 XPath *
2540 ************************************************************************/
2543 * xsltXPathCompileFlags:
2544 * @style: the stylesheet
2545 * @str: the XPath expression
2546 * @flags: extra compilation flags to pass down to libxml2 XPath
2548 * Compile an XPath expression
2550 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2551 * the caller has to free the object.
2554 xsltXPathCompileFlags(xsltStylesheetPtr style
, const xmlChar
*str
, int flags
) {
2555 xmlXPathContextPtr xpathCtxt
;
2556 xmlXPathCompExprPtr ret
;
2558 if (style
!= NULL
) {
2559 xpathCtxt
= style
->principal
->xpathCtxt
;
2560 if (xpathCtxt
== NULL
)
2562 xpathCtxt
->dict
= style
->dict
;
2564 xpathCtxt
= xmlXPathNewContext(NULL
);
2565 if (xpathCtxt
== NULL
)
2568 xpathCtxt
->flags
= flags
;
2571 * Compile the expression.
2573 ret
= xmlXPathCtxtCompile(xpathCtxt
, str
);
2575 if (style
== NULL
) {
2576 xmlXPathFreeContext(xpathCtxt
);
2579 * TODO: there is a lot of optimizations which should be possible
2580 * like variable slot precomputations, function precomputations, etc.
2588 * @style: the stylesheet
2589 * @str: the XPath expression
2591 * Compile an XPath expression
2593 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2594 * the caller has to free the object.
2597 xsltXPathCompile(xsltStylesheetPtr style
, const xmlChar
*str
) {
2598 return(xsltXPathCompileFlags(style
, str
, 0));
2601 /************************************************************************
2603 * Hooks for the debugger *
2605 ************************************************************************/
2610 * xsltGetDebuggerStatus:
2612 * Get xslDebugStatus.
2614 * Returns the value of xslDebugStatus.
2617 xsltGetDebuggerStatus(void)
2619 return(xslDebugStatus
);
2622 #ifdef WITH_DEBUGGER
2625 * There is currently only 3 debugging callback defined
2626 * Debugger callbacks are disabled by default
2628 #define XSLT_CALLBACK_NUMBER 3
2630 typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks
;
2631 typedef xsltDebuggerCallbacks
*xsltDebuggerCallbacksPtr
;
2632 struct _xsltDebuggerCallbacks
{
2633 xsltHandleDebuggerCallback handler
;
2634 xsltAddCallCallback add
;
2635 xsltDropCallCallback drop
;
2638 static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks
= {
2645 * xsltSetDebuggerStatus:
2646 * @value : the value to be set
2648 * This function sets the value of xslDebugStatus.
2651 xsltSetDebuggerStatus(int value
)
2653 xslDebugStatus
= value
;
2657 * xsltSetDebuggerCallbacks:
2658 * @no : number of callbacks
2659 * @block : the block of callbacks
2661 * This function allow to plug a debugger into the XSLT library
2662 * @block points to a block of memory containing the address of @no
2663 * callback routines.
2665 * Returns 0 in case of success and -1 in case of error
2668 xsltSetDebuggerCallbacks(int no
, void *block
)
2670 xsltDebuggerCallbacksPtr callbacks
;
2672 if ((block
== NULL
) || (no
!= XSLT_CALLBACK_NUMBER
))
2675 callbacks
= (xsltDebuggerCallbacksPtr
) block
;
2676 xsltDebuggerCurrentCallbacks
.handler
= callbacks
->handler
;
2677 xsltDebuggerCurrentCallbacks
.add
= callbacks
->add
;
2678 xsltDebuggerCurrentCallbacks
.drop
= callbacks
->drop
;
2683 * xslHandleDebugger:
2684 * @cur : source node being executed
2685 * @node : data node being processed
2686 * @templ : temlate that applies to node
2687 * @ctxt : the xslt transform context
2689 * If either cur or node are a breakpoint, or xslDebugStatus in state
2690 * where debugging must occcur at this time then transfer control
2691 * to the xslDebugBreak function
2694 xslHandleDebugger(xmlNodePtr cur
, xmlNodePtr node
, xsltTemplatePtr templ
,
2695 xsltTransformContextPtr ctxt
)
2697 if (xsltDebuggerCurrentCallbacks
.handler
!= NULL
)
2698 xsltDebuggerCurrentCallbacks
.handler(cur
, node
, templ
, ctxt
);
2703 * @templ : current template being applied
2704 * @source : the source node being processed
2706 * Add template "call" to call stack
2707 * Returns : 1 on sucess 0 otherwise an error may be printed if
2708 * WITH_XSLT_DEBUG_BREAKPOINTS is defined
2711 xslAddCall(xsltTemplatePtr templ
, xmlNodePtr source
)
2713 if (xsltDebuggerCurrentCallbacks
.add
!= NULL
)
2714 return(xsltDebuggerCurrentCallbacks
.add(templ
, source
));
2721 * Drop the topmost item off the call stack
2726 if (xsltDebuggerCurrentCallbacks
.drop
!= NULL
)
2727 xsltDebuggerCurrentCallbacks
.drop();
2730 #endif /* WITH_DEBUGGER */