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 #ifdef HAVE_SYS_TIME_H
26 #include <libxml/xmlmemory.h>
27 #include <libxml/tree.h>
28 #include <libxml/HTMLtree.h>
29 #include <libxml/xmlerror.h>
30 #include <libxml/xmlIO.h>
31 #include "xsltutils.h"
32 #include "templates.h"
33 #include "xsltInternals.h"
35 #include "transform.h"
37 /* gettimeofday on Windows ??? */
38 #if defined(WIN32) && !defined(__CYGWIN__)
41 #pragma comment(lib, "ws2_32.lib")
42 #define gettimeofday(p1,p2)
43 #define HAVE_GETTIMEOFDAY
44 #define XSLT_WIN32_PERFORMANCE_COUNTER
48 /************************************************************************
50 * Convenience function *
52 ************************************************************************/
57 * @name: the attribute name
58 * @nameSpace: the URI of the namespace
60 * Similar to xmlGetNsProp() but with a slightly different semantic
62 * Search and get the value of an attribute associated to a node
63 * This attribute has to be anchored in the namespace specified,
64 * or has no namespace and the element is in that namespace.
66 * This does the entity substitution.
67 * This function looks in DTD attribute declaration for #FIXED or
68 * default declaration values unless DTD use has been turned off.
70 * Returns the attribute value or NULL if not found.
71 * It's up to the caller to free the memory.
74 xsltGetNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) {
82 prop = node->properties;
83 if (nameSpace == NULL)
84 return(xmlGetProp(node, name));
85 while (prop != NULL) {
88 * - same attribute names
89 * - and the attribute carrying that namespace
91 if ((xmlStrEqual(prop->name, name)) &&
92 (((prop->ns == NULL) && (node->ns != NULL) &&
93 (xmlStrEqual(node->ns->href, nameSpace))) ||
94 ((prop->ns != NULL) &&
95 (xmlStrEqual(prop->ns->href, nameSpace))))) {
98 ret = xmlNodeListGetString(node->doc, prop->children, 1);
99 if (ret == NULL) return(xmlStrdup((xmlChar *)""));
106 * Check if there is a default declaration in the internal
107 * or external subsets
111 if (doc->intSubset != NULL) {
112 xmlAttributePtr attrDecl;
114 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
115 if ((attrDecl == NULL) && (doc->extSubset != NULL))
116 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
118 if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) {
120 * The DTD declaration only allows a prefix search
122 ns = xmlSearchNs(doc, node, attrDecl->prefix);
123 if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace)))
124 return(xmlStrdup(attrDecl->defaultValue));
132 /************************************************************************
134 * Handling of XSLT stylesheets messages *
136 ************************************************************************/
140 * @ctxt: an XSLT processing context
141 * @node: The current node
142 * @inst: The node containing the message instruction
144 * Process and xsl:message construct
147 xsltMessage(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst) {
148 xmlChar *prop, *message;
151 if ((ctxt == NULL) || (inst == NULL))
154 prop = xsltGetNsProp(inst, (const xmlChar *)"terminate", XSLT_NAMESPACE);
156 if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
158 } else if (xmlStrEqual(prop, (const xmlChar *)"no")) {
161 xsltGenericError(xsltGenericErrorContext,
162 "xsl:message : terminate expecting 'yes' or 'no'\n");
166 message = xsltEvalTemplateString(ctxt, node, inst);
167 if (message != NULL) {
168 int len = xmlStrlen(message);
170 xsltGenericError(xsltGenericErrorContext, "%s",
171 (const char *)message);
172 if ((len > 0) && (message[len - 1] != '\n'))
173 xsltGenericError(xsltGenericErrorContext, "\n");
177 ctxt->state = XSLT_STATE_STOPPED;
180 /************************************************************************
182 * Handling of out of context errors *
184 ************************************************************************/
187 * xsltGenericErrorDefaultFunc:
188 * @ctx: an error context
189 * @msg: the message to display/transmit
190 * @...: extra parameters for the message display
192 * Default handler for out of context error messages.
195 xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
198 if (xsltGenericErrorContext == NULL)
199 xsltGenericErrorContext = (void *) stderr;
202 vfprintf((FILE *)xsltGenericErrorContext, msg, args);
206 xmlGenericErrorFunc xsltGenericError = xsltGenericErrorDefaultFunc;
207 void *xsltGenericErrorContext = NULL;
211 * xsltSetGenericErrorFunc:
212 * @ctx: the new error handling context
213 * @handler: the new handler function
215 * Function to reset the handler and the error context for out of
216 * context error messages.
217 * This simply means that @handler will be called for subsequent
218 * error messages while not parsing nor validating. And @ctx will
219 * be passed as first argument to @handler
220 * One can simply force messages to be emitted to another FILE * than
221 * stderr by setting @ctx to this file handle and @handler to NULL.
224 xsltSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) {
225 xsltGenericErrorContext = ctx;
227 xsltGenericError = handler;
229 xsltGenericError = xsltGenericErrorDefaultFunc;
233 * xsltGenericDebugDefaultFunc:
234 * @ctx: an error context
235 * @msg: the message to display/transmit
236 * @...: extra parameters for the message display
238 * Default handler for out of context error messages.
241 xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
244 if (xsltGenericDebugContext == NULL)
248 vfprintf((FILE *)xsltGenericDebugContext, msg, args);
252 xmlGenericErrorFunc xsltGenericDebug = xsltGenericDebugDefaultFunc;
253 void *xsltGenericDebugContext = NULL;
257 * xsltSetGenericDebugFunc:
258 * @ctx: the new error handling context
259 * @handler: the new handler function
261 * Function to reset the handler and the error context for out of
262 * context error messages.
263 * This simply means that @handler will be called for subsequent
264 * error messages while not parsing or validating. And @ctx will
265 * be passed as first argument to @handler
266 * One can simply force messages to be emitted to another FILE * than
267 * stderr by setting @ctx to this file handle and @handler to NULL.
270 xsltSetGenericDebugFunc(void *ctx, xmlGenericErrorFunc handler) {
271 xsltGenericDebugContext = ctx;
273 xsltGenericDebug = handler;
275 xsltGenericDebug = xsltGenericDebugDefaultFunc;
279 * xsltPrintErrorContext:
280 * @ctxt: the transformation context
281 * @style: the stylesheet
282 * @node: the current node being processed
284 * Display the context of an error.
287 xsltPrintErrorContext(xsltTransformContextPtr ctxt,
288 xsltStylesheetPtr style, xmlNodePtr node) {
290 const xmlChar *file = NULL;
291 const xmlChar *name = NULL;
292 const char *type = "error";
295 ctxt->state = XSLT_STATE_ERROR;
296 if ((node == NULL) && (ctxt != NULL))
300 if ((node->type == XML_DOCUMENT_NODE) ||
301 (node->type == XML_HTML_DOCUMENT_NODE)) {
302 xmlDocPtr doc = (xmlDocPtr) node;
307 * Try to find contextual informations to report
309 if (node->type == XML_ELEMENT_NODE) {
310 line = (int) node->content;
311 } else if ((node->prev != NULL) &&
312 (node->prev->type == XML_ELEMENT_NODE)) {
313 line = (int) node->prev->content;
314 } else if ((node->parent != NULL) &&
315 (node->parent->type == XML_ELEMENT_NODE)) {
316 line = (int) node->parent->content;
318 if ((node->doc != NULL) && (node->doc->URL != NULL))
319 file = node->doc->URL;
320 if (node->name != NULL)
326 type = "runtime error";
327 else if (style != NULL)
328 type = "compilation error";
330 if ((file != NULL) && (line != 0) && (name != NULL))
331 xsltGenericError(xsltGenericErrorContext,
332 "%s: file %s line %d element %s\n",
333 type, file, line, name);
334 else if ((file != NULL) && (name != NULL))
335 xsltGenericError(xsltGenericErrorContext,
336 "%s: file %s element %s\n",
338 else if ((file != NULL) && (line != 0))
339 xsltGenericError(xsltGenericErrorContext,
340 "%s: file %s line %d\n",
342 else if (file != NULL)
343 xsltGenericError(xsltGenericErrorContext,
346 else if (name != NULL)
347 xsltGenericError(xsltGenericErrorContext,
351 xsltGenericError(xsltGenericErrorContext,
356 /************************************************************************
360 ************************************************************************/
364 * @node: the node holding the QName
365 * @name: pointer to the initial QName value
367 * This function analyzes @name, if the name contains a prefix,
368 * the function seaches the associated namespace in scope for it.
369 * It will also replace @name value with the NCName, the old value being
371 * Errors in the prefix lookup are signalled by setting @name to NULL.
373 * NOTE: the namespace returned is a pointer to the place where it is
374 * defined and hence has the same lifespan as the document holding it.
376 * Returns the namespace URI if there is a prefix, or NULL if @name is
380 xsltGetQNameURI(xmlNodePtr node, xmlChar ** name)
389 if ((qname == NULL) || (*qname == 0))
392 xsltGenericError(xsltGenericErrorContext,
393 "QName: no element for namespace lookup %s\n",
400 /* nasty but valid */
405 * we are not trying to validate but just to cut, and yes it will
406 * work even if this is a set of UTF-8 encoded chars
408 while ((qname[len] != 0) && (qname[len] != ':'))
415 * handle xml: separately, this one is magical
417 if ((qname[0] == 'x') && (qname[1] == 'm') &&
418 (qname[2] == 'l') && (qname[3] == ':')) {
421 *name = xmlStrdup(&qname[4]);
423 return(XML_XML_NAMESPACE);
427 ns = xmlSearchNs(node->doc, node, qname);
429 xsltGenericError(xsltGenericErrorContext,
430 "%s:%s : no namespace bound to prefix %s\n",
431 qname, &qname[len + 1]);
436 *name = xmlStrdup(&qname[len + 1]);
441 /************************************************************************
445 ************************************************************************/
448 * xsltDocumentSortFunction:
449 * @list: the node set
451 * reorder the current node list @list accordingly to the document order
454 xsltDocumentSortFunction(xmlNodeSetPtr list) {
464 /* TODO: sort is really not optimized, does it needs to ? */
465 for (i = 0;i < len -1;i++) {
466 for (j = i + 1; j < len; j++) {
467 tst = xmlXPathCmpNodes(list->nodeTab[i], list->nodeTab[j]);
469 node = list->nodeTab[i];
470 list->nodeTab[i] = list->nodeTab[j];
471 list->nodeTab[j] = node;
478 * xsltComputeSortResult:
479 * @ctxt: a XSLT process context
480 * @sorts: array of sort nodes
482 * reorder the current node list accordingly to the set of sorting
483 * requirement provided by the array of nodes.
485 static xmlXPathObjectPtr *
486 xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) {
487 xmlXPathObjectPtr *results = NULL;
488 xmlNodeSetPtr list = NULL;
489 xmlXPathObjectPtr res;
492 xsltStylePreCompPtr comp;
495 int oldPos, oldSize ;
497 xmlNsPtr *oldNamespaces;
499 comp = sort->_private;
501 xsltGenericError(xsltGenericErrorContext,
502 "xsl:sort : compilation failed\n");
506 if ((comp->select == NULL) || (comp->comp == NULL))
509 list = ctxt->nodeList;
510 if ((list == NULL) || (list->nodeNr <= 1))
515 /* TODO: xsl:sort lang attribute */
516 /* TODO: xsl:sort case-order attribute */
519 results = xmlMalloc(len * sizeof(xmlXPathObjectPtr));
520 if (results == NULL) {
521 xsltGenericError(xsltGenericErrorContext,
522 "xsltComputeSortResult: memory allocation failure\n");
526 oldNode = ctxt->node;
527 oldInst = ctxt->inst;
528 oldPos = ctxt->xpathCtxt->proximityPosition;
529 oldSize = ctxt->xpathCtxt->contextSize;
530 oldNsNr = ctxt->xpathCtxt->nsNr;
531 oldNamespaces = ctxt->xpathCtxt->namespaces;
532 for (i = 0;i < len;i++) {
534 ctxt->xpathCtxt->contextSize = len;
535 ctxt->xpathCtxt->proximityPosition = i + 1;
536 ctxt->node = list->nodeTab[i];
537 ctxt->xpathCtxt->node = ctxt->node;
538 ctxt->xpathCtxt->namespaces = comp->nsList;
539 ctxt->xpathCtxt->nsNr = comp->nsNr;
540 res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
542 if (res->type != XPATH_STRING)
543 res = xmlXPathConvertString(res);
545 res = xmlXPathConvertNumber(res);
546 res->index = i; /* Save original pos for dupl resolv */
548 if (res->type == XPATH_NUMBER) {
551 #ifdef WITH_XSLT_DEBUG_PROCESS
552 xsltGenericDebug(xsltGenericDebugContext,
553 "xsltComputeSortResult: select didn't evaluate to a number\n");
558 if (res->type == XPATH_STRING) {
561 #ifdef WITH_XSLT_DEBUG_PROCESS
562 xsltGenericDebug(xsltGenericDebugContext,
563 "xsltComputeSortResult: select didn't evaluate to a string\n");
569 ctxt->state = XSLT_STATE_STOPPED;
573 ctxt->node = oldNode;
574 ctxt->inst = oldInst;
575 ctxt->xpathCtxt->contextSize = oldSize;
576 ctxt->xpathCtxt->proximityPosition = oldPos;
577 ctxt->xpathCtxt->nsNr = oldNsNr;
578 ctxt->xpathCtxt->namespaces = oldNamespaces;
584 * xsltDoSortFunction:
585 * @ctxt: a XSLT process context
586 * @sorts: array of sort nodes
587 * @nbsorts: the number of sorts in the array
589 * reorder the current node list accordingly to the set of sorting
590 * requirement provided by the arry of nodes.
593 xsltDoSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts,
595 xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT];
596 xmlXPathObjectPtr *results = NULL, *res;
597 xmlNodeSetPtr list = NULL;
598 int descending, number, desc, numb;
604 xmlXPathObjectPtr tmp;
605 xsltStylePreCompPtr comp;
607 if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) ||
608 (nbsorts >= XSLT_MAX_SORT))
610 if (sorts[0] == NULL)
612 comp = sorts[0]->_private;
616 list = ctxt->nodeList;
617 if ((list == NULL) || (list->nodeNr <= 1))
618 return; /* nothing to do */
622 resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]);
623 for (i = 1;i < XSLT_MAX_SORT;i++)
624 resultsTab[i] = NULL;
626 results = resultsTab[0];
628 descending = comp->descending;
629 number = comp->number;
633 /* Shell's sort of node-set */
634 for (incr = len / 2; incr > 0; incr /= 2) {
635 for (i = incr; i < len; i++) {
637 if (results[i] == NULL)
641 if (results[j] == NULL)
645 if (results[j]->floatval == results[j + incr]->floatval)
647 else if (results[j]->floatval >
648 results[j + incr]->floatval)
652 tst = xmlStrcmp(results[j]->stringval,
653 results[j + incr]->stringval);
660 * Okay we need to use multi level sorts
663 while (depth < nbsorts) {
664 if (sorts[depth] == NULL)
666 comp = sorts[depth]->_private;
669 desc = comp->descending;
673 * Compute the result of the next level for the
674 * full set, this might be optimized ... or not
676 if (resultsTab[depth] == NULL)
677 resultsTab[depth] = xsltComputeSortResult(ctxt,
679 res = resultsTab[depth];
686 if (res[j]->floatval == res[j + incr]->floatval)
688 else if (res[j]->floatval >
689 res[j + incr]->floatval)
693 tst = xmlStrcmp(res[j]->stringval,
694 res[j + incr]->stringval);
701 * if we still can't differenciate at this level
702 * try one level deeper.
710 tst = results[j]->index > results[j + incr]->index;
714 results[j] = results[j + incr];
715 results[j + incr] = tmp;
716 node = list->nodeTab[j];
717 list->nodeTab[j] = list->nodeTab[j + incr];
718 list->nodeTab[j + incr] = node;
720 while (depth < nbsorts) {
721 if (sorts[depth] == NULL)
723 if (resultsTab[depth] == NULL)
725 res = resultsTab[depth];
727 res[j] = res[j + incr];
738 for (j = 0; j < nbsorts; j++) {
739 if (resultsTab[j] != NULL) {
740 for (i = 0;i < len;i++)
741 xmlXPathFreeObject(resultsTab[j][i]);
742 xmlFree(resultsTab[j]);
747 /************************************************************************
751 ************************************************************************/
755 * @buf: an output buffer
756 * @result: the result xmlDocPtr
757 * @style: the stylesheet
759 * Save the result @result obtained by applying the @style stylesheet
760 * to an I/O output channel @buf
762 * Returns the number of byte written or -1 in case of failure.
765 xsltSaveResultTo(xmlOutputBufferPtr buf, xmlDocPtr result,
766 xsltStylesheetPtr style) {
767 const xmlChar *encoding;
770 const xmlChar *method;
773 if ((buf == NULL) || (result == NULL) || (style == NULL))
775 if ((result->children == NULL) ||
776 ((result->children->type == XML_DTD_NODE) &&
777 (result->children->next == NULL)))
780 if ((style->methodURI != NULL) &&
781 ((style->method == NULL) ||
782 (!xmlStrEqual(style->method, (const xmlChar *) "xhtml")))) {
783 xsltGenericError(xsltGenericErrorContext,
784 "xsltSaveResultTo : unknown ouput method\n");
790 XSLT_GET_IMPORT_PTR(method, style, method)
791 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
792 XSLT_GET_IMPORT_INT(indent, style, indent);
794 if ((method == NULL) && (result->type == XML_HTML_DOCUMENT_NODE))
795 method = (const xmlChar *) "html";
798 root = xmlDocGetRootElement(result);
802 if ((method != NULL) &&
803 (xmlStrEqual(method, (const xmlChar *) "html"))) {
804 if (encoding != NULL) {
805 htmlSetMetaEncoding(result, (const xmlChar *) encoding);
807 htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
811 htmlDocContentDumpFormatOutput(buf, result, (const char *) encoding,
813 xmlOutputBufferFlush(buf);
814 } else if ((method != NULL) &&
815 (xmlStrEqual(method, (const xmlChar *) "xhtml"))) {
816 if (encoding != NULL) {
817 htmlSetMetaEncoding(result, (const xmlChar *) encoding);
819 htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
821 htmlDocContentDumpOutput(buf, result, (const char *) encoding);
822 xmlOutputBufferFlush(buf);
823 } else if ((method != NULL) &&
824 (xmlStrEqual(method, (const xmlChar *) "text"))) {
827 cur = result->children;
828 while (cur != NULL) {
829 if (cur->type == XML_TEXT_NODE)
830 xmlOutputBufferWriteString(buf, (const char *) cur->content);
835 if (cur->children != NULL) {
836 if ((cur->children->type != XML_ENTITY_DECL) &&
837 (cur->children->type != XML_ENTITY_REF_NODE) &&
838 (cur->children->type != XML_ENTITY_NODE)) {
843 if (cur->next != NULL) {
852 if (cur == (xmlNodePtr) style->doc) {
856 if (cur->next != NULL) {
860 } while (cur != NULL);
862 xmlOutputBufferFlush(buf);
866 const xmlChar *version;
868 XSLT_GET_IMPORT_INT(omitXmlDecl, style, omitXmlDeclaration);
869 XSLT_GET_IMPORT_INT(standalone, style, standalone);
870 XSLT_GET_IMPORT_PTR(version, style, version)
872 if (omitXmlDecl != 1) {
873 xmlOutputBufferWriteString(buf, "<?xml version=");
874 if (result->version != NULL)
875 xmlBufferWriteQuotedString(buf->buffer, result->version);
877 xmlOutputBufferWriteString(buf, "\"1.0\"");
878 if (encoding == NULL) {
879 if (result->encoding != NULL)
880 encoding = result->encoding;
881 else if (result->charset != XML_CHAR_ENCODING_UTF8)
882 encoding = (const xmlChar *)
883 xmlGetCharEncodingName((xmlCharEncoding)
886 if (encoding != NULL) {
887 xmlOutputBufferWriteString(buf, " encoding=");
888 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
890 switch (standalone) {
892 xmlOutputBufferWriteString(buf, " standalone=\"no\"");
895 xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
900 xmlOutputBufferWriteString(buf, "?>\n");
902 if (result->children != NULL) {
903 xmlNodePtr child = result->children;
905 while (child != NULL) {
906 xmlNodeDumpOutput(buf, result, child, 0, (indent == 1),
907 (const char *) encoding);
908 if (child->type == XML_DTD_NODE)
909 xmlOutputBufferWriteString(buf, "\n");
912 xmlOutputBufferWriteString(buf, "\n");
914 xmlOutputBufferFlush(buf);
916 return(buf->written - base);
920 * xsltSaveResultToFilename:
921 * @URL: a filename or URL
922 * @result: the result xmlDocPtr
923 * @style: the stylesheet
924 * @compression: the compression factor (0 - 9 included)
926 * Save the result @result obtained by applying the @style stylesheet
929 * Returns the number of byte written or -1 in case of failure.
932 xsltSaveResultToFilename(const char *URL, xmlDocPtr result,
933 xsltStylesheetPtr style, int compression) {
934 xmlOutputBufferPtr buf;
935 const xmlChar *encoding;
938 if ((URL == NULL) || (result == NULL) || (style == NULL))
940 if (result->children == NULL)
943 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
944 if (encoding != NULL) {
945 xmlCharEncodingHandlerPtr encoder;
947 encoder = xmlFindCharEncodingHandler((char *)encoding);
948 if ((encoder != NULL) &&
949 (xmlStrEqual((const xmlChar *)encoder->name,
950 (const xmlChar *) "UTF-8")))
952 buf = xmlOutputBufferCreateFilename(URL, encoder, compression);
954 buf = xmlOutputBufferCreateFilename(URL, NULL, compression);
958 xsltSaveResultTo(buf, result, style);
959 ret = xmlOutputBufferClose(buf);
964 * xsltSaveResultToFile:
965 * @file: a FILE * I/O
966 * @result: the result xmlDocPtr
967 * @style: the stylesheet
969 * Save the result @result obtained by applying the @style stylesheet
970 * to an open FILE * I/O.
971 * This does not close the FILE @file
973 * Returns the number of bytes written or -1 in case of failure.
976 xsltSaveResultToFile(FILE *file, xmlDocPtr result, xsltStylesheetPtr style) {
977 xmlOutputBufferPtr buf;
978 const xmlChar *encoding;
981 if ((file == NULL) || (result == NULL) || (style == NULL))
983 if (result->children == NULL)
986 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
987 if (encoding != NULL) {
988 xmlCharEncodingHandlerPtr encoder;
990 encoder = xmlFindCharEncodingHandler((char *)encoding);
991 if ((encoder != NULL) &&
992 (xmlStrEqual((const xmlChar *)encoder->name,
993 (const xmlChar *) "UTF-8")))
995 buf = xmlOutputBufferCreateFile(file, encoder);
997 buf = xmlOutputBufferCreateFile(file, NULL);
1002 xsltSaveResultTo(buf, result, style);
1003 ret = xmlOutputBufferClose(buf);
1008 * xsltSaveResultToFd:
1009 * @fd: a file descriptor
1010 * @result: the result xmlDocPtr
1011 * @style: the stylesheet
1013 * Save the result @result obtained by applying the @style stylesheet
1014 * to an open file descriptor
1015 * This does not close the descriptor.
1017 * Returns the number of bytes written or -1 in case of failure.
1020 xsltSaveResultToFd(int fd, xmlDocPtr result, xsltStylesheetPtr style) {
1021 xmlOutputBufferPtr buf;
1022 const xmlChar *encoding;
1025 if ((fd < 0) || (result == NULL) || (style == NULL))
1027 if (result->children == NULL)
1030 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1031 if (encoding != NULL) {
1032 xmlCharEncodingHandlerPtr encoder;
1034 encoder = xmlFindCharEncodingHandler((char *)encoding);
1035 if ((encoder != NULL) &&
1036 (xmlStrEqual((const xmlChar *)encoder->name,
1037 (const xmlChar *) "UTF-8")))
1039 buf = xmlOutputBufferCreateFd(fd, encoder);
1041 buf = xmlOutputBufferCreateFd(fd, NULL);
1045 xsltSaveResultTo(buf, result, style);
1046 ret = xmlOutputBufferClose(buf);
1050 /************************************************************************
1052 * Generating profiling informations *
1054 ************************************************************************/
1056 static long calibration = -1;
1059 * xsltCalibrateTimestamps:
1061 * Used for to calibrate the xsltTimestamp() function
1062 * Should work if launched at startup and we don't loose our quantum :-)
1064 * Returns the number of milliseconds used by xsltTimestamp()
1067 xsltCalibrateTimestamps(void) {
1070 for (i = 0;i < 999;i++)
1072 return(xsltTimestamp() / 1000);
1076 * xsltCalibrateAdjust:
1077 * @delta: a negative dealy value found
1079 * Used for to correct the calibration for xsltTimestamp()
1082 xsltCalibrateAdjust(long delta) {
1083 calibration += delta;
1089 * Used for gathering profiling data
1091 * Returns the number of tenth of milliseconds since the beginning of the
1097 #ifdef XSLT_WIN32_PERFORMANCE_COUNTER
1099 LARGE_INTEGER performanceCount;
1100 LARGE_INTEGER performanceFrequency;
1103 static LONGLONG startupQuadCount = 0;
1104 static LONGLONG startupQuadFreq = 0;
1106 ok = QueryPerformanceCounter(&performanceCount);
1109 quadCount = performanceCount.QuadPart;
1110 if (calibration < 0) {
1112 ok = QueryPerformanceFrequency(&performanceFrequency);
1115 startupQuadFreq = performanceFrequency.QuadPart;
1116 startupQuadCount = quadCount;
1119 if (startupQuadFreq == 0)
1121 seconds = (quadCount - startupQuadCount) / (double) startupQuadFreq;
1122 return (long) (seconds * XSLT_TIMESTAMP_TICS_PER_SEC);
1124 #else /* XSLT_WIN32_PERFORMANCE_COUNTER */
1125 #ifdef HAVE_GETTIMEOFDAY
1126 static struct timeval startup;
1130 if (calibration < 0) {
1131 gettimeofday(&startup, NULL);
1133 calibration = xsltCalibrateTimestamps();
1134 gettimeofday(&startup, NULL);
1138 gettimeofday(&cur, NULL);
1139 tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
1140 tics += (cur.tv_usec - startup.tv_usec) /
1141 (1000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
1143 tics -= calibration;
1147 /* Neither gettimeofday() nor Win32 performance counter available */
1151 #endif /* HAVE_GETTIMEOFDAY */
1152 #endif /* XSLT_WIN32_PERFORMANCE_COUNTER */
1155 #define MAX_TEMPLATES 10000
1158 * xsltSaveProfiling:
1159 * @ctxt: an XSLT context
1160 * @output: a FILE * for saving the informations
1162 * Save the profiling informations on @output
1165 xsltSaveProfiling(xsltTransformContextPtr ctxt, FILE *output) {
1170 xsltTemplatePtr *templates;
1171 xsltStylesheetPtr style;
1172 xsltTemplatePtr template;
1174 if ((output == NULL) || (ctxt == NULL))
1176 if (ctxt->profile == 0)
1180 max = MAX_TEMPLATES;
1181 templates = xmlMalloc(max * sizeof(xsltTemplatePtr));
1182 if (templates == NULL)
1185 style = ctxt->style;
1186 while (style != NULL) {
1187 template = style->templates;
1188 while (template != NULL) {
1192 if (template->nbCalls > 0)
1193 templates[nb++] = template;
1194 template = template->next;
1197 style = xsltNextImport(style);
1200 for (i = 0;i < nb -1;i++) {
1201 for (j = i + 1; j < nb; j++) {
1202 if ((templates[i]->time <= templates[j]->time) ||
1203 ((templates[i]->time == templates[j]->time) &&
1204 (templates[i]->nbCalls <= templates[j]->nbCalls))) {
1205 template = templates[j];
1206 templates[j] = templates[i];
1207 templates[i] = template;
1212 fprintf(output, "%6s%20s%20s%10s Calls Tot 100us Avg\n\n",
1213 "number", "match", "name", "mode");
1216 for (i = 0;i < nb;i++) {
1217 fprintf(output, "%5d ", i);
1218 if (templates[i]->match != NULL) {
1219 if (xmlStrlen(templates[i]->match) > 20)
1220 fprintf(output, "%s\n%26s", templates[i]->match, "");
1222 fprintf(output, "%20s", templates[i]->match);
1224 fprintf(output, "%20s", "");
1226 if (templates[i]->name != NULL) {
1227 if (xmlStrlen(templates[i]->name) > 20)
1228 fprintf(output, "%s\n%46s", templates[i]->name, "");
1230 fprintf(output, "%20s", templates[i]->name);
1232 fprintf(output, "%20s", "");
1234 if (templates[i]->mode != NULL) {
1235 if (xmlStrlen(templates[i]->mode) > 10)
1236 fprintf(output, "%s\n%56s", templates[i]->mode, "");
1238 fprintf(output, "%10s", templates[i]->mode);
1240 fprintf(output, "%10s", "");
1242 fprintf(output, " %6d", templates[i]->nbCalls);
1243 fprintf(output, " %6ld %6ld\n", templates[i]->time,
1244 templates[i]->time / templates[i]->nbCalls);
1245 total += templates[i]->nbCalls;
1246 totalt += templates[i]->time;
1248 fprintf(output, "\n%30s%26s %6d %6ld\n", "Total", "", total, totalt);
1253 /************************************************************************
1255 * Hooks for the debugger *
1257 ************************************************************************/
1260 * There is currently only 3 debugging callback defined
1261 * Debugger callbacks are disabled by default
1263 #define XSLT_CALLBACK_NUMBER 3
1265 typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks;
1266 typedef xsltDebuggerCallbacks *xsltDebuggerCallbacksPtr;
1267 struct _xsltDebuggerCallbacks {
1268 xsltHandleDebuggerCallback handler;
1269 xsltAddCallCallback add;
1270 xsltDropCallCallback drop;
1273 static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks = {
1282 * xslSetDebuggerCallbacks:
1283 * @no : number of callbacks
1284 * @block : the block of callbacks
1286 * This function allow to plug a debugger into the XSLT library
1287 * @block points to a block of memory containing the address of @no
1288 * callback routines.
1290 * Returns 0 in case of success and -1 in case of error
1293 xsltSetDebuggerCallbacks(int no, void *block)
1295 xsltDebuggerCallbacksPtr callbacks;
1297 if ((block == NULL) || (no != XSLT_CALLBACK_NUMBER))
1300 callbacks = (xsltDebuggerCallbacksPtr) block;
1301 xsltDebuggerCurrentCallbacks.handler = callbacks->handler;
1302 xsltDebuggerCurrentCallbacks.add = callbacks->add;
1303 xsltDebuggerCurrentCallbacks.drop = callbacks->drop;
1308 * xslHandleDebugger:
1309 * @cur : source node being executed
1310 * @node : data node being processed
1311 * @templ : temlate that applies to node
1312 * @ctxt : the xslt transform context
1314 * If either cur or node are a breakpoint, or xslDebugStatus in state
1315 * where debugging must occcur at this time then transfer control
1316 * to the xslDebugBreak function
1319 xslHandleDebugger(xmlNodePtr cur, xmlNodePtr node, xsltTemplatePtr templ,
1320 xsltTransformContextPtr ctxt)
1322 if (xsltDebuggerCurrentCallbacks.handler != NULL)
1323 xsltDebuggerCurrentCallbacks.handler(cur, node, templ, ctxt);
1328 * @templ : current template being applied
1329 * @source : the source node being processed
1331 * Add template "call" to call stack
1332 * Returns : 1 on sucess 0 otherwise an error may be printed if
1333 * WITH_XSLT_DEBUG_BREAKPOINTS is defined
1336 xslAddCall(xsltTemplatePtr templ, xmlNodePtr source)
1338 if (xsltDebuggerCurrentCallbacks.add != NULL)
1339 return(xsltDebuggerCurrentCallbacks.add(templ, source));
1346 * Drop the topmost item off the call stack
1351 if (xsltDebuggerCurrentCallbacks.drop != NULL)
1352 xsltDebuggerCurrentCallbacks.drop();