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.
17 #ifdef HAVE_SYS_TIME_H
28 #include <libxml/xmlmemory.h>
29 #include <libxml/tree.h>
30 #include <libxml/HTMLtree.h>
31 #include <libxml/xmlerror.h>
32 #include <libxml/xmlIO.h>
33 #include "xsltutils.h"
34 #include "templates.h"
35 #include "xsltInternals.h"
37 #include "transform.h"
39 /* gettimeofday on Windows ??? */
40 #if defined(WIN32) && !defined(__CYGWIN__)
43 #pragma comment(lib, "ws2_32.lib")
44 #define gettimeofday(p1,p2)
45 #define HAVE_GETTIMEOFDAY
46 #define XSLT_WIN32_PERFORMANCE_COUNTER
52 #define vsnprintf trio_vsnprintf
55 /************************************************************************
57 * Convenience function *
59 ************************************************************************/
63 * @style: the stylesheet
65 * @name: the attribute name
66 * @nameSpace: the URI of the namespace
68 * Similar to xmlGetNsProp() but with a slightly different semantic
70 * Search and get the value of an attribute associated to a node
71 * This attribute has to be anchored in the namespace specified,
72 * or has no namespace and the element is in that namespace.
74 * This does the entity substitution.
75 * This function looks in DTD attribute declaration for #FIXED or
76 * default declaration values unless DTD use has been turned off.
78 * Returns the attribute value or NULL if not found. The string is allocated
79 * in the stylesheet dictionnary.
82 xsltGetCNsProp(xsltStylesheetPtr style, xmlNodePtr node,
83 const xmlChar *name, const xmlChar *nameSpace) {
90 if ((node == NULL) || (style == NULL) || (style->dict == NULL))
93 prop = node->properties;
94 if (nameSpace == NULL) {
95 return xmlGetProp(node, name);
97 while (prop != NULL) {
100 * - same attribute names
101 * - and the attribute carrying that namespace
103 if ((xmlStrEqual(prop->name, name)) &&
104 (((prop->ns == NULL) && (node->ns != NULL) &&
105 (xmlStrEqual(node->ns->href, nameSpace))) ||
106 ((prop->ns != NULL) &&
107 (xmlStrEqual(prop->ns->href, nameSpace))))) {
109 tmp = xmlNodeListGetString(node->doc, prop->children, 1);
111 ret = xmlDictLookup(style->dict, BAD_CAST "", 0);
113 ret = xmlDictLookup(style->dict, tmp, -1);
122 * Check if there is a default declaration in the internal
123 * or external subsets
127 if (doc->intSubset != NULL) {
128 xmlAttributePtr attrDecl;
130 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
131 if ((attrDecl == NULL) && (doc->extSubset != NULL))
132 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
134 if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) {
136 * The DTD declaration only allows a prefix search
138 ns = xmlSearchNs(doc, node, attrDecl->prefix);
139 if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace)))
140 return(xmlDictLookup(style->dict,
141 attrDecl->defaultValue, -1));
150 * @name: the attribute name
151 * @nameSpace: the URI of the namespace
153 * Similar to xmlGetNsProp() but with a slightly different semantic
155 * Search and get the value of an attribute associated to a node
156 * This attribute has to be anchored in the namespace specified,
157 * or has no namespace and the element is in that namespace.
159 * This does the entity substitution.
160 * This function looks in DTD attribute declaration for #FIXED or
161 * default declaration values unless DTD use has been turned off.
163 * Returns the attribute value or NULL if not found.
164 * It's up to the caller to free the memory.
167 xsltGetNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) {
175 prop = node->properties;
176 if (nameSpace == NULL)
177 return(xmlGetProp(node, name));
178 while (prop != NULL) {
181 * - same attribute names
182 * - and the attribute carrying that namespace
184 if ((xmlStrEqual(prop->name, name)) &&
185 (((prop->ns == NULL) && (node->ns != NULL) &&
186 (xmlStrEqual(node->ns->href, nameSpace))) ||
187 ((prop->ns != NULL) &&
188 (xmlStrEqual(prop->ns->href, nameSpace))))) {
191 ret = xmlNodeListGetString(node->doc, prop->children, 1);
192 if (ret == NULL) return(xmlStrdup((xmlChar *)""));
199 * Check if there is a default declaration in the internal
200 * or external subsets
204 if (doc->intSubset != NULL) {
205 xmlAttributePtr attrDecl;
207 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
208 if ((attrDecl == NULL) && (doc->extSubset != NULL))
209 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
211 if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) {
213 * The DTD declaration only allows a prefix search
215 ns = xmlSearchNs(doc, node, attrDecl->prefix);
216 if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace)))
217 return(xmlStrdup(attrDecl->defaultValue));
226 * @utf: a sequence of UTF-8 encoded bytes
227 * @len: a pointer to @bytes len
229 * Read one UTF8 Char from @utf
230 * Function copied from libxml2 xmlGetUTF8Char() ... to discard ultimately
231 * and use the original API
233 * Returns the char value or -1 in case of error and update @len with the
234 * number of bytes used
237 xsltGetUTF8Char(const unsigned char *utf, int *len) {
251 if ((utf[1] & 0xc0) != 0x80)
253 if ((c & 0xe0) == 0xe0) {
256 if ((utf[2] & 0xc0) != 0x80)
258 if ((c & 0xf0) == 0xf0) {
261 if ((c & 0xf8) != 0xf0 || (utf[3] & 0xc0) != 0x80)
265 c = (utf[0] & 0x7) << 18;
266 c |= (utf[1] & 0x3f) << 12;
267 c |= (utf[2] & 0x3f) << 6;
272 c = (utf[0] & 0xf) << 12;
273 c |= (utf[1] & 0x3f) << 6;
279 c = (utf[0] & 0x1f) << 6;
294 /************************************************************************
296 * Handling of XSLT stylesheets messages *
298 ************************************************************************/
302 * @ctxt: an XSLT processing context
303 * @node: The current node
304 * @inst: The node containing the message instruction
306 * Process and xsl:message construct
309 xsltMessage(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst) {
310 xmlChar *prop, *message;
313 if ((ctxt == NULL) || (inst == NULL))
316 prop = xsltGetNsProp(inst, (const xmlChar *)"terminate", XSLT_NAMESPACE);
318 if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
320 } else if (xmlStrEqual(prop, (const xmlChar *)"no")) {
323 xsltGenericError(xsltGenericErrorContext,
324 "xsl:message : terminate expecting 'yes' or 'no'\n");
325 ctxt->state = XSLT_STATE_ERROR;
329 message = xsltEvalTemplateString(ctxt, node, inst);
330 if (message != NULL) {
331 int len = xmlStrlen(message);
333 xsltGenericError(xsltGenericErrorContext, "%s",
334 (const char *)message);
335 if ((len > 0) && (message[len - 1] != '\n'))
336 xsltGenericError(xsltGenericErrorContext, "\n");
340 ctxt->state = XSLT_STATE_STOPPED;
343 /************************************************************************
345 * Handling of out of context errors *
347 ************************************************************************/
349 #define XSLT_GET_VAR_STR(msg, str) { \
355 str = (char *) xmlMalloc(150); \
363 chars = vsnprintf(str, size, msg, ap); \
365 if ((chars > -1) && (chars < size)) \
371 if ((larger = (char *) xmlRealloc(str, size)) == NULL) {\
379 * xsltGenericErrorDefaultFunc:
380 * @ctx: an error context
381 * @msg: the message to display/transmit
382 * @...: extra parameters for the message display
384 * Default handler for out of context error messages.
387 xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
390 if (xsltGenericErrorContext == NULL)
391 xsltGenericErrorContext = (void *) stderr;
394 vfprintf((FILE *)xsltGenericErrorContext, msg, args);
398 xmlGenericErrorFunc xsltGenericError = xsltGenericErrorDefaultFunc;
399 void *xsltGenericErrorContext = NULL;
403 * xsltSetGenericErrorFunc:
404 * @ctx: the new error handling context
405 * @handler: the new handler function
407 * Function to reset the handler and the error context for out of
408 * context error messages.
409 * This simply means that @handler will be called for subsequent
410 * error messages while not parsing nor validating. And @ctx will
411 * be passed as first argument to @handler
412 * One can simply force messages to be emitted to another FILE * than
413 * stderr by setting @ctx to this file handle and @handler to NULL.
416 xsltSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) {
417 xsltGenericErrorContext = ctx;
419 xsltGenericError = handler;
421 xsltGenericError = xsltGenericErrorDefaultFunc;
425 * xsltGenericDebugDefaultFunc:
426 * @ctx: an error context
427 * @msg: the message to display/transmit
428 * @...: extra parameters for the message display
430 * Default handler for out of context error messages.
433 xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
436 if (xsltGenericDebugContext == NULL)
440 vfprintf((FILE *)xsltGenericDebugContext, msg, args);
444 xmlGenericErrorFunc xsltGenericDebug = xsltGenericDebugDefaultFunc;
445 void *xsltGenericDebugContext = NULL;
449 * xsltSetGenericDebugFunc:
450 * @ctx: the new error handling context
451 * @handler: the new handler function
453 * Function to reset the handler and the error context for out of
454 * context error messages.
455 * This simply means that @handler will be called for subsequent
456 * error messages while not parsing or validating. And @ctx will
457 * be passed as first argument to @handler
458 * One can simply force messages to be emitted to another FILE * than
459 * stderr by setting @ctx to this file handle and @handler to NULL.
462 xsltSetGenericDebugFunc(void *ctx, xmlGenericErrorFunc handler) {
463 xsltGenericDebugContext = ctx;
465 xsltGenericDebug = handler;
467 xsltGenericDebug = xsltGenericDebugDefaultFunc;
471 * xsltPrintErrorContext:
472 * @ctxt: the transformation context
473 * @style: the stylesheet
474 * @node: the current node being processed
476 * Display the context of an error.
479 xsltPrintErrorContext(xsltTransformContextPtr ctxt,
480 xsltStylesheetPtr style, xmlNodePtr node) {
482 const xmlChar *file = NULL;
483 const xmlChar *name = NULL;
484 const char *type = "error";
485 xmlGenericErrorFunc error = xsltGenericError;
486 void *errctx = xsltGenericErrorContext;
489 ctxt->state = XSLT_STATE_ERROR;
490 if (ctxt->error != NULL) {
492 errctx = ctxt->errctx;
495 if ((node == NULL) && (ctxt != NULL))
499 if ((node->type == XML_DOCUMENT_NODE) ||
500 (node->type == XML_HTML_DOCUMENT_NODE)) {
501 xmlDocPtr doc = (xmlDocPtr) node;
505 line = xmlGetLineNo(node);
506 if ((node->doc != NULL) && (node->doc->URL != NULL))
507 file = node->doc->URL;
508 if (node->name != NULL)
514 type = "runtime error";
515 else if (style != NULL)
516 type = "compilation error";
518 if ((file != NULL) && (line != 0) && (name != NULL))
519 error(errctx, "%s: file %s line %d element %s\n",
520 type, file, line, name);
521 else if ((file != NULL) && (name != NULL))
522 error(errctx, "%s: file %s element %s\n", type, file, name);
523 else if ((file != NULL) && (line != 0))
524 error(errctx, "%s: file %s line %d\n", type, file, line);
525 else if (file != NULL)
526 error(errctx, "%s: file %s\n", type, file);
527 else if (name != NULL)
528 error(errctx, "%s: element %s\n", type, name);
530 error(errctx, "%s\n", type);
534 * xsltSetTransformErrorFunc:
535 * @ctxt: the XSLT transformation context
536 * @ctx: the new error handling context
537 * @handler: the new handler function
539 * Function to reset the handler and the error context for out of
540 * context error messages specific to a given XSLT transromation.
542 * This simply means that @handler will be called for subsequent
543 * error messages while running the transformation.
546 xsltSetTransformErrorFunc(xsltTransformContextPtr ctxt,
547 void *ctx, xmlGenericErrorFunc handler)
549 ctxt->error = handler;
554 * xsltTransformError:
555 * @ctxt: an XSLT transformation context
556 * @style: the XSLT stylesheet used
557 * @node: the current node in the stylesheet
558 * @msg: the message to display/transmit
559 * @...: extra parameters for the message display
561 * Display and format an error messages, gives file, line, position and
562 * extra parameters, will use the specific transformation context if available
565 xsltTransformError(xsltTransformContextPtr ctxt,
566 xsltStylesheetPtr style,
568 const char *msg, ...) {
569 xmlGenericErrorFunc error = xsltGenericError;
570 void *errctx = xsltGenericErrorContext;
574 ctxt->state = XSLT_STATE_ERROR;
575 if (ctxt->error != NULL) {
577 errctx = ctxt->errctx;
580 if ((node == NULL) && (ctxt != NULL))
582 xsltPrintErrorContext(ctxt, style, node);
583 XSLT_GET_VAR_STR(msg, str);
584 error(errctx, "%s", str);
589 /************************************************************************
593 ************************************************************************/
597 * @dict: a dictionnary
598 * @name: the full QName
599 * @prefix: the return value
601 * Split QNames into prefix and local names, both allocated from a dictionnary.
603 * Returns: the localname or NULL in case of error.
606 xsltSplitQName(xmlDictPtr dict, const xmlChar *name, const xmlChar **prefix) {
608 const xmlChar *ret = NULL;
611 if ((name == NULL) || (dict == NULL)) return(NULL);
613 return(xmlDictLookup(dict, name, -1));
614 while ((name[len] != 0) && (name[len] != ':')) len++;
615 if (name[len] == 0) return(xmlDictLookup(dict, name, -1));
616 *prefix = xmlDictLookup(dict, name, len);
617 ret = xmlDictLookup(dict, &name[len + 1], -1);
623 * @node: the node holding the QName
624 * @name: pointer to the initial QName value
626 * This function analyzes @name, if the name contains a prefix,
627 * the function seaches the associated namespace in scope for it.
628 * It will also replace @name value with the NCName, the old value being
630 * Errors in the prefix lookup are signalled by setting @name to NULL.
632 * NOTE: the namespace returned is a pointer to the place where it is
633 * defined and hence has the same lifespan as the document holding it.
635 * Returns the namespace URI if there is a prefix, or NULL if @name is
639 xsltGetQNameURI(xmlNodePtr node, xmlChar ** name)
648 if ((qname == NULL) || (*qname == 0))
651 xsltGenericError(xsltGenericErrorContext,
652 "QName: no element for namespace lookup %s\n",
659 /* nasty but valid */
664 * we are not trying to validate but just to cut, and yes it will
665 * work even if this is a set of UTF-8 encoded chars
667 while ((qname[len] != 0) && (qname[len] != ':'))
674 * handle xml: separately, this one is magical
676 if ((qname[0] == 'x') && (qname[1] == 'm') &&
677 (qname[2] == 'l') && (qname[3] == ':')) {
680 *name = xmlStrdup(&qname[4]);
682 return(XML_XML_NAMESPACE);
686 ns = xmlSearchNs(node->doc, node, qname);
688 xsltGenericError(xsltGenericErrorContext,
689 "%s:%s : no namespace bound to prefix %s\n",
690 qname, &qname[len + 1], qname);
695 *name = xmlStrdup(&qname[len + 1]);
702 * @style: stylesheet pointer
703 * @node: the node holding the QName
704 * @name: pointer to the initial QName value
706 * This function is similar to xsltGetQNameURI, but is used when
707 * @name is a dictionary entry.
709 * Returns the namespace URI if there is a prefix, or NULL if @name is
713 xsltGetQNameURI2(xsltStylesheetPtr style, xmlNodePtr node,
714 const xmlChar **name) {
721 qname = (xmlChar *)*name;
722 if ((qname == NULL) || (*qname == 0))
725 xsltGenericError(xsltGenericErrorContext,
726 "QName: no element for namespace lookup %s\n",
733 * we are not trying to validate but just to cut, and yes it will
734 * work even if this is a set of UTF-8 encoded chars
736 while ((qname[len] != 0) && (qname[len] != ':'))
743 * handle xml: separately, this one is magical
745 if ((qname[0] == 'x') && (qname[1] == 'm') &&
746 (qname[2] == 'l') && (qname[3] == ':')) {
749 *name = xmlDictLookup(style->dict, &qname[4], -1);
750 return(XML_XML_NAMESPACE);
753 qname = xmlStrndup(*name, len);
754 ns = xmlSearchNs(node->doc, node, qname);
756 xsltGenericError(xsltGenericErrorContext,
757 "%s : no namespace bound to prefix %s\n",
763 *name = xmlDictLookup(style->dict, (*name)+len+1, -1);
768 /************************************************************************
772 ************************************************************************/
775 * xsltDocumentSortFunction:
776 * @list: the node set
778 * reorder the current node list @list accordingly to the document order
781 xsltDocumentSortFunction(xmlNodeSetPtr list) {
791 /* TODO: sort is really not optimized, does it needs to ? */
792 for (i = 0;i < len -1;i++) {
793 for (j = i + 1; j < len; j++) {
794 tst = xmlXPathCmpNodes(list->nodeTab[i], list->nodeTab[j]);
796 node = list->nodeTab[i];
797 list->nodeTab[i] = list->nodeTab[j];
798 list->nodeTab[j] = node;
805 * xsltComputeSortResult:
806 * @ctxt: a XSLT process context
809 * reorder the current node list accordingly to the set of sorting
810 * requirement provided by the array of nodes.
812 * Returns a ordered XPath nodeset or NULL in case of error.
815 xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) {
816 xmlXPathObjectPtr *results = NULL;
817 xmlNodeSetPtr list = NULL;
818 xmlXPathObjectPtr res;
821 xsltStylePreCompPtr comp;
824 int oldPos, oldSize ;
826 xmlNsPtr *oldNamespaces;
830 xsltGenericError(xsltGenericErrorContext,
831 "xsl:sort : compilation failed\n");
835 if ((comp->select == NULL) || (comp->comp == NULL))
838 list = ctxt->nodeList;
839 if ((list == NULL) || (list->nodeNr <= 1))
844 /* TODO: xsl:sort lang attribute */
845 /* TODO: xsl:sort case-order attribute */
848 results = xmlMalloc(len * sizeof(xmlXPathObjectPtr));
849 if (results == NULL) {
850 xsltGenericError(xsltGenericErrorContext,
851 "xsltComputeSortResult: memory allocation failure\n");
855 oldNode = ctxt->node;
856 oldInst = ctxt->inst;
857 oldPos = ctxt->xpathCtxt->proximityPosition;
858 oldSize = ctxt->xpathCtxt->contextSize;
859 oldNsNr = ctxt->xpathCtxt->nsNr;
860 oldNamespaces = ctxt->xpathCtxt->namespaces;
861 for (i = 0;i < len;i++) {
863 ctxt->xpathCtxt->contextSize = len;
864 ctxt->xpathCtxt->proximityPosition = i + 1;
865 ctxt->node = list->nodeTab[i];
866 ctxt->xpathCtxt->node = ctxt->node;
867 ctxt->xpathCtxt->namespaces = comp->nsList;
868 ctxt->xpathCtxt->nsNr = comp->nsNr;
869 res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
871 if (res->type != XPATH_STRING)
872 res = xmlXPathConvertString(res);
874 res = xmlXPathConvertNumber(res);
875 res->index = i; /* Save original pos for dupl resolv */
877 if (res->type == XPATH_NUMBER) {
880 #ifdef WITH_XSLT_DEBUG_PROCESS
881 xsltGenericDebug(xsltGenericDebugContext,
882 "xsltComputeSortResult: select didn't evaluate to a number\n");
887 if (res->type == XPATH_STRING) {
890 #ifdef WITH_XSLT_DEBUG_PROCESS
891 xsltGenericDebug(xsltGenericDebugContext,
892 "xsltComputeSortResult: select didn't evaluate to a string\n");
898 ctxt->state = XSLT_STATE_STOPPED;
902 ctxt->node = oldNode;
903 ctxt->inst = oldInst;
904 ctxt->xpathCtxt->contextSize = oldSize;
905 ctxt->xpathCtxt->proximityPosition = oldPos;
906 ctxt->xpathCtxt->nsNr = oldNsNr;
907 ctxt->xpathCtxt->namespaces = oldNamespaces;
913 * xsltDefaultSortFunction:
914 * @ctxt: a XSLT process context
915 * @sorts: array of sort nodes
916 * @nbsorts: the number of sorts in the array
918 * reorder the current node list accordingly to the set of sorting
919 * requirement provided by the arry of nodes.
922 xsltDefaultSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts,
924 xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT];
925 xmlXPathObjectPtr *results = NULL, *res;
926 xmlNodeSetPtr list = NULL;
927 int descending, number, desc, numb;
933 xmlXPathObjectPtr tmp;
934 xsltStylePreCompPtr comp;
935 int tempstype[XSLT_MAX_SORT], temporder[XSLT_MAX_SORT];
937 if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) ||
938 (nbsorts >= XSLT_MAX_SORT))
940 if (sorts[0] == NULL)
942 comp = sorts[0]->psvi;
946 list = ctxt->nodeList;
947 if ((list == NULL) || (list->nodeNr <= 1))
948 return; /* nothing to do */
950 for (j = 0; j < nbsorts; j++) {
951 comp = sorts[j]->psvi;
953 if ((comp->stype == NULL) && (comp->has_stype != 0)) {
955 xsltEvalAttrValueTemplate(ctxt, sorts[j],
956 (const xmlChar *) "data-type",
958 if (comp->stype != NULL) {
960 if (xmlStrEqual(comp->stype, (const xmlChar *) "text"))
962 else if (xmlStrEqual(comp->stype, (const xmlChar *) "number"))
965 xsltTransformError(ctxt, NULL, sorts[j],
966 "xsltDoSortFunction: no support for data-type = %s\n",
968 comp->number = 0; /* use default */
973 if ((comp->order == NULL) && (comp->has_order != 0)) {
974 comp->order = xsltEvalAttrValueTemplate(ctxt, sorts[j],
975 (const xmlChar *) "order",
977 if (comp->order != NULL) {
979 if (xmlStrEqual(comp->order, (const xmlChar *) "ascending"))
980 comp->descending = 0;
981 else if (xmlStrEqual(comp->order,
982 (const xmlChar *) "descending"))
983 comp->descending = 1;
985 xsltTransformError(ctxt, NULL, sorts[j],
986 "xsltDoSortFunction: invalid value %s for order\n",
988 comp->descending = 0; /* use default */
996 resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]);
997 for (i = 1;i < XSLT_MAX_SORT;i++)
998 resultsTab[i] = NULL;
1000 results = resultsTab[0];
1002 comp = sorts[0]->psvi;
1003 descending = comp->descending;
1004 number = comp->number;
1005 if (results == NULL)
1008 /* Shell's sort of node-set */
1009 for (incr = len / 2; incr > 0; incr /= 2) {
1010 for (i = incr; i < len; i++) {
1012 if (results[i] == NULL)
1016 if (results[j] == NULL)
1020 /* We make NaN smaller than number in accordance
1022 if (xmlXPathIsNaN(results[j]->floatval)) {
1023 if (xmlXPathIsNaN(results[j + incr]->floatval))
1027 } else if (xmlXPathIsNaN(results[j + incr]->floatval))
1029 else if (results[j]->floatval ==
1030 results[j + incr]->floatval)
1032 else if (results[j]->floatval >
1033 results[j + incr]->floatval)
1037 tst = xmlStrcmp(results[j]->stringval,
1038 results[j + incr]->stringval);
1045 * Okay we need to use multi level sorts
1048 while (depth < nbsorts) {
1049 if (sorts[depth] == NULL)
1051 comp = sorts[depth]->psvi;
1054 desc = comp->descending;
1055 numb = comp->number;
1058 * Compute the result of the next level for the
1059 * full set, this might be optimized ... or not
1061 if (resultsTab[depth] == NULL)
1062 resultsTab[depth] = xsltComputeSortResult(ctxt,
1064 res = resultsTab[depth];
1067 if (res[j] == NULL) {
1068 if (res[j+incr] != NULL)
1072 /* We make NaN smaller than number in
1073 accordance with XSLT spec */
1074 if (xmlXPathIsNaN(res[j]->floatval)) {
1075 if (xmlXPathIsNaN(res[j +
1080 } else if (xmlXPathIsNaN(res[j + incr]->
1083 else if (res[j]->floatval == res[j + incr]->
1086 else if (res[j]->floatval >
1087 res[j + incr]->floatval)
1091 tst = xmlStrcmp(res[j]->stringval,
1092 res[j + incr]->stringval);
1099 * if we still can't differenciate at this level
1100 * try one level deeper.
1108 tst = results[j]->index > results[j + incr]->index;
1112 results[j] = results[j + incr];
1113 results[j + incr] = tmp;
1114 node = list->nodeTab[j];
1115 list->nodeTab[j] = list->nodeTab[j + incr];
1116 list->nodeTab[j + incr] = node;
1118 while (depth < nbsorts) {
1119 if (sorts[depth] == NULL)
1121 if (resultsTab[depth] == NULL)
1123 res = resultsTab[depth];
1125 res[j] = res[j + incr];
1126 res[j + incr] = tmp;
1136 for (j = 0; j < nbsorts; j++) {
1137 comp = sorts[j]->psvi;
1138 if (tempstype[j] == 1) {
1139 /* The data-type needs to be recomputed each time */
1140 xmlFree((void *)(comp->stype));
1143 if (temporder[j] == 1) {
1144 /* The order needs to be recomputed each time */
1145 xmlFree((void *)(comp->order));
1148 if (resultsTab[j] != NULL) {
1149 for (i = 0;i < len;i++)
1150 xmlXPathFreeObject(resultsTab[j][i]);
1151 xmlFree(resultsTab[j]);
1157 static xsltSortFunc xsltSortFunction = xsltDefaultSortFunction;
1160 * xsltDoSortFunction:
1161 * @ctxt: a XSLT process context
1162 * @sorts: array of sort nodes
1163 * @nbsorts: the number of sorts in the array
1165 * reorder the current node list accordingly to the set of sorting
1166 * requirement provided by the arry of nodes.
1167 * This is a wrapper function, the actual function used is specified
1168 * using xsltSetCtxtSortFunc() to set the context specific sort function,
1169 * or xsltSetSortFunc() to set the global sort function.
1170 * If a sort function is set on the context, this will get called.
1171 * Otherwise the global sort function is called.
1174 xsltDoSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr * sorts,
1177 if (ctxt->sortfunc != NULL)
1178 (ctxt->sortfunc)(ctxt, sorts, nbsorts);
1179 else if (xsltSortFunction != NULL)
1180 xsltSortFunction(ctxt, sorts, nbsorts);
1185 * @handler: the new handler function
1187 * Function to reset the global handler for XSLT sorting.
1188 * If the handler is NULL, the default sort function will be used.
1191 xsltSetSortFunc(xsltSortFunc handler) {
1192 if (handler != NULL)
1193 xsltSortFunction = handler;
1195 xsltSortFunction = xsltDefaultSortFunction;
1199 * xsltSetCtxtSortFunc:
1200 * @ctxt: a XSLT process context
1201 * @handler: the new handler function
1203 * Function to set the handler for XSLT sorting
1204 * for the specified context.
1205 * If the handler is NULL, then the global
1206 * sort function will be called
1209 xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt, xsltSortFunc handler) {
1210 ctxt->sortfunc = handler;
1213 /************************************************************************
1217 ************************************************************************/
1220 * xsltSetCtxtParseOptions:
1221 * @ctxt: a XSLT process context
1222 * @options: a combination of libxml2 xmlParserOption
1224 * Change the default parser option passed by the XSLT engine to the
1225 * parser when using document() loading.
1227 * Returns the previous options or -1 in case of error
1230 xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt, int options)
1236 oldopts = ctxt->parserOptions;
1237 ctxt->parserOptions = options;
1241 /************************************************************************
1245 ************************************************************************/
1249 * @buf: an output buffer
1250 * @result: the result xmlDocPtr
1251 * @style: the stylesheet
1253 * Save the result @result obtained by applying the @style stylesheet
1254 * to an I/O output channel @buf
1256 * Returns the number of byte written or -1 in case of failure.
1259 xsltSaveResultTo(xmlOutputBufferPtr buf, xmlDocPtr result,
1260 xsltStylesheetPtr style) {
1261 const xmlChar *encoding;
1263 const xmlChar *method;
1266 if ((buf == NULL) || (result == NULL) || (style == NULL))
1268 if ((result->children == NULL) ||
1269 ((result->children->type == XML_DTD_NODE) &&
1270 (result->children->next == NULL)))
1273 if ((style->methodURI != NULL) &&
1274 ((style->method == NULL) ||
1275 (!xmlStrEqual(style->method, (const xmlChar *) "xhtml")))) {
1276 xsltGenericError(xsltGenericErrorContext,
1277 "xsltSaveResultTo : unknown ouput method\n");
1281 base = buf->written;
1283 XSLT_GET_IMPORT_PTR(method, style, method)
1284 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1285 XSLT_GET_IMPORT_INT(indent, style, indent);
1287 if ((method == NULL) && (result->type == XML_HTML_DOCUMENT_NODE))
1288 method = (const xmlChar *) "html";
1290 if ((method != NULL) &&
1291 (xmlStrEqual(method, (const xmlChar *) "html"))) {
1292 if (encoding != NULL) {
1293 htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1295 htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
1299 htmlDocContentDumpFormatOutput(buf, result, (const char *) encoding,
1301 xmlOutputBufferFlush(buf);
1302 } else if ((method != NULL) &&
1303 (xmlStrEqual(method, (const xmlChar *) "xhtml"))) {
1304 if (encoding != NULL) {
1305 htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1307 htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
1309 htmlDocContentDumpOutput(buf, result, (const char *) encoding);
1310 xmlOutputBufferFlush(buf);
1311 } else if ((method != NULL) &&
1312 (xmlStrEqual(method, (const xmlChar *) "text"))) {
1315 cur = result->children;
1316 while (cur != NULL) {
1317 if (cur->type == XML_TEXT_NODE)
1318 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1323 if (cur->children != NULL) {
1324 if ((cur->children->type != XML_ENTITY_DECL) &&
1325 (cur->children->type != XML_ENTITY_REF_NODE) &&
1326 (cur->children->type != XML_ENTITY_NODE)) {
1327 cur = cur->children;
1331 if (cur->next != NULL) {
1340 if (cur == (xmlNodePtr) style->doc) {
1344 if (cur->next != NULL) {
1348 } while (cur != NULL);
1350 xmlOutputBufferFlush(buf);
1355 XSLT_GET_IMPORT_INT(omitXmlDecl, style, omitXmlDeclaration);
1356 XSLT_GET_IMPORT_INT(standalone, style, standalone);
1358 if (omitXmlDecl != 1) {
1359 xmlOutputBufferWriteString(buf, "<?xml version=");
1360 if (result->version != NULL)
1361 xmlBufferWriteQuotedString(buf->buffer, result->version);
1363 xmlOutputBufferWriteString(buf, "\"1.0\"");
1364 if (encoding == NULL) {
1365 if (result->encoding != NULL)
1366 encoding = result->encoding;
1367 else if (result->charset != XML_CHAR_ENCODING_UTF8)
1368 encoding = (const xmlChar *)
1369 xmlGetCharEncodingName((xmlCharEncoding)
1372 if (encoding != NULL) {
1373 xmlOutputBufferWriteString(buf, " encoding=");
1374 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
1376 switch (standalone) {
1378 xmlOutputBufferWriteString(buf, " standalone=\"no\"");
1381 xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
1386 xmlOutputBufferWriteString(buf, "?>\n");
1388 if (result->children != NULL) {
1389 xmlNodePtr child = result->children;
1391 while (child != NULL) {
1392 xmlNodeDumpOutput(buf, result, child, 0, (indent == 1),
1393 (const char *) encoding);
1394 if ((child->type == XML_DTD_NODE) ||
1395 ((child->type == XML_COMMENT_NODE) &&
1396 (child->next != NULL)))
1397 xmlOutputBufferWriteString(buf, "\n");
1398 child = child->next;
1400 xmlOutputBufferWriteString(buf, "\n");
1402 xmlOutputBufferFlush(buf);
1404 return(buf->written - base);
1408 * xsltSaveResultToFilename:
1409 * @URL: a filename or URL
1410 * @result: the result xmlDocPtr
1411 * @style: the stylesheet
1412 * @compression: the compression factor (0 - 9 included)
1414 * Save the result @result obtained by applying the @style stylesheet
1417 * Returns the number of byte written or -1 in case of failure.
1420 xsltSaveResultToFilename(const char *URL, xmlDocPtr result,
1421 xsltStylesheetPtr style, int compression) {
1422 xmlOutputBufferPtr buf;
1423 const xmlChar *encoding;
1426 if ((URL == NULL) || (result == NULL) || (style == NULL))
1428 if (result->children == NULL)
1431 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1432 if (encoding != NULL) {
1433 xmlCharEncodingHandlerPtr encoder;
1435 encoder = xmlFindCharEncodingHandler((char *)encoding);
1436 if ((encoder != NULL) &&
1437 (xmlStrEqual((const xmlChar *)encoder->name,
1438 (const xmlChar *) "UTF-8")))
1440 buf = xmlOutputBufferCreateFilename(URL, encoder, compression);
1442 buf = xmlOutputBufferCreateFilename(URL, NULL, compression);
1446 xsltSaveResultTo(buf, result, style);
1447 ret = xmlOutputBufferClose(buf);
1452 * xsltSaveResultToFile:
1453 * @file: a FILE * I/O
1454 * @result: the result xmlDocPtr
1455 * @style: the stylesheet
1457 * Save the result @result obtained by applying the @style stylesheet
1458 * to an open FILE * I/O.
1459 * This does not close the FILE @file
1461 * Returns the number of bytes written or -1 in case of failure.
1464 xsltSaveResultToFile(FILE *file, xmlDocPtr result, xsltStylesheetPtr style) {
1465 xmlOutputBufferPtr buf;
1466 const xmlChar *encoding;
1469 if ((file == NULL) || (result == NULL) || (style == NULL))
1471 if (result->children == NULL)
1474 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1475 if (encoding != NULL) {
1476 xmlCharEncodingHandlerPtr encoder;
1478 encoder = xmlFindCharEncodingHandler((char *)encoding);
1479 if ((encoder != NULL) &&
1480 (xmlStrEqual((const xmlChar *)encoder->name,
1481 (const xmlChar *) "UTF-8")))
1483 buf = xmlOutputBufferCreateFile(file, encoder);
1485 buf = xmlOutputBufferCreateFile(file, NULL);
1490 xsltSaveResultTo(buf, result, style);
1491 ret = xmlOutputBufferClose(buf);
1496 * xsltSaveResultToFd:
1497 * @fd: a file descriptor
1498 * @result: the result xmlDocPtr
1499 * @style: the stylesheet
1501 * Save the result @result obtained by applying the @style stylesheet
1502 * to an open file descriptor
1503 * This does not close the descriptor.
1505 * Returns the number of bytes written or -1 in case of failure.
1508 xsltSaveResultToFd(int fd, xmlDocPtr result, xsltStylesheetPtr style) {
1509 xmlOutputBufferPtr buf;
1510 const xmlChar *encoding;
1513 if ((fd < 0) || (result == NULL) || (style == NULL))
1515 if (result->children == NULL)
1518 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1519 if (encoding != NULL) {
1520 xmlCharEncodingHandlerPtr encoder;
1522 encoder = xmlFindCharEncodingHandler((char *)encoding);
1523 if ((encoder != NULL) &&
1524 (xmlStrEqual((const xmlChar *)encoder->name,
1525 (const xmlChar *) "UTF-8")))
1527 buf = xmlOutputBufferCreateFd(fd, encoder);
1529 buf = xmlOutputBufferCreateFd(fd, NULL);
1533 xsltSaveResultTo(buf, result, style);
1534 ret = xmlOutputBufferClose(buf);
1539 * xsltSaveResultToString:
1540 * @doc_txt_ptr: Memory pointer for allocated XML text
1541 * @doc_txt_len: Length of the generated XML text
1542 * @result: the result xmlDocPtr
1543 * @style: the stylesheet
1545 * Save the result @result obtained by applying the @style stylesheet
1548 * Returns the number of byte written or -1 in case of failure.
1551 xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len,
1552 xmlDocPtr result, xsltStylesheetPtr style) {
1553 xmlOutputBufferPtr buf;
1554 const xmlChar *encoding;
1556 *doc_txt_ptr = NULL;
1558 if (result->children == NULL)
1561 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1562 if (encoding != NULL) {
1563 xmlCharEncodingHandlerPtr encoder;
1565 encoder = xmlFindCharEncodingHandler((char *)encoding);
1566 if ((encoder != NULL) &&
1567 (xmlStrEqual((const xmlChar *)encoder->name,
1568 (const xmlChar *) "UTF-8")))
1570 buf = xmlAllocOutputBuffer(encoder);
1572 buf = xmlAllocOutputBuffer(NULL);
1576 xsltSaveResultTo(buf, result, style);
1577 if (buf->conv != NULL) {
1578 *doc_txt_len = buf->conv->use;
1579 *doc_txt_ptr = xmlStrndup(buf->conv->content, *doc_txt_len);
1581 *doc_txt_len = buf->buffer->use;
1582 *doc_txt_ptr = xmlStrndup(buf->buffer->content, *doc_txt_len);
1584 (void)xmlOutputBufferClose(buf);
1588 /************************************************************************
1590 * Generating profiling informations *
1592 ************************************************************************/
1594 static long calibration = -1;
1597 * xsltCalibrateTimestamps:
1599 * Used for to calibrate the xsltTimestamp() function
1600 * Should work if launched at startup and we don't loose our quantum :-)
1602 * Returns the number of milliseconds used by xsltTimestamp()
1605 xsltCalibrateTimestamps(void) {
1608 for (i = 0;i < 999;i++)
1610 return(xsltTimestamp() / 1000);
1614 * xsltCalibrateAdjust:
1615 * @delta: a negative dealy value found
1617 * Used for to correct the calibration for xsltTimestamp()
1620 xsltCalibrateAdjust(long delta) {
1621 calibration += delta;
1627 * Used for gathering profiling data
1629 * Returns the number of tenth of milliseconds since the beginning of the
1635 #ifdef XSLT_WIN32_PERFORMANCE_COUNTER
1637 LARGE_INTEGER performanceCount;
1638 LARGE_INTEGER performanceFrequency;
1641 static LONGLONG startupQuadCount = 0;
1642 static LONGLONG startupQuadFreq = 0;
1644 ok = QueryPerformanceCounter(&performanceCount);
1647 quadCount = performanceCount.QuadPart;
1648 if (calibration < 0) {
1650 ok = QueryPerformanceFrequency(&performanceFrequency);
1653 startupQuadFreq = performanceFrequency.QuadPart;
1654 startupQuadCount = quadCount;
1657 if (startupQuadFreq == 0)
1659 seconds = (quadCount - startupQuadCount) / (double) startupQuadFreq;
1660 return (long) (seconds * XSLT_TIMESTAMP_TICS_PER_SEC);
1662 #else /* XSLT_WIN32_PERFORMANCE_COUNTER */
1663 #ifdef HAVE_GETTIMEOFDAY
1664 static struct timeval startup;
1668 if (calibration < 0) {
1669 gettimeofday(&startup, NULL);
1671 calibration = xsltCalibrateTimestamps();
1672 gettimeofday(&startup, NULL);
1676 gettimeofday(&cur, NULL);
1677 tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
1678 tics += (cur.tv_usec - startup.tv_usec) /
1679 (1000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
1681 tics -= calibration;
1685 /* Neither gettimeofday() nor Win32 performance counter available */
1689 #endif /* HAVE_GETTIMEOFDAY */
1690 #endif /* XSLT_WIN32_PERFORMANCE_COUNTER */
1693 #define MAX_TEMPLATES 10000
1696 * xsltSaveProfiling:
1697 * @ctxt: an XSLT context
1698 * @output: a FILE * for saving the informations
1700 * Save the profiling informations on @output
1703 xsltSaveProfiling(xsltTransformContextPtr ctxt, FILE *output) {
1708 xsltTemplatePtr *templates;
1709 xsltStylesheetPtr style;
1710 xsltTemplatePtr template;
1712 if ((output == NULL) || (ctxt == NULL))
1714 if (ctxt->profile == 0)
1718 max = MAX_TEMPLATES;
1719 templates = xmlMalloc(max * sizeof(xsltTemplatePtr));
1720 if (templates == NULL)
1723 style = ctxt->style;
1724 while (style != NULL) {
1725 template = style->templates;
1726 while (template != NULL) {
1730 if (template->nbCalls > 0)
1731 templates[nb++] = template;
1732 template = template->next;
1735 style = xsltNextImport(style);
1738 for (i = 0;i < nb -1;i++) {
1739 for (j = i + 1; j < nb; j++) {
1740 if ((templates[i]->time <= templates[j]->time) ||
1741 ((templates[i]->time == templates[j]->time) &&
1742 (templates[i]->nbCalls <= templates[j]->nbCalls))) {
1743 template = templates[j];
1744 templates[j] = templates[i];
1745 templates[i] = template;
1750 fprintf(output, "%6s%20s%20s%10s Calls Tot 100us Avg\n\n",
1751 "number", "match", "name", "mode");
1754 for (i = 0;i < nb;i++) {
1755 fprintf(output, "%5d ", i);
1756 if (templates[i]->match != NULL) {
1757 if (xmlStrlen(templates[i]->match) > 20)
1758 fprintf(output, "%s\n%26s", templates[i]->match, "");
1760 fprintf(output, "%20s", templates[i]->match);
1762 fprintf(output, "%20s", "");
1764 if (templates[i]->name != NULL) {
1765 if (xmlStrlen(templates[i]->name) > 20)
1766 fprintf(output, "%s\n%46s", templates[i]->name, "");
1768 fprintf(output, "%20s", templates[i]->name);
1770 fprintf(output, "%20s", "");
1772 if (templates[i]->mode != NULL) {
1773 if (xmlStrlen(templates[i]->mode) > 10)
1774 fprintf(output, "%s\n%56s", templates[i]->mode, "");
1776 fprintf(output, "%10s", templates[i]->mode);
1778 fprintf(output, "%10s", "");
1780 fprintf(output, " %6d", templates[i]->nbCalls);
1781 fprintf(output, " %6ld %6ld\n", templates[i]->time,
1782 templates[i]->time / templates[i]->nbCalls);
1783 total += templates[i]->nbCalls;
1784 totalt += templates[i]->time;
1786 fprintf(output, "\n%30s%26s %6d %6ld\n", "Total", "", total, totalt);
1791 /************************************************************************
1793 * Fetching profiling informations *
1795 ************************************************************************/
1798 * xsltGetProfileInformation:
1799 * @ctxt: a transformation context
1801 * This function should be called after the transformation completed
1802 * to extract template processing profiling informations if availble.
1803 * The informations are returned as an XML document tree like
1804 * <?xml version="1.0"?>
1806 * <template rank="1" match="*" name=""
1807 * mode="" calls="6" time="48" average="8"/>
1808 * <template rank="2" match="item2|item3" name=""
1809 * mode="" calls="10" time="30" average="3"/>
1810 * <template rank="3" match="item1" name=""
1811 * mode="" calls="5" time="17" average="3"/>
1813 * The caller will need to free up the returned tree with xmlFreeDoc()
1815 * Returns the xmlDocPtr corresponding to the result or NULL if not available.
1819 xsltGetProfileInformation(xsltTransformContextPtr ctxt)
1821 xmlDocPtr ret = NULL;
1822 xmlNodePtr root, child;
1825 xsltStylesheetPtr style;
1826 xsltTemplatePtr *templates;
1827 xsltTemplatePtr templ;
1828 int nb = 0, max = 0, i, j;
1839 (xsltTemplatePtr *) xmlMalloc(max * sizeof(xsltTemplatePtr));
1840 if (templates == NULL)
1844 * collect all the templates in an array
1846 style = ctxt->style;
1847 while (style != NULL) {
1848 templ = style->templates;
1849 while (templ != NULL) {
1853 if (templ->nbCalls > 0)
1854 templates[nb++] = templ;
1855 templ = templ->next;
1858 style = (xsltStylesheetPtr) xsltNextImport(style);
1862 * Sort the array by time spent
1864 for (i = 0; i < nb - 1; i++) {
1865 for (j = i + 1; j < nb; j++) {
1866 if ((templates[i]->time <= templates[j]->time) ||
1867 ((templates[i]->time == templates[j]->time) &&
1868 (templates[i]->nbCalls <= templates[j]->nbCalls))) {
1869 templ = templates[j];
1870 templates[j] = templates[i];
1871 templates[i] = templ;
1877 * Generate a document corresponding to the results.
1879 ret = xmlNewDoc(BAD_CAST "1.0");
1880 root = xmlNewDocNode(ret, NULL, BAD_CAST "profile", NULL);
1881 xmlDocSetRootElement(ret, root);
1883 for (i = 0; i < nb; i++) {
1884 child = xmlNewChild(root, NULL, BAD_CAST "template", NULL);
1885 sprintf(buf, "%d", i + 1);
1886 xmlSetProp(child, BAD_CAST "rank", BAD_CAST buf);
1887 xmlSetProp(child, BAD_CAST "match", BAD_CAST templates[i]->match);
1888 xmlSetProp(child, BAD_CAST "name", BAD_CAST templates[i]->name);
1889 xmlSetProp(child, BAD_CAST "mode", BAD_CAST templates[i]->mode);
1891 sprintf(buf, "%d", templates[i]->nbCalls);
1892 xmlSetProp(child, BAD_CAST "calls", BAD_CAST buf);
1894 sprintf(buf, "%ld", templates[i]->time);
1895 xmlSetProp(child, BAD_CAST "time", BAD_CAST buf);
1897 sprintf(buf, "%ld", templates[i]->time / templates[i]->nbCalls);
1898 xmlSetProp(child, BAD_CAST "average", BAD_CAST buf);
1906 /************************************************************************
1908 * Hooks for libxml2 XPath *
1910 ************************************************************************/
1914 * @style: the stylesheet
1915 * @str: the XPath expression
1917 * Compile an XPath expression
1919 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
1920 * the caller has to free the object.
1923 xsltXPathCompile(xsltStylesheetPtr style, const xmlChar *str) {
1924 xmlXPathContextPtr xctxt;
1925 xmlXPathCompExprPtr ret;
1928 xctxt = xmlXPathNewContext(style->doc);
1930 xctxt = xmlXPathNewContext(NULL);
1932 xctxt->dict = style->dict;
1933 ret = xmlXPathCtxtCompile(xctxt, str);
1934 xmlXPathFreeContext(xctxt);
1936 * TODO: there is a lot of optimizations which should be possible
1937 * like variable slot precomputations, function precomputations, etc.
1943 /************************************************************************
1945 * Hooks for the debugger *
1947 ************************************************************************/
1950 * There is currently only 3 debugging callback defined
1951 * Debugger callbacks are disabled by default
1953 #define XSLT_CALLBACK_NUMBER 3
1955 typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks;
1956 typedef xsltDebuggerCallbacks *xsltDebuggerCallbacksPtr;
1957 struct _xsltDebuggerCallbacks {
1958 xsltHandleDebuggerCallback handler;
1959 xsltAddCallCallback add;
1960 xsltDropCallCallback drop;
1963 static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks = {
1972 * xsltSetDebuggerStatus:
1973 * @value : the value to be set
1975 * This function sets the value of xslDebugStatus.
1978 xsltSetDebuggerStatus(int value)
1980 xslDebugStatus = value;
1984 * xsltGetDebuggerStatus:
1986 * Get xslDebugStatus.
1988 * Returns the value of xslDebugStatus.
1991 xsltGetDebuggerStatus(void)
1993 return(xslDebugStatus);
1997 * xsltSetDebuggerCallbacks:
1998 * @no : number of callbacks
1999 * @block : the block of callbacks
2001 * This function allow to plug a debugger into the XSLT library
2002 * @block points to a block of memory containing the address of @no
2003 * callback routines.
2005 * Returns 0 in case of success and -1 in case of error
2008 xsltSetDebuggerCallbacks(int no, void *block)
2010 xsltDebuggerCallbacksPtr callbacks;
2012 if ((block == NULL) || (no != XSLT_CALLBACK_NUMBER))
2015 callbacks = (xsltDebuggerCallbacksPtr) block;
2016 xsltDebuggerCurrentCallbacks.handler = callbacks->handler;
2017 xsltDebuggerCurrentCallbacks.add = callbacks->add;
2018 xsltDebuggerCurrentCallbacks.drop = callbacks->drop;
2023 * xslHandleDebugger:
2024 * @cur : source node being executed
2025 * @node : data node being processed
2026 * @templ : temlate that applies to node
2027 * @ctxt : the xslt transform context
2029 * If either cur or node are a breakpoint, or xslDebugStatus in state
2030 * where debugging must occcur at this time then transfer control
2031 * to the xslDebugBreak function
2034 xslHandleDebugger(xmlNodePtr cur, xmlNodePtr node, xsltTemplatePtr templ,
2035 xsltTransformContextPtr ctxt)
2037 if (xsltDebuggerCurrentCallbacks.handler != NULL)
2038 xsltDebuggerCurrentCallbacks.handler(cur, node, templ, ctxt);
2043 * @templ : current template being applied
2044 * @source : the source node being processed
2046 * Add template "call" to call stack
2047 * Returns : 1 on sucess 0 otherwise an error may be printed if
2048 * WITH_XSLT_DEBUG_BREAKPOINTS is defined
2051 xslAddCall(xsltTemplatePtr templ, xmlNodePtr source)
2053 if (xsltDebuggerCurrentCallbacks.add != NULL)
2054 return(xsltDebuggerCurrentCallbacks.add(templ, source));
2061 * Drop the topmost item off the call stack
2066 if (xsltDebuggerCurrentCallbacks.drop != NULL)
2067 xsltDebuggerCurrentCallbacks.drop();