2 * xsltutils.c: Utilities for the XSL Transformation 1.0 engine
5 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 * See Copyright for the status of this software.
15 #ifndef XSLT_NEED_TRIO
23 #ifdef HAVE_SYS_TIME_H
34 #include <libxml/xmlmemory.h>
35 #include <libxml/tree.h>
36 #include <libxml/HTMLtree.h>
37 #include <libxml/xmlerror.h>
38 #include <libxml/xmlIO.h>
39 #include "xsltutils.h"
40 #include "templates.h"
41 #include "xsltInternals.h"
43 #include "transform.h"
45 #if defined(_WIN32) && !defined(__CYGWIN__)
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;
308 #ifdef XSLT_REFACTORED
311 * xsltPointerListAddSize:
312 * @list: the pointer list structure
313 * @item: the item to be stored
314 * @initialSize: the initial size of the list
316 * Adds an item to the list.
318 * Returns the position of the added item in the list or
319 * -1 in case of an error.
322 xsltPointerListAddSize(xsltPointerListPtr list
,
326 if (list
->items
== NULL
) {
327 if (initialSize
<= 0)
329 list
->items
= (void **) xmlMalloc(
330 initialSize
* sizeof(void *));
331 if (list
->items
== NULL
) {
332 xsltGenericError(xsltGenericErrorContext
,
333 "xsltPointerListAddSize: memory allocation failure.\n");
337 list
->size
= initialSize
;
338 } else if (list
->size
<= list
->number
) {
340 list
->items
= (void **) xmlRealloc(list
->items
,
341 list
->size
* sizeof(void *));
342 if (list
->items
== NULL
) {
343 xsltGenericError(xsltGenericErrorContext
,
344 "xsltPointerListAddSize: memory re-allocation failure.\n");
349 list
->items
[list
->number
++] = item
;
354 * xsltPointerListCreate:
355 * @initialSize: the initial size for the list
357 * Creates an xsltPointerList structure.
359 * Returns a xsltPointerList structure or NULL in case of an error.
362 xsltPointerListCreate(int initialSize
)
364 xsltPointerListPtr ret
;
366 ret
= xmlMalloc(sizeof(xsltPointerList
));
368 xsltGenericError(xsltGenericErrorContext
,
369 "xsltPointerListCreate: memory allocation failure.\n");
372 memset(ret
, 0, sizeof(xsltPointerList
));
373 if (initialSize
> 0) {
374 xsltPointerListAddSize(ret
, NULL
, initialSize
);
381 * xsltPointerListFree:
382 * @list: pointer to the list to be freed
384 * Frees the xsltPointerList structure. This does not free
385 * the content of the list.
388 xsltPointerListFree(xsltPointerListPtr list
)
392 if (list
->items
!= NULL
)
393 xmlFree(list
->items
);
398 * xsltPointerListClear:
399 * @list: pointer to the list to be cleared
401 * Resets the list, but does not free the allocated array
402 * and does not free the content of the list.
405 xsltPointerListClear(xsltPointerListPtr list
)
407 if (list
->items
!= NULL
) {
408 xmlFree(list
->items
);
415 #endif /* XSLT_REFACTORED */
417 /************************************************************************
419 * Handling of XSLT stylesheets messages *
421 ************************************************************************/
425 * @ctxt: an XSLT processing context
426 * @node: The current node
427 * @inst: The node containing the message instruction
429 * Process and xsl:message construct
432 xsltMessage(xsltTransformContextPtr ctxt
, xmlNodePtr node
, xmlNodePtr inst
) {
433 xmlGenericErrorFunc error
= xsltGenericError
;
434 void *errctx
= xsltGenericErrorContext
;
435 xmlChar
*prop
, *message
;
438 if ((ctxt
== NULL
) || (inst
== NULL
))
441 if (ctxt
->error
!= NULL
) {
443 errctx
= ctxt
->errctx
;
446 prop
= xmlGetNsProp(inst
, (const xmlChar
*)"terminate", NULL
);
448 if (xmlStrEqual(prop
, (const xmlChar
*)"yes")) {
450 } else if (xmlStrEqual(prop
, (const xmlChar
*)"no")) {
453 xsltTransformError(ctxt
, NULL
, inst
,
454 "xsl:message : terminate expecting 'yes' or 'no'\n");
458 message
= xsltEvalTemplateString(ctxt
, node
, inst
);
459 if (message
!= NULL
) {
460 int len
= xmlStrlen(message
);
462 error(errctx
, "%s", (const char *)message
);
463 if ((len
> 0) && (message
[len
- 1] != '\n'))
468 ctxt
->state
= XSLT_STATE_STOPPED
;
471 /************************************************************************
473 * Handling of out of context errors *
475 ************************************************************************/
477 #define XSLT_GET_VAR_STR(msg, str) { \
483 str = (char *) xmlMalloc(150); \
489 while (size < 64000) { \
491 chars = vsnprintf(str, size, msg, ap); \
493 if ((chars > -1) && (chars < size)) \
499 if ((larger = (char *) xmlRealloc(str, size)) == NULL) {\
507 * xsltGenericErrorDefaultFunc:
508 * @ctx: an error context
509 * @msg: the message to display/transmit
510 * @...: extra parameters for the message display
512 * Default handler for out of context error messages.
514 static void WINAPIV
LIBXSLT_ATTR_FORMAT(2,3)
515 xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED
, const char *msg
, ...) {
518 if (xsltGenericErrorContext
== NULL
)
519 xsltGenericErrorContext
= (void *) stderr
;
522 vfprintf((FILE *)xsltGenericErrorContext
, msg
, args
);
526 xmlGenericErrorFunc xsltGenericError
= xsltGenericErrorDefaultFunc
;
527 void *xsltGenericErrorContext
= NULL
;
531 * xsltSetGenericErrorFunc:
532 * @ctx: the new error handling context
533 * @handler: the new handler function
535 * Function to reset the handler and the error context for out of
536 * context error messages.
537 * This simply means that @handler will be called for subsequent
538 * error messages while not parsing nor validating. And @ctx will
539 * be passed as first argument to @handler
540 * One can simply force messages to be emitted to another FILE * than
541 * stderr by setting @ctx to this file handle and @handler to NULL.
544 xsltSetGenericErrorFunc(void *ctx
, xmlGenericErrorFunc handler
) {
545 xsltGenericErrorContext
= ctx
;
547 xsltGenericError
= handler
;
549 xsltGenericError
= xsltGenericErrorDefaultFunc
;
553 * xsltGenericDebugDefaultFunc:
554 * @ctx: an error context
555 * @msg: the message to display/transmit
556 * @...: extra parameters for the message display
558 * Default handler for out of context error messages.
560 static void WINAPIV
LIBXSLT_ATTR_FORMAT(2,3)
561 xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED
, const char *msg
, ...) {
564 if (xsltGenericDebugContext
== NULL
)
568 vfprintf((FILE *)xsltGenericDebugContext
, msg
, args
);
572 xmlGenericErrorFunc xsltGenericDebug
= xsltGenericDebugDefaultFunc
;
573 void *xsltGenericDebugContext
= NULL
;
577 * xsltSetGenericDebugFunc:
578 * @ctx: the new error handling context
579 * @handler: the new handler function
581 * Function to reset the handler and the error context for out of
582 * context error messages.
583 * This simply means that @handler will be called for subsequent
584 * error messages while not parsing or validating. And @ctx will
585 * be passed as first argument to @handler
586 * One can simply force messages to be emitted to another FILE * than
587 * stderr by setting @ctx to this file handle and @handler to NULL.
590 xsltSetGenericDebugFunc(void *ctx
, xmlGenericErrorFunc handler
) {
591 xsltGenericDebugContext
= ctx
;
593 xsltGenericDebug
= handler
;
595 xsltGenericDebug
= xsltGenericDebugDefaultFunc
;
599 * xsltPrintErrorContext:
600 * @ctxt: the transformation context
601 * @style: the stylesheet
602 * @node: the current node being processed
604 * Display the context of an error.
607 xsltPrintErrorContext(xsltTransformContextPtr ctxt
,
608 xsltStylesheetPtr style
, xmlNodePtr node
) {
610 const xmlChar
*file
= NULL
;
611 const xmlChar
*name
= NULL
;
612 const char *type
= "error";
613 xmlGenericErrorFunc error
= xsltGenericError
;
614 void *errctx
= xsltGenericErrorContext
;
617 if (ctxt
->state
== XSLT_STATE_OK
)
618 ctxt
->state
= XSLT_STATE_ERROR
;
619 if (ctxt
->error
!= NULL
) {
621 errctx
= ctxt
->errctx
;
624 if ((node
== NULL
) && (ctxt
!= NULL
))
628 if ((node
->type
== XML_DOCUMENT_NODE
) ||
629 (node
->type
== XML_HTML_DOCUMENT_NODE
)) {
630 xmlDocPtr doc
= (xmlDocPtr
) node
;
634 line
= xmlGetLineNo(node
);
635 if ((node
->doc
!= NULL
) && (node
->doc
->URL
!= NULL
))
636 file
= node
->doc
->URL
;
637 if (node
->name
!= NULL
)
643 type
= "runtime error";
644 else if (style
!= NULL
) {
645 #ifdef XSLT_REFACTORED
646 if (XSLT_CCTXT(style
)->errSeverity
== XSLT_ERROR_SEVERITY_WARNING
)
647 type
= "compilation warning";
649 type
= "compilation error";
651 type
= "compilation error";
655 if ((file
!= NULL
) && (line
!= 0) && (name
!= NULL
))
656 error(errctx
, "%s: file %s line %d element %s\n",
657 type
, file
, line
, name
);
658 else if ((file
!= NULL
) && (name
!= NULL
))
659 error(errctx
, "%s: file %s element %s\n", type
, file
, name
);
660 else if ((file
!= NULL
) && (line
!= 0))
661 error(errctx
, "%s: file %s line %d\n", type
, file
, line
);
662 else if (file
!= NULL
)
663 error(errctx
, "%s: file %s\n", type
, file
);
664 else if (name
!= NULL
)
665 error(errctx
, "%s: element %s\n", type
, name
);
667 error(errctx
, "%s\n", type
);
671 * xsltSetTransformErrorFunc:
672 * @ctxt: the XSLT transformation context
673 * @ctx: the new error handling context
674 * @handler: the new handler function
676 * Function to reset the handler and the error context for out of
677 * context error messages specific to a given XSLT transromation.
679 * This simply means that @handler will be called for subsequent
680 * error messages while running the transformation.
683 xsltSetTransformErrorFunc(xsltTransformContextPtr ctxt
,
684 void *ctx
, xmlGenericErrorFunc handler
)
686 ctxt
->error
= handler
;
691 * xsltTransformError:
692 * @ctxt: an XSLT transformation context
693 * @style: the XSLT stylesheet used
694 * @node: the current node in the stylesheet
695 * @msg: the message to display/transmit
696 * @...: extra parameters for the message display
698 * Display and format an error messages, gives file, line, position and
699 * extra parameters, will use the specific transformation context if available
702 xsltTransformError(xsltTransformContextPtr ctxt
,
703 xsltStylesheetPtr style
,
705 const char *msg
, ...) {
706 xmlGenericErrorFunc error
= xsltGenericError
;
707 void *errctx
= xsltGenericErrorContext
;
711 if (ctxt
->state
== XSLT_STATE_OK
)
712 ctxt
->state
= XSLT_STATE_ERROR
;
713 if (ctxt
->error
!= NULL
) {
715 errctx
= ctxt
->errctx
;
718 if ((node
== NULL
) && (ctxt
!= NULL
))
720 xsltPrintErrorContext(ctxt
, style
, node
);
721 XSLT_GET_VAR_STR(msg
, str
);
722 error(errctx
, "%s", str
);
727 /************************************************************************
731 ************************************************************************/
735 * @dict: a dictionary
736 * @name: the full QName
737 * @prefix: the return value
739 * Split QNames into prefix and local names, both allocated from a dictionary.
741 * Returns: the localname or NULL in case of error.
744 xsltSplitQName(xmlDictPtr dict
, const xmlChar
*name
, const xmlChar
**prefix
) {
746 const xmlChar
*ret
= NULL
;
749 if ((name
== NULL
) || (dict
== NULL
)) return(NULL
);
751 return(xmlDictLookup(dict
, name
, -1));
752 while ((name
[len
] != 0) && (name
[len
] != ':')) len
++;
753 if (name
[len
] == 0) return(xmlDictLookup(dict
, name
, -1));
754 *prefix
= xmlDictLookup(dict
, name
, len
);
755 ret
= xmlDictLookup(dict
, &name
[len
+ 1], -1);
761 * @node: the node holding the QName
762 * @name: pointer to the initial QName value
764 * This function analyzes @name, if the name contains a prefix,
765 * the function seaches the associated namespace in scope for it.
766 * It will also replace @name value with the NCName, the old value being
768 * Errors in the prefix lookup are signalled by setting @name to NULL.
770 * NOTE: the namespace returned is a pointer to the place where it is
771 * defined and hence has the same lifespan as the document holding it.
773 * Returns the namespace URI if there is a prefix, or NULL if @name is
777 xsltGetQNameURI(xmlNodePtr node
, xmlChar
** name
)
786 if ((qname
== NULL
) || (*qname
== 0))
789 xsltGenericError(xsltGenericErrorContext
,
790 "QName: no element for namespace lookup %s\n",
797 /* nasty but valid */
802 * we are not trying to validate but just to cut, and yes it will
803 * work even if this is a set of UTF-8 encoded chars
805 while ((qname
[len
] != 0) && (qname
[len
] != ':'))
812 * handle xml: separately, this one is magical
814 if ((qname
[0] == 'x') && (qname
[1] == 'm') &&
815 (qname
[2] == 'l') && (qname
[3] == ':')) {
818 *name
= xmlStrdup(&qname
[4]);
820 return(XML_XML_NAMESPACE
);
824 ns
= xmlSearchNs(node
->doc
, node
, qname
);
826 xsltGenericError(xsltGenericErrorContext
,
827 "%s:%s : no namespace bound to prefix %s\n",
828 qname
, &qname
[len
+ 1], qname
);
833 *name
= xmlStrdup(&qname
[len
+ 1]);
840 * @style: stylesheet pointer
841 * @node: the node holding the QName
842 * @name: pointer to the initial QName value
844 * This function is similar to xsltGetQNameURI, but is used when
845 * @name is a dictionary entry.
847 * Returns the namespace URI if there is a prefix, or NULL if @name is
851 xsltGetQNameURI2(xsltStylesheetPtr style
, xmlNodePtr node
,
852 const xmlChar
**name
) {
859 qname
= (xmlChar
*)*name
;
860 if ((qname
== NULL
) || (*qname
== 0))
863 xsltGenericError(xsltGenericErrorContext
,
864 "QName: no element for namespace lookup %s\n",
871 * we are not trying to validate but just to cut, and yes it will
872 * work even if this is a set of UTF-8 encoded chars
874 while ((qname
[len
] != 0) && (qname
[len
] != ':'))
881 * handle xml: separately, this one is magical
883 if ((qname
[0] == 'x') && (qname
[1] == 'm') &&
884 (qname
[2] == 'l') && (qname
[3] == ':')) {
887 *name
= xmlDictLookup(style
->dict
, &qname
[4], -1);
888 return(XML_XML_NAMESPACE
);
891 qname
= xmlStrndup(*name
, len
);
892 ns
= xmlSearchNs(node
->doc
, node
, qname
);
895 xsltTransformError(NULL
, style
, node
,
896 "No namespace bound to prefix '%s'.\n",
900 xsltGenericError(xsltGenericErrorContext
,
901 "%s : no namespace bound to prefix %s\n",
908 *name
= xmlDictLookup(style
->dict
, (*name
)+len
+1, -1);
913 /************************************************************************
917 ************************************************************************/
920 * xsltDocumentSortFunction:
921 * @list: the node set
923 * reorder the current node list @list accordingly to the document order
924 * This function is slow, obsolete and should not be used anymore.
927 xsltDocumentSortFunction(xmlNodeSetPtr list
) {
937 /* TODO: sort is really not optimized, does it needs to ? */
938 for (i
= 0;i
< len
-1;i
++) {
939 for (j
= i
+ 1; j
< len
; j
++) {
940 tst
= xmlXPathCmpNodes(list
->nodeTab
[i
], list
->nodeTab
[j
]);
942 node
= list
->nodeTab
[i
];
943 list
->nodeTab
[i
] = list
->nodeTab
[j
];
944 list
->nodeTab
[j
] = node
;
951 * xsltComputeSortResult:
952 * @ctxt: a XSLT process context
955 * reorder the current node list accordingly to the set of sorting
956 * requirement provided by the array of nodes.
958 * Returns a ordered XPath nodeset or NULL in case of error.
961 xsltComputeSortResult(xsltTransformContextPtr ctxt
, xmlNodePtr sort
) {
962 #ifdef XSLT_REFACTORED
963 xsltStyleItemSortPtr comp
;
965 xsltStylePreCompPtr comp
;
967 xmlXPathObjectPtr
*results
= NULL
;
968 xmlNodeSetPtr list
= NULL
;
969 xmlXPathObjectPtr res
;
974 int oldPos
, oldSize
;
976 xmlNsPtr
*oldNamespaces
;
980 xsltGenericError(xsltGenericErrorContext
,
981 "xsl:sort : compilation failed\n");
985 if ((comp
->select
== NULL
) || (comp
->comp
== NULL
))
988 list
= ctxt
->nodeList
;
989 if ((list
== NULL
) || (list
->nodeNr
<= 1))
994 /* TODO: xsl:sort lang attribute */
995 /* TODO: xsl:sort case-order attribute */
998 results
= xmlMalloc(len
* sizeof(xmlXPathObjectPtr
));
999 if (results
== NULL
) {
1000 xsltGenericError(xsltGenericErrorContext
,
1001 "xsltComputeSortResult: memory allocation failure\n");
1005 oldNode
= ctxt
->node
;
1006 oldInst
= ctxt
->inst
;
1007 oldPos
= ctxt
->xpathCtxt
->proximityPosition
;
1008 oldSize
= ctxt
->xpathCtxt
->contextSize
;
1009 oldNsNr
= ctxt
->xpathCtxt
->nsNr
;
1010 oldNamespaces
= ctxt
->xpathCtxt
->namespaces
;
1011 for (i
= 0;i
< len
;i
++) {
1013 ctxt
->xpathCtxt
->contextSize
= len
;
1014 ctxt
->xpathCtxt
->proximityPosition
= i
+ 1;
1015 ctxt
->node
= list
->nodeTab
[i
];
1016 ctxt
->xpathCtxt
->node
= ctxt
->node
;
1017 #ifdef XSLT_REFACTORED
1018 if (comp
->inScopeNs
!= NULL
) {
1019 ctxt
->xpathCtxt
->namespaces
= comp
->inScopeNs
->list
;
1020 ctxt
->xpathCtxt
->nsNr
= comp
->inScopeNs
->xpathNumber
;
1022 ctxt
->xpathCtxt
->namespaces
= NULL
;
1023 ctxt
->xpathCtxt
->nsNr
= 0;
1026 ctxt
->xpathCtxt
->namespaces
= comp
->nsList
;
1027 ctxt
->xpathCtxt
->nsNr
= comp
->nsNr
;
1029 res
= xmlXPathCompiledEval(comp
->comp
, ctxt
->xpathCtxt
);
1031 if (res
->type
!= XPATH_STRING
)
1032 res
= xmlXPathConvertString(res
);
1034 res
= xmlXPathConvertNumber(res
);
1035 res
->index
= i
; /* Save original pos for dupl resolv */
1037 if (res
->type
== XPATH_NUMBER
) {
1040 #ifdef WITH_XSLT_DEBUG_PROCESS
1041 xsltGenericDebug(xsltGenericDebugContext
,
1042 "xsltComputeSortResult: select didn't evaluate to a number\n");
1047 if (res
->type
== XPATH_STRING
) {
1048 if (comp
->locale
!= (xsltLocale
)0) {
1049 xmlChar
*str
= res
->stringval
;
1050 res
->stringval
= (xmlChar
*) xsltStrxfrm(comp
->locale
, str
);
1056 #ifdef WITH_XSLT_DEBUG_PROCESS
1057 xsltGenericDebug(xsltGenericDebugContext
,
1058 "xsltComputeSortResult: select didn't evaluate to a string\n");
1064 ctxt
->state
= XSLT_STATE_STOPPED
;
1068 ctxt
->node
= oldNode
;
1069 ctxt
->inst
= oldInst
;
1070 ctxt
->xpathCtxt
->contextSize
= oldSize
;
1071 ctxt
->xpathCtxt
->proximityPosition
= oldPos
;
1072 ctxt
->xpathCtxt
->nsNr
= oldNsNr
;
1073 ctxt
->xpathCtxt
->namespaces
= oldNamespaces
;
1079 * xsltDefaultSortFunction:
1080 * @ctxt: a XSLT process context
1081 * @sorts: array of sort nodes
1082 * @nbsorts: the number of sorts in the array
1084 * reorder the current node list accordingly to the set of sorting
1085 * requirement provided by the arry of nodes.
1088 xsltDefaultSortFunction(xsltTransformContextPtr ctxt
, xmlNodePtr
*sorts
,
1090 #ifdef XSLT_REFACTORED
1091 xsltStyleItemSortPtr comp
;
1093 xsltStylePreCompPtr comp
;
1095 xmlXPathObjectPtr
*resultsTab
[XSLT_MAX_SORT
];
1096 xmlXPathObjectPtr
*results
= NULL
, *res
;
1097 xmlNodeSetPtr list
= NULL
;
1098 int descending
, number
, desc
, numb
;
1104 xmlXPathObjectPtr tmp
;
1105 int tempstype
[XSLT_MAX_SORT
], temporder
[XSLT_MAX_SORT
];
1107 if ((ctxt
== NULL
) || (sorts
== NULL
) || (nbsorts
<= 0) ||
1108 (nbsorts
>= XSLT_MAX_SORT
))
1110 if (sorts
[0] == NULL
)
1112 comp
= sorts
[0]->psvi
;
1116 list
= ctxt
->nodeList
;
1117 if ((list
== NULL
) || (list
->nodeNr
<= 1))
1118 return; /* nothing to do */
1120 for (j
= 0; j
< nbsorts
; j
++) {
1121 comp
= sorts
[j
]->psvi
;
1123 if ((comp
->stype
== NULL
) && (comp
->has_stype
!= 0)) {
1125 xsltEvalAttrValueTemplate(ctxt
, sorts
[j
],
1126 (const xmlChar
*) "data-type",
1128 if (comp
->stype
!= NULL
) {
1130 if (xmlStrEqual(comp
->stype
, (const xmlChar
*) "text"))
1132 else if (xmlStrEqual(comp
->stype
, (const xmlChar
*) "number"))
1135 xsltTransformError(ctxt
, NULL
, sorts
[j
],
1136 "xsltDoSortFunction: no support for data-type = %s\n",
1138 comp
->number
= 0; /* use default */
1143 if ((comp
->order
== NULL
) && (comp
->has_order
!= 0)) {
1144 comp
->order
= xsltEvalAttrValueTemplate(ctxt
, sorts
[j
],
1145 (const xmlChar
*) "order",
1147 if (comp
->order
!= NULL
) {
1149 if (xmlStrEqual(comp
->order
, (const xmlChar
*) "ascending"))
1150 comp
->descending
= 0;
1151 else if (xmlStrEqual(comp
->order
,
1152 (const xmlChar
*) "descending"))
1153 comp
->descending
= 1;
1155 xsltTransformError(ctxt
, NULL
, sorts
[j
],
1156 "xsltDoSortFunction: invalid value %s for order\n",
1158 comp
->descending
= 0; /* use default */
1166 resultsTab
[0] = xsltComputeSortResult(ctxt
, sorts
[0]);
1167 for (i
= 1;i
< XSLT_MAX_SORT
;i
++)
1168 resultsTab
[i
] = NULL
;
1170 results
= resultsTab
[0];
1172 comp
= sorts
[0]->psvi
;
1173 descending
= comp
->descending
;
1174 number
= comp
->number
;
1175 if (results
== NULL
)
1178 /* Shell's sort of node-set */
1179 for (incr
= len
/ 2; incr
> 0; incr
/= 2) {
1180 for (i
= incr
; i
< len
; i
++) {
1182 if (results
[i
] == NULL
)
1186 if (results
[j
] == NULL
)
1190 /* We make NaN smaller than number in accordance
1192 if (xmlXPathIsNaN(results
[j
]->floatval
)) {
1193 if (xmlXPathIsNaN(results
[j
+ incr
]->floatval
))
1197 } else if (xmlXPathIsNaN(results
[j
+ incr
]->floatval
))
1199 else if (results
[j
]->floatval
==
1200 results
[j
+ incr
]->floatval
)
1202 else if (results
[j
]->floatval
>
1203 results
[j
+ incr
]->floatval
)
1206 } else if(comp
->locale
!= (xsltLocale
)0) {
1207 tst
= xsltLocaleStrcmp(
1209 (xsltLocaleChar
*) results
[j
]->stringval
,
1210 (xsltLocaleChar
*) results
[j
+ incr
]->stringval
);
1212 tst
= xmlStrcmp(results
[j
]->stringval
,
1213 results
[j
+ incr
]->stringval
);
1220 * Okay we need to use multi level sorts
1223 while (depth
< nbsorts
) {
1224 if (sorts
[depth
] == NULL
)
1226 comp
= sorts
[depth
]->psvi
;
1229 desc
= comp
->descending
;
1230 numb
= comp
->number
;
1233 * Compute the result of the next level for the
1234 * full set, this might be optimized ... or not
1236 if (resultsTab
[depth
] == NULL
)
1237 resultsTab
[depth
] = xsltComputeSortResult(ctxt
,
1239 res
= resultsTab
[depth
];
1242 if (res
[j
] == NULL
) {
1243 if (res
[j
+incr
] != NULL
)
1245 } else if (res
[j
+incr
] == NULL
) {
1249 /* We make NaN smaller than number in
1250 accordance with XSLT spec */
1251 if (xmlXPathIsNaN(res
[j
]->floatval
)) {
1252 if (xmlXPathIsNaN(res
[j
+
1257 } else if (xmlXPathIsNaN(res
[j
+ incr
]->
1260 else if (res
[j
]->floatval
== res
[j
+ incr
]->
1263 else if (res
[j
]->floatval
>
1264 res
[j
+ incr
]->floatval
)
1267 } else if(comp
->locale
!= (xsltLocale
)0) {
1268 tst
= xsltLocaleStrcmp(
1270 (xsltLocaleChar
*) res
[j
]->stringval
,
1271 (xsltLocaleChar
*) res
[j
+ incr
]->stringval
);
1273 tst
= xmlStrcmp(res
[j
]->stringval
,
1274 res
[j
+ incr
]->stringval
);
1281 * if we still can't differenciate at this level
1282 * try one level deeper.
1290 tst
= results
[j
]->index
> results
[j
+ incr
]->index
;
1294 results
[j
] = results
[j
+ incr
];
1295 results
[j
+ incr
] = tmp
;
1296 node
= list
->nodeTab
[j
];
1297 list
->nodeTab
[j
] = list
->nodeTab
[j
+ incr
];
1298 list
->nodeTab
[j
+ incr
] = node
;
1300 while (depth
< nbsorts
) {
1301 if (sorts
[depth
] == NULL
)
1303 if (resultsTab
[depth
] == NULL
)
1305 res
= resultsTab
[depth
];
1307 res
[j
] = res
[j
+ incr
];
1308 res
[j
+ incr
] = tmp
;
1318 for (j
= 0; j
< nbsorts
; j
++) {
1319 comp
= sorts
[j
]->psvi
;
1320 if (tempstype
[j
] == 1) {
1321 /* The data-type needs to be recomputed each time */
1322 xmlFree((void *)(comp
->stype
));
1325 if (temporder
[j
] == 1) {
1326 /* The order needs to be recomputed each time */
1327 xmlFree((void *)(comp
->order
));
1330 if (resultsTab
[j
] != NULL
) {
1331 for (i
= 0;i
< len
;i
++)
1332 xmlXPathFreeObject(resultsTab
[j
][i
]);
1333 xmlFree(resultsTab
[j
]);
1339 static xsltSortFunc xsltSortFunction
= xsltDefaultSortFunction
;
1342 * xsltDoSortFunction:
1343 * @ctxt: a XSLT process context
1344 * @sorts: array of sort nodes
1345 * @nbsorts: the number of sorts in the array
1347 * reorder the current node list accordingly to the set of sorting
1348 * requirement provided by the arry of nodes.
1349 * This is a wrapper function, the actual function used is specified
1350 * using xsltSetCtxtSortFunc() to set the context specific sort function,
1351 * or xsltSetSortFunc() to set the global sort function.
1352 * If a sort function is set on the context, this will get called.
1353 * Otherwise the global sort function is called.
1356 xsltDoSortFunction(xsltTransformContextPtr ctxt
, xmlNodePtr
* sorts
,
1359 if (ctxt
->sortfunc
!= NULL
)
1360 (ctxt
->sortfunc
)(ctxt
, sorts
, nbsorts
);
1361 else if (xsltSortFunction
!= NULL
)
1362 xsltSortFunction(ctxt
, sorts
, nbsorts
);
1367 * @handler: the new handler function
1369 * Function to reset the global handler for XSLT sorting.
1370 * If the handler is NULL, the default sort function will be used.
1373 xsltSetSortFunc(xsltSortFunc handler
) {
1374 if (handler
!= NULL
)
1375 xsltSortFunction
= handler
;
1377 xsltSortFunction
= xsltDefaultSortFunction
;
1381 * xsltSetCtxtSortFunc:
1382 * @ctxt: a XSLT process context
1383 * @handler: the new handler function
1385 * Function to set the handler for XSLT sorting
1386 * for the specified context.
1387 * If the handler is NULL, then the global
1388 * sort function will be called
1391 xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt
, xsltSortFunc handler
) {
1392 ctxt
->sortfunc
= handler
;
1395 /************************************************************************
1399 ************************************************************************/
1402 * xsltSetCtxtParseOptions:
1403 * @ctxt: a XSLT process context
1404 * @options: a combination of libxml2 xmlParserOption
1406 * Change the default parser option passed by the XSLT engine to the
1407 * parser when using document() loading.
1409 * Returns the previous options or -1 in case of error
1412 xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt
, int options
)
1418 oldopts
= ctxt
->parserOptions
;
1420 oldopts
|= XML_PARSE_XINCLUDE
;
1421 ctxt
->parserOptions
= options
;
1422 if (options
& XML_PARSE_XINCLUDE
)
1429 /************************************************************************
1433 ************************************************************************/
1437 * @buf: an output buffer
1438 * @result: the result xmlDocPtr
1439 * @style: the stylesheet
1441 * Save the result @result obtained by applying the @style stylesheet
1442 * to an I/O output channel @buf
1444 * Returns the number of byte written or -1 in case of failure.
1447 xsltSaveResultTo(xmlOutputBufferPtr buf
, xmlDocPtr result
,
1448 xsltStylesheetPtr style
) {
1449 const xmlChar
*encoding
;
1451 const xmlChar
*method
;
1454 if ((buf
== NULL
) || (result
== NULL
) || (style
== NULL
))
1456 if ((result
->children
== NULL
) ||
1457 ((result
->children
->type
== XML_DTD_NODE
) &&
1458 (result
->children
->next
== NULL
)))
1461 if ((style
->methodURI
!= NULL
) &&
1462 ((style
->method
== NULL
) ||
1463 (!xmlStrEqual(style
->method
, (const xmlChar
*) "xhtml")))) {
1464 xsltGenericError(xsltGenericErrorContext
,
1465 "xsltSaveResultTo : unknown output method\n");
1469 base
= buf
->written
;
1471 XSLT_GET_IMPORT_PTR(method
, style
, method
)
1472 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1473 XSLT_GET_IMPORT_INT(indent
, style
, indent
);
1475 if ((method
== NULL
) && (result
->type
== XML_HTML_DOCUMENT_NODE
))
1476 method
= (const xmlChar
*) "html";
1478 if ((method
!= NULL
) &&
1479 (xmlStrEqual(method
, (const xmlChar
*) "html"))) {
1480 if (encoding
!= NULL
) {
1481 htmlSetMetaEncoding(result
, (const xmlChar
*) encoding
);
1483 htmlSetMetaEncoding(result
, (const xmlChar
*) "UTF-8");
1487 htmlDocContentDumpFormatOutput(buf
, result
, (const char *) encoding
,
1489 xmlOutputBufferFlush(buf
);
1490 } else if ((method
!= NULL
) &&
1491 (xmlStrEqual(method
, (const xmlChar
*) "xhtml"))) {
1492 if (encoding
!= NULL
) {
1493 htmlSetMetaEncoding(result
, (const xmlChar
*) encoding
);
1495 htmlSetMetaEncoding(result
, (const xmlChar
*) "UTF-8");
1497 htmlDocContentDumpOutput(buf
, result
, (const char *) encoding
);
1498 xmlOutputBufferFlush(buf
);
1499 } else if ((method
!= NULL
) &&
1500 (xmlStrEqual(method
, (const xmlChar
*) "text"))) {
1503 cur
= result
->children
;
1504 while (cur
!= NULL
) {
1505 if (cur
->type
== XML_TEXT_NODE
)
1506 xmlOutputBufferWriteString(buf
, (const char *) cur
->content
);
1511 if (cur
->children
!= NULL
) {
1512 if ((cur
->children
->type
!= XML_ENTITY_DECL
) &&
1513 (cur
->children
->type
!= XML_ENTITY_REF_NODE
) &&
1514 (cur
->children
->type
!= XML_ENTITY_NODE
)) {
1515 cur
= cur
->children
;
1519 if (cur
->next
!= NULL
) {
1528 if (cur
== (xmlNodePtr
) style
->doc
) {
1532 if (cur
->next
!= NULL
) {
1536 } while (cur
!= NULL
);
1538 xmlOutputBufferFlush(buf
);
1543 XSLT_GET_IMPORT_INT(omitXmlDecl
, style
, omitXmlDeclaration
);
1544 XSLT_GET_IMPORT_INT(standalone
, style
, standalone
);
1546 if (omitXmlDecl
!= 1) {
1547 xmlOutputBufferWriteString(buf
, "<?xml version=");
1548 if (result
->version
!= NULL
) {
1549 xmlOutputBufferWriteString(buf
, "\"");
1550 xmlOutputBufferWriteString(buf
, (const char *)result
->version
);
1551 xmlOutputBufferWriteString(buf
, "\"");
1553 xmlOutputBufferWriteString(buf
, "\"1.0\"");
1554 if (encoding
== NULL
) {
1555 if (result
->encoding
!= NULL
)
1556 encoding
= result
->encoding
;
1557 else if (result
->charset
!= XML_CHAR_ENCODING_UTF8
)
1558 encoding
= (const xmlChar
*)
1559 xmlGetCharEncodingName((xmlCharEncoding
)
1562 if (encoding
!= NULL
) {
1563 xmlOutputBufferWriteString(buf
, " encoding=");
1564 xmlOutputBufferWriteString(buf
, "\"");
1565 xmlOutputBufferWriteString(buf
, (const char *) encoding
);
1566 xmlOutputBufferWriteString(buf
, "\"");
1568 switch (standalone
) {
1570 xmlOutputBufferWriteString(buf
, " standalone=\"no\"");
1573 xmlOutputBufferWriteString(buf
, " standalone=\"yes\"");
1578 xmlOutputBufferWriteString(buf
, "?>\n");
1580 if (result
->children
!= NULL
) {
1581 xmlNodePtr children
= result
->children
;
1582 xmlNodePtr child
= children
;
1585 * Hack to avoid quadratic behavior when scanning
1586 * result->children in xmlGetIntSubset called by
1587 * xmlNodeDumpOutput.
1589 result
->children
= NULL
;
1591 while (child
!= NULL
) {
1592 xmlNodeDumpOutput(buf
, result
, child
, 0, (indent
== 1),
1593 (const char *) encoding
);
1594 if (indent
&& ((child
->type
== XML_DTD_NODE
) ||
1595 ((child
->type
== XML_COMMENT_NODE
) &&
1596 (child
->next
!= NULL
))))
1597 xmlOutputBufferWriteString(buf
, "\n");
1598 child
= child
->next
;
1601 xmlOutputBufferWriteString(buf
, "\n");
1603 result
->children
= children
;
1605 xmlOutputBufferFlush(buf
);
1607 return(buf
->written
- base
);
1611 * xsltSaveResultToFilename:
1612 * @URL: a filename or URL
1613 * @result: the result xmlDocPtr
1614 * @style: the stylesheet
1615 * @compression: the compression factor (0 - 9 included)
1617 * Save the result @result obtained by applying the @style stylesheet
1620 * Returns the number of byte written or -1 in case of failure.
1623 xsltSaveResultToFilename(const char *URL
, xmlDocPtr result
,
1624 xsltStylesheetPtr style
, int compression
) {
1625 xmlOutputBufferPtr buf
;
1626 const xmlChar
*encoding
;
1629 if ((URL
== NULL
) || (result
== NULL
) || (style
== NULL
))
1631 if (result
->children
== NULL
)
1634 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1635 if (encoding
!= NULL
) {
1636 xmlCharEncodingHandlerPtr encoder
;
1638 encoder
= xmlFindCharEncodingHandler((char *)encoding
);
1639 if ((encoder
!= NULL
) &&
1640 (xmlStrEqual((const xmlChar
*)encoder
->name
,
1641 (const xmlChar
*) "UTF-8")))
1643 buf
= xmlOutputBufferCreateFilename(URL
, encoder
, compression
);
1645 buf
= xmlOutputBufferCreateFilename(URL
, NULL
, compression
);
1649 xsltSaveResultTo(buf
, result
, style
);
1650 ret
= xmlOutputBufferClose(buf
);
1655 * xsltSaveResultToFile:
1656 * @file: a FILE * I/O
1657 * @result: the result xmlDocPtr
1658 * @style: the stylesheet
1660 * Save the result @result obtained by applying the @style stylesheet
1661 * to an open FILE * I/O.
1662 * This does not close the FILE @file
1664 * Returns the number of bytes written or -1 in case of failure.
1667 xsltSaveResultToFile(FILE *file
, xmlDocPtr result
, xsltStylesheetPtr style
) {
1668 xmlOutputBufferPtr buf
;
1669 const xmlChar
*encoding
;
1672 if ((file
== NULL
) || (result
== NULL
) || (style
== NULL
))
1674 if (result
->children
== NULL
)
1677 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1678 if (encoding
!= NULL
) {
1679 xmlCharEncodingHandlerPtr encoder
;
1681 encoder
= xmlFindCharEncodingHandler((char *)encoding
);
1682 if ((encoder
!= NULL
) &&
1683 (xmlStrEqual((const xmlChar
*)encoder
->name
,
1684 (const xmlChar
*) "UTF-8")))
1686 buf
= xmlOutputBufferCreateFile(file
, encoder
);
1688 buf
= xmlOutputBufferCreateFile(file
, NULL
);
1693 xsltSaveResultTo(buf
, result
, style
);
1694 ret
= xmlOutputBufferClose(buf
);
1699 * xsltSaveResultToFd:
1700 * @fd: a file descriptor
1701 * @result: the result xmlDocPtr
1702 * @style: the stylesheet
1704 * Save the result @result obtained by applying the @style stylesheet
1705 * to an open file descriptor
1706 * This does not close the descriptor.
1708 * Returns the number of bytes written or -1 in case of failure.
1711 xsltSaveResultToFd(int fd
, xmlDocPtr result
, xsltStylesheetPtr style
) {
1712 xmlOutputBufferPtr buf
;
1713 const xmlChar
*encoding
;
1716 if ((fd
< 0) || (result
== NULL
) || (style
== NULL
))
1718 if (result
->children
== NULL
)
1721 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1722 if (encoding
!= NULL
) {
1723 xmlCharEncodingHandlerPtr encoder
;
1725 encoder
= xmlFindCharEncodingHandler((char *)encoding
);
1726 if ((encoder
!= NULL
) &&
1727 (xmlStrEqual((const xmlChar
*)encoder
->name
,
1728 (const xmlChar
*) "UTF-8")))
1730 buf
= xmlOutputBufferCreateFd(fd
, encoder
);
1732 buf
= xmlOutputBufferCreateFd(fd
, NULL
);
1736 xsltSaveResultTo(buf
, result
, style
);
1737 ret
= xmlOutputBufferClose(buf
);
1742 * xsltSaveResultToString:
1743 * @doc_txt_ptr: Memory pointer for allocated XML text
1744 * @doc_txt_len: Length of the generated XML text
1745 * @result: the result xmlDocPtr
1746 * @style: the stylesheet
1748 * Save the result @result obtained by applying the @style stylesheet
1749 * to a new allocated string.
1751 * Returns 0 in case of success and -1 in case of error
1754 xsltSaveResultToString(xmlChar
**doc_txt_ptr
, int * doc_txt_len
,
1755 xmlDocPtr result
, xsltStylesheetPtr style
) {
1756 xmlOutputBufferPtr buf
;
1757 const xmlChar
*encoding
;
1759 *doc_txt_ptr
= NULL
;
1761 if (result
->children
== NULL
)
1764 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
1765 if (encoding
!= NULL
) {
1766 xmlCharEncodingHandlerPtr encoder
;
1768 encoder
= xmlFindCharEncodingHandler((char *)encoding
);
1769 if ((encoder
!= NULL
) &&
1770 (xmlStrEqual((const xmlChar
*)encoder
->name
,
1771 (const xmlChar
*) "UTF-8")))
1773 buf
= xmlAllocOutputBuffer(encoder
);
1775 buf
= xmlAllocOutputBuffer(NULL
);
1779 xsltSaveResultTo(buf
, result
, style
);
1780 #ifdef LIBXML2_NEW_BUFFER
1781 if (buf
->conv
!= NULL
) {
1782 *doc_txt_len
= xmlBufUse(buf
->conv
);
1783 *doc_txt_ptr
= xmlStrndup(xmlBufContent(buf
->conv
), *doc_txt_len
);
1785 *doc_txt_len
= xmlBufUse(buf
->buffer
);
1786 *doc_txt_ptr
= xmlStrndup(xmlBufContent(buf
->buffer
), *doc_txt_len
);
1789 if (buf
->conv
!= NULL
) {
1790 *doc_txt_len
= buf
->conv
->use
;
1791 *doc_txt_ptr
= xmlStrndup(buf
->conv
->content
, *doc_txt_len
);
1793 *doc_txt_len
= buf
->buffer
->use
;
1794 *doc_txt_ptr
= xmlStrndup(buf
->buffer
->content
, *doc_txt_len
);
1797 (void)xmlOutputBufferClose(buf
);
1801 #ifdef WITH_PROFILER
1803 /************************************************************************
1805 * Generating profiling information *
1807 ************************************************************************/
1809 static long calibration
= -1;
1812 * xsltCalibrateTimestamps:
1814 * Used for to calibrate the xsltTimestamp() function
1815 * Should work if launched at startup and we don't loose our quantum :-)
1817 * Returns the number of milliseconds used by xsltTimestamp()
1819 #if !defined(XSLT_WIN32_PERFORMANCE_COUNTER) && \
1820 (defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY))
1822 xsltCalibrateTimestamps(void) {
1825 for (i
= 0;i
< 999;i
++)
1827 return(xsltTimestamp() / 1000);
1832 * xsltCalibrateAdjust:
1833 * @delta: a negative dealy value found
1835 * Used for to correct the calibration for xsltTimestamp()
1838 xsltCalibrateAdjust(long delta
) {
1839 calibration
+= delta
;
1845 * Used for gathering profiling data
1847 * Returns the number of tenth of milliseconds since the beginning of the
1853 #ifdef XSLT_WIN32_PERFORMANCE_COUNTER
1855 LARGE_INTEGER performanceCount
;
1856 LARGE_INTEGER performanceFrequency
;
1859 static LONGLONG startupQuadCount
= 0;
1860 static LONGLONG startupQuadFreq
= 0;
1862 ok
= QueryPerformanceCounter(&performanceCount
);
1865 quadCount
= performanceCount
.QuadPart
;
1866 if (calibration
< 0) {
1868 ok
= QueryPerformanceFrequency(&performanceFrequency
);
1871 startupQuadFreq
= performanceFrequency
.QuadPart
;
1872 startupQuadCount
= quadCount
;
1875 if (startupQuadFreq
== 0)
1877 seconds
= (quadCount
- startupQuadCount
) / (double) startupQuadFreq
;
1878 return (long) (seconds
* XSLT_TIMESTAMP_TICS_PER_SEC
);
1880 #else /* XSLT_WIN32_PERFORMANCE_COUNTER */
1881 #ifdef HAVE_CLOCK_GETTIME
1882 # if defined(CLOCK_MONOTONIC)
1883 # define XSLT_CLOCK CLOCK_MONOTONIC
1884 # elif defined(CLOCK_HIGHRES)
1885 # define XSLT_CLOCK CLOCK_HIGHRES
1887 # define XSLT_CLOCK CLOCK_REALTIME
1889 static struct timespec startup
;
1890 struct timespec cur
;
1893 if (calibration
< 0) {
1894 clock_gettime(XSLT_CLOCK
, &startup
);
1896 calibration
= xsltCalibrateTimestamps();
1897 clock_gettime(XSLT_CLOCK
, &startup
);
1901 clock_gettime(XSLT_CLOCK
, &cur
);
1902 tics
= (cur
.tv_sec
- startup
.tv_sec
) * XSLT_TIMESTAMP_TICS_PER_SEC
;
1903 tics
+= (cur
.tv_nsec
- startup
.tv_nsec
) /
1904 (1000000000l / XSLT_TIMESTAMP_TICS_PER_SEC
);
1906 tics
-= calibration
;
1909 #elif HAVE_GETTIMEOFDAY
1910 static struct timeval startup
;
1914 if (calibration
< 0) {
1915 gettimeofday(&startup
, NULL
);
1917 calibration
= xsltCalibrateTimestamps();
1918 gettimeofday(&startup
, NULL
);
1922 gettimeofday(&cur
, NULL
);
1923 tics
= (cur
.tv_sec
- startup
.tv_sec
) * XSLT_TIMESTAMP_TICS_PER_SEC
;
1924 tics
+= (cur
.tv_usec
- startup
.tv_usec
) /
1925 (1000000l / XSLT_TIMESTAMP_TICS_PER_SEC
);
1927 tics
-= calibration
;
1931 /* Neither gettimeofday() nor Win32 performance counter available */
1935 #endif /* HAVE_GETTIMEOFDAY */
1936 #endif /* XSLT_WIN32_PERFORMANCE_COUNTER */
1940 pretty_templ_match(xsltTemplatePtr templ
) {
1941 static char dst
[1001];
1942 char *src
= (char *)templ
->match
;
1945 /* strip white spaces */
1946 for (j
=0; i
<1000 && src
[j
]; i
++,j
++) {
1947 for(;src
[j
]==' ';j
++);
1950 if(i
<998 && templ
->mode
) {
1953 src
=(char *)templ
->mode
;
1954 for (j
=0; i
<999 && src
[j
]; i
++,j
++) {
1963 #define MAX_TEMPLATES 10000
1966 * xsltSaveProfiling:
1967 * @ctxt: an XSLT context
1968 * @output: a FILE * for saving the information
1970 * Save the profiling information on @output
1973 xsltSaveProfiling(xsltTransformContextPtr ctxt
, FILE *output
) {
1977 unsigned long totalt
;
1978 xsltTemplatePtr
*templates
;
1979 xsltStylesheetPtr style
;
1980 xsltTemplatePtr templ1
,templ2
;
1983 if ((output
== NULL
) || (ctxt
== NULL
))
1985 if (ctxt
->profile
== 0)
1989 max
= MAX_TEMPLATES
;
1990 templates
= xmlMalloc(max
* sizeof(xsltTemplatePtr
));
1991 if (templates
== NULL
)
1994 style
= ctxt
->style
;
1995 while (style
!= NULL
) {
1996 templ1
= style
->templates
;
1997 while (templ1
!= NULL
) {
2001 if (templ1
->nbCalls
> 0)
2002 templates
[nb
++] = templ1
;
2003 templ1
= templ1
->next
;
2006 style
= xsltNextImport(style
);
2009 for (i
= 0;i
< nb
-1;i
++) {
2010 for (j
= i
+ 1; j
< nb
; j
++) {
2011 if ((templates
[i
]->time
<= templates
[j
]->time
) ||
2012 ((templates
[i
]->time
== templates
[j
]->time
) &&
2013 (templates
[i
]->nbCalls
<= templates
[j
]->nbCalls
))) {
2014 templ1
= templates
[j
];
2015 templates
[j
] = templates
[i
];
2016 templates
[i
] = templ1
;
2022 /* print flat profile */
2024 fprintf(output
, "%6s%20s%20s%10s Calls Tot 100us Avg\n\n",
2025 "number", "match", "name", "mode");
2028 for (i
= 0;i
< nb
;i
++) {
2029 templ1
= templates
[i
];
2030 fprintf(output
, "%5d ", i
);
2031 if (templ1
->match
!= NULL
) {
2032 if (xmlStrlen(templ1
->match
) > 20)
2033 fprintf(output
, "%s\n%26s", templ1
->match
, "");
2035 fprintf(output
, "%20s", templ1
->match
);
2037 fprintf(output
, "%20s", "");
2039 if (templ1
->name
!= NULL
) {
2040 if (xmlStrlen(templ1
->name
) > 20)
2041 fprintf(output
, "%s\n%46s", templ1
->name
, "");
2043 fprintf(output
, "%20s", templ1
->name
);
2045 fprintf(output
, "%20s", "");
2047 if (templ1
->mode
!= NULL
) {
2048 if (xmlStrlen(templ1
->mode
) > 10)
2049 fprintf(output
, "%s\n%56s", templ1
->mode
, "");
2051 fprintf(output
, "%10s", templ1
->mode
);
2053 fprintf(output
, "%10s", "");
2055 fprintf(output
, " %6d", templ1
->nbCalls
);
2056 fprintf(output
, " %6ld %6ld\n", templ1
->time
,
2057 templ1
->time
/ templ1
->nbCalls
);
2058 total
+= templ1
->nbCalls
;
2059 totalt
+= templ1
->time
;
2061 fprintf(output
, "\n%30s%26s %6d %6ld\n", "Total", "", total
, totalt
);
2064 /* print call graph */
2066 childt
= xmlMalloc((nb
+ 1) * sizeof(int));
2070 /* precalculate children times */
2071 for (i
= 0; i
< nb
; i
++) {
2072 templ1
= templates
[i
];
2075 for (k
= 0; k
< nb
; k
++) {
2076 templ2
= templates
[k
];
2077 for (l
= 0; l
< templ2
->templNr
; l
++) {
2078 if (templ2
->templCalledTab
[l
] == templ1
) {
2079 childt
[i
] +=templ2
->time
;
2086 fprintf(output
, "\nindex %% time self children called name\n");
2088 for (i
= 0; i
< nb
; i
++) {
2089 char ix_str
[20], timep_str
[20], times_str
[20], timec_str
[20], called_str
[20];
2092 templ1
= templates
[i
];
2094 for (j
= 0; j
< templ1
->templNr
; j
++) {
2095 templ2
= templ1
->templCalledTab
[j
];
2096 for (k
= 0; k
< nb
; k
++) {
2097 if (templates
[k
] == templ2
)
2100 t
=templ2
?templ2
->time
:totalt
;
2101 snprintf(times_str
,sizeof(times_str
),"%8.3f",(float)t
/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 templ1
->templCountTab
[j
], /* number of times caller calls 'this' */
2105 templ1
->nbCalls
); /* total number of calls to 'this' */
2107 fprintf(output
, " %-8s %-8s %-12s %s [%d]\n",
2108 times_str
,timec_str
,called_str
,
2109 (templ2
?(templ2
->name
?(char *)templ2
->name
:pretty_templ_match(templ2
)):"-"),k
);
2112 snprintf(ix_str
,sizeof(ix_str
),"[%d]",i
);
2113 snprintf(timep_str
,sizeof(timep_str
),"%6.2f",(float)templ1
->time
*100.0/totalt
);
2114 snprintf(times_str
,sizeof(times_str
),"%8.3f",(float)templ1
->time
/XSLT_TIMESTAMP_TICS_PER_SEC
);
2115 snprintf(timec_str
,sizeof(timec_str
),"%8.3f",(float)childt
[i
]/XSLT_TIMESTAMP_TICS_PER_SEC
);
2116 fprintf(output
, "%-5s %-6s %-8s %-8s %6d %s [%d]\n",
2117 ix_str
, timep_str
,times_str
,timec_str
,
2119 templ1
->name
?(char *)templ1
->name
:pretty_templ_match(templ1
),i
);
2121 * - go over templates[0..nb] and their templCalledTab[]
2122 * - print those where we in the the call-stack
2125 for (k
= 0; k
< nb
; k
++) {
2126 templ2
= templates
[k
];
2127 for (l
= 0; l
< templ2
->templNr
; l
++) {
2128 if (templ2
->templCalledTab
[l
] == templ1
) {
2129 total
+=templ2
->templCountTab
[l
];
2133 for (k
= 0; k
< nb
; k
++) {
2134 templ2
= templates
[k
];
2135 for (l
= 0; l
< templ2
->templNr
; l
++) {
2136 if (templ2
->templCalledTab
[l
] == templ1
) {
2137 snprintf(times_str
,sizeof(times_str
),"%8.3f",(float)templ2
->time
/XSLT_TIMESTAMP_TICS_PER_SEC
);
2138 snprintf(timec_str
,sizeof(timec_str
),"%8.3f",(float)childt
[k
]/XSLT_TIMESTAMP_TICS_PER_SEC
);
2139 snprintf(called_str
,sizeof(called_str
),"%6d/%d",
2140 templ2
->templCountTab
[l
], /* number of times 'this' calls callee */
2141 total
); /* total number of calls from 'this' */
2142 fprintf(output
, " %-8s %-8s %-12s %s [%d]\n",
2143 times_str
,timec_str
,called_str
,
2144 templ2
->name
?(char *)templ2
->name
:pretty_templ_match(templ2
),k
);
2148 fprintf(output
, "-----------------------------------------------\n");
2151 fprintf(output
, "\f\nIndex by function name\n");
2152 for (i
= 0; i
< nb
; i
++) {
2153 templ1
= templates
[i
];
2154 fprintf(output
, "[%d] %s (%s:%d)\n",
2155 i
, templ1
->name
?(char *)templ1
->name
:pretty_templ_match(templ1
),
2156 templ1
->style
->doc
->URL
,templ1
->elem
->line
);
2159 fprintf(output
, "\f\n");
2165 /************************************************************************
2167 * Fetching profiling information *
2169 ************************************************************************/
2172 * xsltGetProfileInformation:
2173 * @ctxt: a transformation context
2175 * This function should be called after the transformation completed
2176 * to extract template processing profiling information if available.
2177 * The information is returned as an XML document tree like
2178 * <?xml version="1.0"?>
2180 * <template rank="1" match="*" name=""
2181 * mode="" calls="6" time="48" average="8"/>
2182 * <template rank="2" match="item2|item3" name=""
2183 * mode="" calls="10" time="30" average="3"/>
2184 * <template rank="3" match="item1" name=""
2185 * mode="" calls="5" time="17" average="3"/>
2187 * The caller will need to free up the returned tree with xmlFreeDoc()
2189 * Returns the xmlDocPtr corresponding to the result or NULL if not available.
2193 xsltGetProfileInformation(xsltTransformContextPtr ctxt
)
2195 xmlDocPtr ret
= NULL
;
2196 xmlNodePtr root
, child
;
2199 xsltStylesheetPtr style
;
2200 xsltTemplatePtr
*templates
;
2201 xsltTemplatePtr templ
;
2202 int nb
= 0, max
= 0, i
, j
;
2213 (xsltTemplatePtr
*) xmlMalloc(max
* sizeof(xsltTemplatePtr
));
2214 if (templates
== NULL
)
2218 * collect all the templates in an array
2220 style
= ctxt
->style
;
2221 while (style
!= NULL
) {
2222 templ
= style
->templates
;
2223 while (templ
!= NULL
) {
2227 if (templ
->nbCalls
> 0)
2228 templates
[nb
++] = templ
;
2229 templ
= templ
->next
;
2232 style
= (xsltStylesheetPtr
) xsltNextImport(style
);
2236 * Sort the array by time spent
2238 for (i
= 0; i
< nb
- 1; i
++) {
2239 for (j
= i
+ 1; j
< nb
; j
++) {
2240 if ((templates
[i
]->time
<= templates
[j
]->time
) ||
2241 ((templates
[i
]->time
== templates
[j
]->time
) &&
2242 (templates
[i
]->nbCalls
<= templates
[j
]->nbCalls
))) {
2243 templ
= templates
[j
];
2244 templates
[j
] = templates
[i
];
2245 templates
[i
] = templ
;
2251 * Generate a document corresponding to the results.
2253 ret
= xmlNewDoc(BAD_CAST
"1.0");
2254 root
= xmlNewDocNode(ret
, NULL
, BAD_CAST
"profile", NULL
);
2255 xmlDocSetRootElement(ret
, root
);
2257 for (i
= 0; i
< nb
; i
++) {
2258 child
= xmlNewChild(root
, NULL
, BAD_CAST
"template", NULL
);
2259 snprintf(buf
, sizeof(buf
), "%d", i
+ 1);
2260 xmlSetProp(child
, BAD_CAST
"rank", BAD_CAST buf
);
2261 xmlSetProp(child
, BAD_CAST
"match", BAD_CAST templates
[i
]->match
);
2262 xmlSetProp(child
, BAD_CAST
"name", BAD_CAST templates
[i
]->name
);
2263 xmlSetProp(child
, BAD_CAST
"mode", BAD_CAST templates
[i
]->mode
);
2265 snprintf(buf
, sizeof(buf
), "%d", templates
[i
]->nbCalls
);
2266 xmlSetProp(child
, BAD_CAST
"calls", BAD_CAST buf
);
2268 snprintf(buf
, sizeof(buf
), "%ld", templates
[i
]->time
);
2269 xmlSetProp(child
, BAD_CAST
"time", BAD_CAST buf
);
2271 snprintf(buf
, sizeof(buf
), "%ld", templates
[i
]->time
/ templates
[i
]->nbCalls
);
2272 xmlSetProp(child
, BAD_CAST
"average", BAD_CAST buf
);
2280 #endif /* WITH_PROFILER */
2282 /************************************************************************
2284 * Hooks for libxml2 XPath *
2286 ************************************************************************/
2289 * xsltXPathCompileFlags:
2290 * @style: the stylesheet
2291 * @str: the XPath expression
2292 * @flags: extra compilation flags to pass down to libxml2 XPath
2294 * Compile an XPath expression
2296 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2297 * the caller has to free the object.
2300 xsltXPathCompileFlags(xsltStylesheetPtr style
, const xmlChar
*str
, int flags
) {
2301 xmlXPathContextPtr xpathCtxt
;
2302 xmlXPathCompExprPtr ret
;
2304 if (style
!= NULL
) {
2305 xpathCtxt
= style
->principal
->xpathCtxt
;
2306 if (xpathCtxt
== NULL
)
2308 xpathCtxt
->dict
= style
->dict
;
2310 xpathCtxt
= xmlXPathNewContext(NULL
);
2311 if (xpathCtxt
== NULL
)
2314 xpathCtxt
->flags
= flags
;
2317 * Compile the expression.
2319 ret
= xmlXPathCtxtCompile(xpathCtxt
, str
);
2321 if (style
== NULL
) {
2322 xmlXPathFreeContext(xpathCtxt
);
2325 * TODO: there is a lot of optimizations which should be possible
2326 * like variable slot precomputations, function precomputations, etc.
2334 * @style: the stylesheet
2335 * @str: the XPath expression
2337 * Compile an XPath expression
2339 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2340 * the caller has to free the object.
2343 xsltXPathCompile(xsltStylesheetPtr style
, const xmlChar
*str
) {
2344 return(xsltXPathCompileFlags(style
, str
, 0));
2347 /************************************************************************
2349 * Hooks for the debugger *
2351 ************************************************************************/
2356 * xsltGetDebuggerStatus:
2358 * Get xslDebugStatus.
2360 * Returns the value of xslDebugStatus.
2363 xsltGetDebuggerStatus(void)
2365 return(xslDebugStatus
);
2368 #ifdef WITH_DEBUGGER
2371 * There is currently only 3 debugging callback defined
2372 * Debugger callbacks are disabled by default
2374 #define XSLT_CALLBACK_NUMBER 3
2376 typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks
;
2377 typedef xsltDebuggerCallbacks
*xsltDebuggerCallbacksPtr
;
2378 struct _xsltDebuggerCallbacks
{
2379 xsltHandleDebuggerCallback handler
;
2380 xsltAddCallCallback add
;
2381 xsltDropCallCallback drop
;
2384 static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks
= {
2391 * xsltSetDebuggerStatus:
2392 * @value : the value to be set
2394 * This function sets the value of xslDebugStatus.
2397 xsltSetDebuggerStatus(int value
)
2399 xslDebugStatus
= value
;
2403 * xsltSetDebuggerCallbacks:
2404 * @no : number of callbacks
2405 * @block : the block of callbacks
2407 * This function allow to plug a debugger into the XSLT library
2408 * @block points to a block of memory containing the address of @no
2409 * callback routines.
2411 * Returns 0 in case of success and -1 in case of error
2414 xsltSetDebuggerCallbacks(int no
, void *block
)
2416 xsltDebuggerCallbacksPtr callbacks
;
2418 if ((block
== NULL
) || (no
!= XSLT_CALLBACK_NUMBER
))
2421 callbacks
= (xsltDebuggerCallbacksPtr
) block
;
2422 xsltDebuggerCurrentCallbacks
.handler
= callbacks
->handler
;
2423 xsltDebuggerCurrentCallbacks
.add
= callbacks
->add
;
2424 xsltDebuggerCurrentCallbacks
.drop
= callbacks
->drop
;
2429 * xslHandleDebugger:
2430 * @cur : source node being executed
2431 * @node : data node being processed
2432 * @templ : temlate that applies to node
2433 * @ctxt : the xslt transform context
2435 * If either cur or node are a breakpoint, or xslDebugStatus in state
2436 * where debugging must occcur at this time then transfer control
2437 * to the xslDebugBreak function
2440 xslHandleDebugger(xmlNodePtr cur
, xmlNodePtr node
, xsltTemplatePtr templ
,
2441 xsltTransformContextPtr ctxt
)
2443 if (xsltDebuggerCurrentCallbacks
.handler
!= NULL
)
2444 xsltDebuggerCurrentCallbacks
.handler(cur
, node
, templ
, ctxt
);
2449 * @templ : current template being applied
2450 * @source : the source node being processed
2452 * Add template "call" to call stack
2453 * Returns : 1 on sucess 0 otherwise an error may be printed if
2454 * WITH_XSLT_DEBUG_BREAKPOINTS is defined
2457 xslAddCall(xsltTemplatePtr templ
, xmlNodePtr source
)
2459 if (xsltDebuggerCurrentCallbacks
.add
!= NULL
)
2460 return(xsltDebuggerCurrentCallbacks
.add(templ
, source
));
2467 * Drop the topmost item off the call stack
2472 if (xsltDebuggerCurrentCallbacks
.drop
!= NULL
)
2473 xsltDebuggerCurrentCallbacks
.drop();
2476 #endif /* WITH_DEBUGGER */