xslt: Import upstream release 1.1.38.
[wine.git] / libs / xslt / libxslt / xsltutils.c
blob3705d28f16e986b21529248bff3a4be5ceda004a
1 /*
2 * xsltutils.c: Utilities for the XSL Transformation 1.0 engine
4 * Reference:
5 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 * See Copyright for the status of this software.
9 * daniel@veillard.com
12 #define IN_LIBXSLT
13 #include "libxslt.h"
15 #ifndef XSLT_NEED_TRIO
16 #include <stdio.h>
17 #else
18 #include <trio.h>
19 #endif
21 #include <string.h>
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <time.h>
26 #ifdef HAVE_SYS_TIME_H
27 #include <sys/time.h>
28 #endif
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
33 #include <libxml/xmlmemory.h>
34 #include <libxml/tree.h>
35 #include <libxml/HTMLtree.h>
36 #include <libxml/xmlerror.h>
37 #include <libxml/xmlIO.h>
38 #include "xsltutils.h"
39 #include "templates.h"
40 #include "xsltInternals.h"
41 #include "imports.h"
42 #include "transform.h"
44 #if defined(_WIN32)
45 #include <windows.h>
46 #define XSLT_WIN32_PERFORMANCE_COUNTER
47 #endif
49 /************************************************************************
50 * *
51 * Convenience function *
52 * *
53 ************************************************************************/
55 /**
56 * xsltGetCNsProp:
57 * @style: the stylesheet
58 * @node: the node
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.
75 const xmlChar *
76 xsltGetCNsProp(xsltStylesheetPtr style, xmlNodePtr node,
77 const xmlChar *name, const xmlChar *nameSpace) {
78 xmlAttrPtr prop;
79 xmlDocPtr doc;
80 xmlNsPtr ns;
81 xmlChar *tmp;
82 const xmlChar *ret;
84 if ((node == NULL) || (style == NULL) || (style->dict == NULL))
85 return(NULL);
87 if (nameSpace == NULL)
88 return xmlGetProp(node, name);
90 if (node->type == XML_NAMESPACE_DECL)
91 return(NULL);
92 if (node->type == XML_ELEMENT_NODE)
93 prop = node->properties;
94 else
95 prop = NULL;
96 while (prop != NULL) {
98 * One need to have
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);
109 if (tmp == NULL)
110 ret = xmlDictLookup(style->dict, BAD_CAST "", 0);
111 else {
112 ret = xmlDictLookup(style->dict, tmp, -1);
113 xmlFree(tmp);
115 return ret;
117 prop = prop->next;
119 tmp = NULL;
121 * Check if there is a default declaration in the internal
122 * or external subsets
124 doc = node->doc;
125 if (doc != NULL) {
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));
144 return(NULL);
147 * xsltGetNsProp:
148 * @node: the node
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.
165 xmlChar *
166 xsltGetNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) {
167 xmlAttrPtr prop;
168 xmlDocPtr doc;
169 xmlNsPtr ns;
171 if (node == NULL)
172 return(NULL);
174 if (nameSpace == NULL)
175 return xmlGetProp(node, name);
177 if (node->type == XML_NAMESPACE_DECL)
178 return(NULL);
179 if (node->type == XML_ELEMENT_NODE)
180 prop = node->properties;
181 else
182 prop = NULL;
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.
187 * Example:
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) {
194 * One need to have
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))))) {
203 xmlChar *ret;
205 ret = xmlNodeListGetString(node->doc, prop->children, 1);
206 if (ret == NULL) return(xmlStrdup((xmlChar *)""));
207 return(ret);
209 prop = prop->next;
213 * Check if there is a default declaration in the internal
214 * or external subsets
216 doc = node->doc;
217 if (doc != NULL) {
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));
235 return(NULL);
239 * xsltGetUTF8Char:
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) {
252 unsigned int c;
254 if (utf == NULL)
255 goto error;
256 if (len == NULL)
257 goto error;
258 if (*len < 1)
259 goto error;
261 c = utf[0];
262 if (c & 0x80) {
263 if (*len < 2)
264 goto error;
265 if ((utf[1] & 0xc0) != 0x80)
266 goto error;
267 if ((c & 0xe0) == 0xe0) {
268 if (*len < 3)
269 goto error;
270 if ((utf[2] & 0xc0) != 0x80)
271 goto error;
272 if ((c & 0xf0) == 0xf0) {
273 if (*len < 4)
274 goto error;
275 if ((c & 0xf8) != 0xf0 || (utf[3] & 0xc0) != 0x80)
276 goto error;
277 *len = 4;
278 /* 4-byte code */
279 c = (utf[0] & 0x7) << 18;
280 c |= (utf[1] & 0x3f) << 12;
281 c |= (utf[2] & 0x3f) << 6;
282 c |= utf[3] & 0x3f;
283 } else {
284 /* 3-byte code */
285 *len = 3;
286 c = (utf[0] & 0xf) << 12;
287 c |= (utf[1] & 0x3f) << 6;
288 c |= utf[2] & 0x3f;
290 } else {
291 /* 2-byte code */
292 *len = 2;
293 c = (utf[0] & 0x1f) << 6;
294 c |= utf[1] & 0x3f;
296 } else {
297 /* 1-byte code */
298 *len = 1;
300 return(c);
302 error:
303 if (len != NULL)
304 *len = 0;
305 return(-1);
309 * xsltGetUTF8CharZ:
310 * @utf: a sequence of UTF-8 encoded bytes
311 * @len: a pointer to @bytes len
313 * Read one UTF8 Char from a null-terminated string.
315 * Returns the char value or -1 in case of error and update @len with the
316 * number of bytes used
319 xsltGetUTF8CharZ(const unsigned char *utf, int *len) {
320 unsigned int c;
322 if (utf == NULL)
323 goto error;
324 if (len == NULL)
325 goto error;
327 c = utf[0];
328 if (c & 0x80) {
329 if ((utf[1] & 0xc0) != 0x80)
330 goto error;
331 if ((c & 0xe0) == 0xe0) {
332 if ((utf[2] & 0xc0) != 0x80)
333 goto error;
334 if ((c & 0xf0) == 0xf0) {
335 if ((c & 0xf8) != 0xf0 || (utf[3] & 0xc0) != 0x80)
336 goto error;
337 *len = 4;
338 /* 4-byte code */
339 c = (utf[0] & 0x7) << 18;
340 c |= (utf[1] & 0x3f) << 12;
341 c |= (utf[2] & 0x3f) << 6;
342 c |= utf[3] & 0x3f;
343 } else {
344 /* 3-byte code */
345 *len = 3;
346 c = (utf[0] & 0xf) << 12;
347 c |= (utf[1] & 0x3f) << 6;
348 c |= utf[2] & 0x3f;
350 } else {
351 /* 2-byte code */
352 *len = 2;
353 c = (utf[0] & 0x1f) << 6;
354 c |= utf[1] & 0x3f;
356 } else {
357 /* 1-byte code */
358 *len = 1;
360 return(c);
362 error:
363 if (len != NULL)
364 *len = 0;
365 return(-1);
368 #ifdef XSLT_REFACTORED
371 * xsltPointerListAddSize:
372 * @list: the pointer list structure
373 * @item: the item to be stored
374 * @initialSize: the initial size of the list
376 * Adds an item to the list.
378 * Returns the position of the added item in the list or
379 * -1 in case of an error.
382 xsltPointerListAddSize(xsltPointerListPtr list,
383 void *item,
384 int initialSize)
386 if (list->items == NULL) {
387 if (initialSize <= 0)
388 initialSize = 1;
389 list->items = (void **) xmlMalloc(
390 initialSize * sizeof(void *));
391 if (list->items == NULL) {
392 xsltGenericError(xsltGenericErrorContext,
393 "xsltPointerListAddSize: memory allocation failure.\n");
394 return(-1);
396 list->number = 0;
397 list->size = initialSize;
398 } else if (list->size <= list->number) {
399 list->size *= 2;
400 list->items = (void **) xmlRealloc(list->items,
401 list->size * sizeof(void *));
402 if (list->items == NULL) {
403 xsltGenericError(xsltGenericErrorContext,
404 "xsltPointerListAddSize: memory re-allocation failure.\n");
405 list->size = 0;
406 return(-1);
409 list->items[list->number++] = item;
410 return(0);
414 * xsltPointerListCreate:
415 * @initialSize: the initial size for the list
417 * Creates an xsltPointerList structure.
419 * Returns a xsltPointerList structure or NULL in case of an error.
421 xsltPointerListPtr
422 xsltPointerListCreate(int initialSize)
424 xsltPointerListPtr ret;
426 ret = xmlMalloc(sizeof(xsltPointerList));
427 if (ret == NULL) {
428 xsltGenericError(xsltGenericErrorContext,
429 "xsltPointerListCreate: memory allocation failure.\n");
430 return (NULL);
432 memset(ret, 0, sizeof(xsltPointerList));
433 if (initialSize > 0) {
434 xsltPointerListAddSize(ret, NULL, initialSize);
435 ret->number = 0;
437 return (ret);
441 * xsltPointerListFree:
442 * @list: pointer to the list to be freed
444 * Frees the xsltPointerList structure. This does not free
445 * the content of the list.
447 void
448 xsltPointerListFree(xsltPointerListPtr list)
450 if (list == NULL)
451 return;
452 if (list->items != NULL)
453 xmlFree(list->items);
454 xmlFree(list);
458 * xsltPointerListClear:
459 * @list: pointer to the list to be cleared
461 * Resets the list, but does not free the allocated array
462 * and does not free the content of the list.
464 void
465 xsltPointerListClear(xsltPointerListPtr list)
467 if (list->items != NULL) {
468 xmlFree(list->items);
469 list->items = NULL;
471 list->number = 0;
472 list->size = 0;
475 #endif /* XSLT_REFACTORED */
477 /************************************************************************
479 * Handling of XSLT stylesheets messages *
481 ************************************************************************/
484 * xsltMessage:
485 * @ctxt: an XSLT processing context
486 * @node: The current node
487 * @inst: The node containing the message instruction
489 * Process and xsl:message construct
491 void
492 xsltMessage(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst) {
493 xmlGenericErrorFunc error = xsltGenericError;
494 void *errctx = xsltGenericErrorContext;
495 xmlChar *prop, *message;
496 int terminate = 0;
498 if ((ctxt == NULL) || (inst == NULL))
499 return;
501 if (ctxt->error != NULL) {
502 error = ctxt->error;
503 errctx = ctxt->errctx;
506 prop = xmlGetNsProp(inst, (const xmlChar *)"terminate", NULL);
507 if (prop != NULL) {
508 if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
509 terminate = 1;
510 } else if (xmlStrEqual(prop, (const xmlChar *)"no")) {
511 terminate = 0;
512 } else {
513 xsltTransformError(ctxt, NULL, inst,
514 "xsl:message : terminate expecting 'yes' or 'no'\n");
516 xmlFree(prop);
518 message = xsltEvalTemplateString(ctxt, node, inst);
519 if (message != NULL) {
520 int len = xmlStrlen(message);
522 error(errctx, "%s", (const char *)message);
523 if ((len > 0) && (message[len - 1] != '\n'))
524 error(errctx, "\n");
525 xmlFree(message);
527 if (terminate)
528 ctxt->state = XSLT_STATE_STOPPED;
531 /************************************************************************
533 * Handling of out of context errors *
535 ************************************************************************/
537 #define XSLT_GET_VAR_STR(msg, str) { \
538 int size; \
539 int chars; \
540 char *larger; \
541 va_list ap; \
543 str = (char *) xmlMalloc(150); \
544 if (str == NULL) \
545 return; \
547 size = 150; \
549 while (size < 64000) { \
550 va_start(ap, msg); \
551 chars = vsnprintf(str, size, msg, ap); \
552 va_end(ap); \
553 if ((chars > -1) && (chars < size)) \
554 break; \
555 if (chars > -1) \
556 size += chars + 1; \
557 else \
558 size += 100; \
559 if ((larger = (char *) xmlRealloc(str, size)) == NULL) {\
560 xmlFree(str); \
561 return; \
563 str = larger; \
567 * xsltGenericErrorDefaultFunc:
568 * @ctx: an error context
569 * @msg: the message to display/transmit
570 * @...: extra parameters for the message display
572 * Default handler for out of context error messages.
574 static void LIBXSLT_ATTR_FORMAT(2,3)
575 xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
576 va_list args;
578 if (xsltGenericErrorContext == NULL)
579 xsltGenericErrorContext = (void *) stderr;
581 va_start(args, msg);
582 vfprintf((FILE *)xsltGenericErrorContext, msg, args);
583 va_end(args);
586 xmlGenericErrorFunc xsltGenericError = xsltGenericErrorDefaultFunc;
587 void *xsltGenericErrorContext = NULL;
591 * xsltSetGenericErrorFunc:
592 * @ctx: the new error handling context
593 * @handler: the new handler function
595 * Function to reset the handler and the error context for out of
596 * context error messages.
597 * This simply means that @handler will be called for subsequent
598 * error messages while not parsing nor validating. And @ctx will
599 * be passed as first argument to @handler
600 * One can simply force messages to be emitted to another FILE * than
601 * stderr by setting @ctx to this file handle and @handler to NULL.
603 void
604 xsltSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) {
605 xsltGenericErrorContext = ctx;
606 if (handler != NULL)
607 xsltGenericError = handler;
608 else
609 xsltGenericError = xsltGenericErrorDefaultFunc;
613 * xsltGenericDebugDefaultFunc:
614 * @ctx: an error context
615 * @msg: the message to display/transmit
616 * @...: extra parameters for the message display
618 * Default handler for out of context error messages.
620 static void LIBXSLT_ATTR_FORMAT(2,3)
621 xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
622 va_list args;
624 if (xsltGenericDebugContext == NULL)
625 return;
627 va_start(args, msg);
628 vfprintf((FILE *)xsltGenericDebugContext, msg, args);
629 va_end(args);
632 xmlGenericErrorFunc xsltGenericDebug = xsltGenericDebugDefaultFunc;
633 void *xsltGenericDebugContext = NULL;
637 * xsltSetGenericDebugFunc:
638 * @ctx: the new error handling context
639 * @handler: the new handler function
641 * Function to reset the handler and the error context for out of
642 * context error messages.
643 * This simply means that @handler will be called for subsequent
644 * error messages while not parsing or validating. And @ctx will
645 * be passed as first argument to @handler
646 * One can simply force messages to be emitted to another FILE * than
647 * stderr by setting @ctx to this file handle and @handler to NULL.
649 void
650 xsltSetGenericDebugFunc(void *ctx, xmlGenericErrorFunc handler) {
651 xsltGenericDebugContext = ctx;
652 if (handler != NULL)
653 xsltGenericDebug = handler;
654 else
655 xsltGenericDebug = xsltGenericDebugDefaultFunc;
659 * xsltPrintErrorContext:
660 * @ctxt: the transformation context
661 * @style: the stylesheet
662 * @node: the current node being processed
664 * Display the context of an error.
666 void
667 xsltPrintErrorContext(xsltTransformContextPtr ctxt,
668 xsltStylesheetPtr style, xmlNodePtr node) {
669 int line = 0;
670 const xmlChar *file = NULL;
671 const xmlChar *name = NULL;
672 const char *type = "error";
673 xmlGenericErrorFunc error = xsltGenericError;
674 void *errctx = xsltGenericErrorContext;
676 if (ctxt != NULL) {
677 if (ctxt->state == XSLT_STATE_OK)
678 ctxt->state = XSLT_STATE_ERROR;
679 if (ctxt->error != NULL) {
680 error = ctxt->error;
681 errctx = ctxt->errctx;
684 if ((node == NULL) && (ctxt != NULL))
685 node = ctxt->inst;
687 if (node != NULL) {
688 if ((node->type == XML_DOCUMENT_NODE) ||
689 (node->type == XML_HTML_DOCUMENT_NODE)) {
690 xmlDocPtr doc = (xmlDocPtr) node;
692 file = doc->URL;
693 } else {
694 line = xmlGetLineNo(node);
695 if ((node->doc != NULL) && (node->doc->URL != NULL))
696 file = node->doc->URL;
697 if (node->name != NULL)
698 name = node->name;
702 if (ctxt != NULL)
703 type = "runtime error";
704 else if (style != NULL) {
705 #ifdef XSLT_REFACTORED
706 if (XSLT_CCTXT(style)->errSeverity == XSLT_ERROR_SEVERITY_WARNING)
707 type = "compilation warning";
708 else
709 type = "compilation error";
710 #else
711 type = "compilation error";
712 #endif
715 if ((file != NULL) && (line != 0) && (name != NULL))
716 error(errctx, "%s: file %s line %d element %s\n",
717 type, file, line, name);
718 else if ((file != NULL) && (name != NULL))
719 error(errctx, "%s: file %s element %s\n", type, file, name);
720 else if ((file != NULL) && (line != 0))
721 error(errctx, "%s: file %s line %d\n", type, file, line);
722 else if (file != NULL)
723 error(errctx, "%s: file %s\n", type, file);
724 else if (name != NULL)
725 error(errctx, "%s: element %s\n", type, name);
726 else
727 error(errctx, "%s\n", type);
731 * xsltSetTransformErrorFunc:
732 * @ctxt: the XSLT transformation context
733 * @ctx: the new error handling context
734 * @handler: the new handler function
736 * Function to reset the handler and the error context for out of
737 * context error messages specific to a given XSLT transromation.
739 * This simply means that @handler will be called for subsequent
740 * error messages while running the transformation.
742 void
743 xsltSetTransformErrorFunc(xsltTransformContextPtr ctxt,
744 void *ctx, xmlGenericErrorFunc handler)
746 ctxt->error = handler;
747 ctxt->errctx = ctx;
751 * xsltTransformError:
752 * @ctxt: an XSLT transformation context
753 * @style: the XSLT stylesheet used
754 * @node: the current node in the stylesheet
755 * @msg: the message to display/transmit
756 * @...: extra parameters for the message display
758 * Display and format an error messages, gives file, line, position and
759 * extra parameters, will use the specific transformation context if available
761 void
762 xsltTransformError(xsltTransformContextPtr ctxt,
763 xsltStylesheetPtr style,
764 xmlNodePtr node,
765 const char *msg, ...) {
766 xmlGenericErrorFunc error = xsltGenericError;
767 void *errctx = xsltGenericErrorContext;
768 char * str;
770 if (ctxt != NULL) {
771 if (ctxt->state == XSLT_STATE_OK)
772 ctxt->state = XSLT_STATE_ERROR;
773 if (ctxt->error != NULL) {
774 error = ctxt->error;
775 errctx = ctxt->errctx;
778 if ((node == NULL) && (ctxt != NULL))
779 node = ctxt->inst;
780 xsltPrintErrorContext(ctxt, style, node);
781 XSLT_GET_VAR_STR(msg, str);
782 error(errctx, "%s", str);
783 if (str != NULL)
784 xmlFree(str);
787 /************************************************************************
789 * QNames *
791 ************************************************************************/
794 * xsltSplitQName:
795 * @dict: a dictionary
796 * @name: the full QName
797 * @prefix: the return value
799 * Split QNames into prefix and local names, both allocated from a dictionary.
801 * Returns: the localname or NULL in case of error.
803 const xmlChar *
804 xsltSplitQName(xmlDictPtr dict, const xmlChar *name, const xmlChar **prefix) {
805 int len = 0;
806 const xmlChar *ret = NULL;
808 *prefix = NULL;
809 if ((name == NULL) || (dict == NULL)) return(NULL);
810 if (name[0] == ':')
811 return(xmlDictLookup(dict, name, -1));
812 while ((name[len] != 0) && (name[len] != ':')) len++;
813 if (name[len] == 0) return(xmlDictLookup(dict, name, -1));
814 *prefix = xmlDictLookup(dict, name, len);
815 ret = xmlDictLookup(dict, &name[len + 1], -1);
816 return(ret);
820 * xsltGetQNameURI:
821 * @node: the node holding the QName
822 * @name: pointer to the initial QName value
824 * This function analyzes @name, if the name contains a prefix,
825 * the function seaches the associated namespace in scope for it.
826 * It will also replace @name value with the NCName, the old value being
827 * freed.
828 * Errors in the prefix lookup are signalled by setting @name to NULL.
830 * NOTE: the namespace returned is a pointer to the place where it is
831 * defined and hence has the same lifespan as the document holding it.
833 * Returns the namespace URI if there is a prefix, or NULL if @name is
834 * not prefixed.
836 const xmlChar *
837 xsltGetQNameURI(xmlNodePtr node, xmlChar ** name)
839 int len = 0;
840 xmlChar *qname;
841 xmlNsPtr ns;
843 if (name == NULL)
844 return(NULL);
845 qname = *name;
846 if ((qname == NULL) || (*qname == 0))
847 return(NULL);
848 if (node == NULL) {
849 xsltGenericError(xsltGenericErrorContext,
850 "QName: no element for namespace lookup %s\n",
851 qname);
852 xmlFree(qname);
853 *name = NULL;
854 return(NULL);
857 /* nasty but valid */
858 if (qname[0] == ':')
859 return(NULL);
862 * we are not trying to validate but just to cut, and yes it will
863 * work even if this is a set of UTF-8 encoded chars
865 while ((qname[len] != 0) && (qname[len] != ':'))
866 len++;
868 if (qname[len] == 0)
869 return(NULL);
872 * handle xml: separately, this one is magical
874 if ((qname[0] == 'x') && (qname[1] == 'm') &&
875 (qname[2] == 'l') && (qname[3] == ':')) {
876 if (qname[4] == 0)
877 return(NULL);
878 *name = xmlStrdup(&qname[4]);
879 xmlFree(qname);
880 return(XML_XML_NAMESPACE);
883 qname[len] = 0;
884 ns = xmlSearchNs(node->doc, node, qname);
885 if (ns == NULL) {
886 xsltGenericError(xsltGenericErrorContext,
887 "%s:%s : no namespace bound to prefix %s\n",
888 qname, &qname[len + 1], qname);
889 *name = NULL;
890 xmlFree(qname);
891 return(NULL);
893 *name = xmlStrdup(&qname[len + 1]);
894 xmlFree(qname);
895 return(ns->href);
899 * xsltGetQNameURI2:
900 * @style: stylesheet pointer
901 * @node: the node holding the QName
902 * @name: pointer to the initial QName value
904 * This function is similar to xsltGetQNameURI, but is used when
905 * @name is a dictionary entry.
907 * Returns the namespace URI if there is a prefix, or NULL if @name is
908 * not prefixed.
910 const xmlChar *
911 xsltGetQNameURI2(xsltStylesheetPtr style, xmlNodePtr node,
912 const xmlChar **name) {
913 int len = 0;
914 xmlChar *qname;
915 xmlNsPtr ns;
917 if (name == NULL)
918 return(NULL);
919 qname = (xmlChar *)*name;
920 if ((qname == NULL) || (*qname == 0))
921 return(NULL);
922 if (node == NULL) {
923 xsltGenericError(xsltGenericErrorContext,
924 "QName: no element for namespace lookup %s\n",
925 qname);
926 *name = NULL;
927 return(NULL);
931 * we are not trying to validate but just to cut, and yes it will
932 * work even if this is a set of UTF-8 encoded chars
934 while ((qname[len] != 0) && (qname[len] != ':'))
935 len++;
937 if (qname[len] == 0)
938 return(NULL);
941 * handle xml: separately, this one is magical
943 if ((qname[0] == 'x') && (qname[1] == 'm') &&
944 (qname[2] == 'l') && (qname[3] == ':')) {
945 if (qname[4] == 0)
946 return(NULL);
947 *name = xmlDictLookup(style->dict, &qname[4], -1);
948 return(XML_XML_NAMESPACE);
951 qname = xmlStrndup(*name, len);
952 ns = xmlSearchNs(node->doc, node, qname);
953 if (ns == NULL) {
954 if (style) {
955 xsltTransformError(NULL, style, node,
956 "No namespace bound to prefix '%s'.\n",
957 qname);
958 style->errors++;
959 } else {
960 xsltGenericError(xsltGenericErrorContext,
961 "%s : no namespace bound to prefix %s\n",
962 *name, qname);
964 *name = NULL;
965 xmlFree(qname);
966 return(NULL);
968 *name = xmlDictLookup(style->dict, (*name)+len+1, -1);
969 xmlFree(qname);
970 return(ns->href);
973 /************************************************************************
975 * Sorting *
977 ************************************************************************/
980 * xsltDocumentSortFunction:
981 * @list: the node set
983 * reorder the current node list @list accordingly to the document order
984 * This function is slow, obsolete and should not be used anymore.
986 void
987 xsltDocumentSortFunction(xmlNodeSetPtr list) {
988 int i, j;
989 int len, tst;
990 xmlNodePtr node;
992 if (list == NULL)
993 return;
994 len = list->nodeNr;
995 if (len <= 1)
996 return;
997 /* TODO: sort is really not optimized, does it needs to ? */
998 for (i = 0;i < len -1;i++) {
999 for (j = i + 1; j < len; j++) {
1000 tst = xmlXPathCmpNodes(list->nodeTab[i], list->nodeTab[j]);
1001 if (tst == -1) {
1002 node = list->nodeTab[i];
1003 list->nodeTab[i] = list->nodeTab[j];
1004 list->nodeTab[j] = node;
1011 * xsltComputeSortResultInternal:
1012 * @ctxt: a XSLT process context
1013 * @sort: xsl:sort node
1014 * @number: data-type is number
1015 * @locale: transform strings according to locale
1017 * reorder the current node list accordingly to the set of sorting
1018 * requirement provided by the array of nodes.
1020 * Returns a ordered XPath nodeset or NULL in case of error.
1022 static xmlXPathObjectPtr *
1023 xsltComputeSortResultInternal(xsltTransformContextPtr ctxt, xmlNodePtr sort,
1024 int number, void *locale) {
1025 #ifdef XSLT_REFACTORED
1026 xsltStyleItemSortPtr comp;
1027 #else
1028 const xsltStylePreComp *comp;
1029 #endif
1030 xmlXPathObjectPtr *results = NULL;
1031 xmlNodeSetPtr list = NULL;
1032 xmlXPathObjectPtr res;
1033 int len = 0;
1034 int i;
1035 xmlNodePtr oldNode;
1036 xmlNodePtr oldInst;
1037 int oldPos, oldSize ;
1038 int oldNsNr;
1039 xmlNsPtr *oldNamespaces;
1041 comp = sort->psvi;
1042 if (comp == NULL) {
1043 xsltGenericError(xsltGenericErrorContext,
1044 "xsl:sort : compilation failed\n");
1045 return(NULL);
1048 if ((comp->select == NULL) || (comp->comp == NULL))
1049 return(NULL);
1051 list = ctxt->nodeList;
1052 if ((list == NULL) || (list->nodeNr <= 1))
1053 return(NULL);
1055 len = list->nodeNr;
1057 /* TODO: xsl:sort lang attribute */
1058 /* TODO: xsl:sort case-order attribute */
1061 results = xmlMalloc(len * sizeof(xmlXPathObjectPtr));
1062 if (results == NULL) {
1063 xsltGenericError(xsltGenericErrorContext,
1064 "xsltComputeSortResult: memory allocation failure\n");
1065 return(NULL);
1068 oldNode = ctxt->node;
1069 oldInst = ctxt->inst;
1070 oldPos = ctxt->xpathCtxt->proximityPosition;
1071 oldSize = ctxt->xpathCtxt->contextSize;
1072 oldNsNr = ctxt->xpathCtxt->nsNr;
1073 oldNamespaces = ctxt->xpathCtxt->namespaces;
1074 for (i = 0;i < len;i++) {
1075 ctxt->inst = sort;
1076 ctxt->xpathCtxt->contextSize = len;
1077 ctxt->xpathCtxt->proximityPosition = i + 1;
1078 ctxt->node = list->nodeTab[i];
1079 ctxt->xpathCtxt->node = ctxt->node;
1080 #ifdef XSLT_REFACTORED
1081 if (comp->inScopeNs != NULL) {
1082 ctxt->xpathCtxt->namespaces = comp->inScopeNs->list;
1083 ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber;
1084 } else {
1085 ctxt->xpathCtxt->namespaces = NULL;
1086 ctxt->xpathCtxt->nsNr = 0;
1088 #else
1089 ctxt->xpathCtxt->namespaces = comp->nsList;
1090 ctxt->xpathCtxt->nsNr = comp->nsNr;
1091 #endif
1092 res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
1093 if (res != NULL) {
1094 if (res->type != XPATH_STRING)
1095 res = xmlXPathConvertString(res);
1096 if (number)
1097 res = xmlXPathConvertNumber(res);
1099 if (res != NULL) {
1100 res->index = i; /* Save original pos for dupl resolv */
1101 if (number) {
1102 if (res->type == XPATH_NUMBER) {
1103 results[i] = res;
1104 } else {
1105 #ifdef WITH_XSLT_DEBUG_PROCESS
1106 xsltGenericDebug(xsltGenericDebugContext,
1107 "xsltComputeSortResult: select didn't evaluate to a number\n");
1108 #endif
1109 results[i] = NULL;
1111 } else {
1112 if (res->type == XPATH_STRING) {
1113 if (locale != NULL) {
1114 xmlChar *str = res->stringval;
1115 xmlChar *sortKey = ctxt->genSortKey(locale, str);
1117 if (sortKey == NULL) {
1118 xsltTransformError(ctxt, NULL, sort,
1119 "xsltComputeSortResult: sort key is null\n");
1120 } else {
1121 res->stringval = sortKey;
1122 xmlFree(str);
1126 results[i] = res;
1127 } else {
1128 #ifdef WITH_XSLT_DEBUG_PROCESS
1129 xsltGenericDebug(xsltGenericDebugContext,
1130 "xsltComputeSortResult: select didn't evaluate to a string\n");
1131 #endif
1132 results[i] = NULL;
1135 } else {
1136 ctxt->state = XSLT_STATE_STOPPED;
1137 results[i] = NULL;
1140 ctxt->node = oldNode;
1141 ctxt->inst = oldInst;
1142 ctxt->xpathCtxt->contextSize = oldSize;
1143 ctxt->xpathCtxt->proximityPosition = oldPos;
1144 ctxt->xpathCtxt->nsNr = oldNsNr;
1145 ctxt->xpathCtxt->namespaces = oldNamespaces;
1147 return(results);
1151 * xsltComputeSortResult:
1152 * @ctxt: a XSLT process context
1153 * @sort: node list
1155 * reorder the current node list accordingly to the set of sorting
1156 * requirement provided by the array of nodes.
1158 * Returns a ordered XPath nodeset or NULL in case of error.
1160 xmlXPathObjectPtr *
1161 xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) {
1162 const xsltStylePreComp *comp = sort->psvi;
1163 int number = 0;
1165 if (comp != NULL)
1166 number = comp->number;
1167 return xsltComputeSortResultInternal(ctxt, sort, number,
1168 /* locale */ NULL);
1172 * xsltDefaultSortFunction:
1173 * @ctxt: a XSLT process context
1174 * @sorts: array of sort nodes
1175 * @nbsorts: the number of sorts in the array
1177 * reorder the current node list accordingly to the set of sorting
1178 * requirement provided by the arry of nodes.
1180 void
1181 xsltDefaultSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts,
1182 int nbsorts) {
1183 #ifdef XSLT_REFACTORED
1184 xsltStyleItemSortPtr comp;
1185 #else
1186 const xsltStylePreComp *comp;
1187 #endif
1188 xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT];
1189 xmlXPathObjectPtr *results = NULL, *res;
1190 xmlNodeSetPtr list = NULL;
1191 int len = 0;
1192 int i, j, incr;
1193 int tst;
1194 int depth;
1195 xmlNodePtr node;
1196 xmlXPathObjectPtr tmp;
1197 int number[XSLT_MAX_SORT], desc[XSLT_MAX_SORT];
1198 void *locale[XSLT_MAX_SORT];
1200 if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) ||
1201 (nbsorts >= XSLT_MAX_SORT))
1202 return;
1203 if (sorts[0] == NULL)
1204 return;
1205 comp = sorts[0]->psvi;
1206 if (comp == NULL)
1207 return;
1209 list = ctxt->nodeList;
1210 if ((list == NULL) || (list->nodeNr <= 1))
1211 return; /* nothing to do */
1213 for (j = 0; j < nbsorts; j++) {
1214 xmlChar *lang;
1216 comp = sorts[j]->psvi;
1217 if ((comp->stype == NULL) && (comp->has_stype != 0)) {
1218 xmlChar *stype =
1219 xsltEvalAttrValueTemplate(ctxt, sorts[j],
1220 BAD_CAST "data-type", NULL);
1221 number[j] = 0;
1222 if (stype != NULL) {
1223 if (xmlStrEqual(stype, (const xmlChar *) "text"))
1225 else if (xmlStrEqual(stype, (const xmlChar *) "number"))
1226 number[j] = 1;
1227 else {
1228 xsltTransformError(ctxt, NULL, sorts[j],
1229 "xsltDoSortFunction: no support for data-type = %s\n",
1230 stype);
1232 xmlFree(stype);
1234 } else {
1235 number[j] = comp->number;
1237 if ((comp->order == NULL) && (comp->has_order != 0)) {
1238 xmlChar *order = xsltEvalAttrValueTemplate(ctxt, sorts[j],
1239 BAD_CAST "order", NULL);
1240 desc[j] = 0;
1241 if (order != NULL) {
1242 if (xmlStrEqual(order, (const xmlChar *) "ascending"))
1244 else if (xmlStrEqual(order, (const xmlChar *) "descending"))
1245 desc[j] = 1;
1246 else {
1247 xsltTransformError(ctxt, NULL, sorts[j],
1248 "xsltDoSortFunction: invalid value %s for order\n",
1249 order);
1251 xmlFree(order);
1253 } else {
1254 desc[j] = comp->descending;
1256 if ((comp->lang == NULL) && (comp->has_lang != 0)) {
1257 lang = xsltEvalAttrValueTemplate(ctxt, sorts[j],
1258 (xmlChar *) "lang",
1259 NULL);
1260 } else {
1261 lang = (xmlChar *) comp->lang;
1263 if (lang != NULL) {
1264 locale[j] = ctxt->newLocale(lang, comp->lower_first);
1265 if (lang != comp->lang)
1266 xmlFree(lang);
1267 } else {
1268 locale[j] = NULL;
1272 len = list->nodeNr;
1274 resultsTab[0] = xsltComputeSortResultInternal(ctxt, sorts[0], number[0],
1275 locale[0]);
1276 for (i = 1;i < XSLT_MAX_SORT;i++)
1277 resultsTab[i] = NULL;
1279 results = resultsTab[0];
1281 comp = sorts[0]->psvi;
1282 if (results == NULL)
1283 goto cleanup;
1285 /* Shell's sort of node-set */
1286 for (incr = len / 2; incr > 0; incr /= 2) {
1287 for (i = incr; i < len; i++) {
1288 j = i - incr;
1289 if (results[i] == NULL)
1290 continue;
1292 while (j >= 0) {
1293 if (results[j] == NULL)
1294 tst = 1;
1295 else {
1296 if (number[0]) {
1297 /* We make NaN smaller than number in accordance
1298 with XSLT spec */
1299 if (xmlXPathIsNaN(results[j]->floatval)) {
1300 if (xmlXPathIsNaN(results[j + incr]->floatval))
1301 tst = 0;
1302 else
1303 tst = -1;
1304 } else if (xmlXPathIsNaN(results[j + incr]->floatval))
1305 tst = 1;
1306 else if (results[j]->floatval ==
1307 results[j + incr]->floatval)
1308 tst = 0;
1309 else if (results[j]->floatval >
1310 results[j + incr]->floatval)
1311 tst = 1;
1312 else tst = -1;
1313 } else {
1314 tst = xmlStrcmp(results[j]->stringval,
1315 results[j + incr]->stringval);
1317 if (desc[0])
1318 tst = -tst;
1320 if (tst == 0) {
1322 * Okay we need to use multi level sorts
1324 depth = 1;
1325 while (depth < nbsorts) {
1326 if (sorts[depth] == NULL)
1327 break;
1328 comp = sorts[depth]->psvi;
1329 if (comp == NULL)
1330 break;
1333 * Compute the result of the next level for the
1334 * full set, this might be optimized ... or not
1336 if (resultsTab[depth] == NULL)
1337 resultsTab[depth] =
1338 xsltComputeSortResultInternal(ctxt,
1339 sorts[depth],
1340 number[depth],
1341 locale[depth]);
1342 res = resultsTab[depth];
1343 if (res == NULL)
1344 break;
1345 if (res[j] == NULL) {
1346 if (res[j+incr] != NULL)
1347 tst = 1;
1348 } else if (res[j+incr] == NULL) {
1349 tst = -1;
1350 } else {
1351 if (number[depth]) {
1352 /* We make NaN smaller than number in
1353 accordance with XSLT spec */
1354 if (xmlXPathIsNaN(res[j]->floatval)) {
1355 if (xmlXPathIsNaN(res[j +
1356 incr]->floatval))
1357 tst = 0;
1358 else
1359 tst = -1;
1360 } else if (xmlXPathIsNaN(res[j + incr]->
1361 floatval))
1362 tst = 1;
1363 else if (res[j]->floatval == res[j + incr]->
1364 floatval)
1365 tst = 0;
1366 else if (res[j]->floatval >
1367 res[j + incr]->floatval)
1368 tst = 1;
1369 else tst = -1;
1370 } else {
1371 tst = xmlStrcmp(res[j]->stringval,
1372 res[j + incr]->stringval);
1374 if (desc[depth])
1375 tst = -tst;
1379 * if we still can't differenciate at this level
1380 * try one level deeper.
1382 if (tst != 0)
1383 break;
1384 depth++;
1387 if (tst == 0) {
1388 tst = results[j]->index > results[j + incr]->index;
1390 if (tst > 0) {
1391 tmp = results[j];
1392 results[j] = results[j + incr];
1393 results[j + incr] = tmp;
1394 node = list->nodeTab[j];
1395 list->nodeTab[j] = list->nodeTab[j + incr];
1396 list->nodeTab[j + incr] = node;
1397 depth = 1;
1398 while (depth < nbsorts) {
1399 if (sorts[depth] == NULL)
1400 break;
1401 if (resultsTab[depth] == NULL)
1402 break;
1403 res = resultsTab[depth];
1404 tmp = res[j];
1405 res[j] = res[j + incr];
1406 res[j + incr] = tmp;
1407 depth++;
1409 j -= incr;
1410 } else
1411 break;
1416 cleanup:
1417 for (j = 0; j < nbsorts; j++) {
1418 if (locale[j] != NULL) {
1419 ctxt->freeLocale(locale[j]);
1421 if (resultsTab[j] != NULL) {
1422 for (i = 0;i < len;i++)
1423 xmlXPathFreeObject(resultsTab[j][i]);
1424 xmlFree(resultsTab[j]);
1430 static xsltSortFunc xsltSortFunction = xsltDefaultSortFunction;
1433 * xsltDoSortFunction:
1434 * @ctxt: a XSLT process context
1435 * @sorts: array of sort nodes
1436 * @nbsorts: the number of sorts in the array
1438 * reorder the current node list accordingly to the set of sorting
1439 * requirement provided by the arry of nodes.
1440 * This is a wrapper function, the actual function used is specified
1441 * using xsltSetCtxtSortFunc() to set the context specific sort function,
1442 * or xsltSetSortFunc() to set the global sort function.
1443 * If a sort function is set on the context, this will get called.
1444 * Otherwise the global sort function is called.
1446 void
1447 xsltDoSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr * sorts,
1448 int nbsorts)
1450 if (ctxt->sortfunc != NULL)
1451 (ctxt->sortfunc)(ctxt, sorts, nbsorts);
1452 else if (xsltSortFunction != NULL)
1453 xsltSortFunction(ctxt, sorts, nbsorts);
1457 * xsltSetSortFunc:
1458 * @handler: the new handler function
1460 * DEPRECATED: Use xsltSetCtxtLocaleHandlers.
1462 * Function to reset the global handler for XSLT sorting.
1463 * If the handler is NULL, the default sort function will be used.
1465 void
1466 xsltSetSortFunc(xsltSortFunc handler) {
1467 if (handler != NULL)
1468 xsltSortFunction = handler;
1469 else
1470 xsltSortFunction = xsltDefaultSortFunction;
1474 * xsltSetCtxtSortFunc:
1475 * @ctxt: a XSLT process context
1476 * @handler: the new handler function
1478 * DEPRECATED: Use xsltSetCtxtLocaleHandlers.
1480 * Function to set the handler for XSLT sorting
1481 * for the specified context.
1482 * If the handler is NULL, then the global
1483 * sort function will be called
1485 void
1486 xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt, xsltSortFunc handler) {
1487 ctxt->sortfunc = handler;
1491 * xsltSetCtxtLocaleHandlers:
1492 * @ctxt: an XSLT transform context
1493 * @newLocale: locale constructor
1494 * @freeLocale: locale destructor
1495 * @genSortKey: sort key generator
1497 * Set the locale handlers.
1499 void
1500 xsltSetCtxtLocaleHandlers(xsltTransformContextPtr ctxt,
1501 xsltNewLocaleFunc newLocale,
1502 xsltFreeLocaleFunc freeLocale,
1503 xsltGenSortKeyFunc genSortKey) {
1504 if (ctxt == NULL)
1505 return;
1507 ctxt->newLocale = newLocale;
1508 ctxt->freeLocale = freeLocale;
1509 ctxt->genSortKey = genSortKey;
1512 /************************************************************************
1514 * Parsing options *
1516 ************************************************************************/
1519 * xsltSetCtxtParseOptions:
1520 * @ctxt: a XSLT process context
1521 * @options: a combination of libxml2 xmlParserOption
1523 * Change the default parser option passed by the XSLT engine to the
1524 * parser when using document() loading.
1526 * Returns the previous options or -1 in case of error
1529 xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt, int options)
1531 int oldopts;
1533 if (ctxt == NULL)
1534 return(-1);
1535 oldopts = ctxt->parserOptions;
1536 if (ctxt->xinclude)
1537 oldopts |= XML_PARSE_XINCLUDE;
1538 ctxt->parserOptions = options;
1539 if (options & XML_PARSE_XINCLUDE)
1540 ctxt->xinclude = 1;
1541 else
1542 ctxt->xinclude = 0;
1543 return(oldopts);
1546 /************************************************************************
1548 * Output *
1550 ************************************************************************/
1553 * xsltSaveResultTo:
1554 * @buf: an output buffer
1555 * @result: the result xmlDocPtr
1556 * @style: the stylesheet
1558 * Save the result @result obtained by applying the @style stylesheet
1559 * to an I/O output channel @buf
1561 * Returns the number of byte written or -1 in case of failure.
1564 xsltSaveResultTo(xmlOutputBufferPtr buf, xmlDocPtr result,
1565 xsltStylesheetPtr style) {
1566 const xmlChar *encoding;
1567 int base;
1568 const xmlChar *method;
1569 int indent;
1571 if ((buf == NULL) || (result == NULL) || (style == NULL))
1572 return(-1);
1573 if ((result->children == NULL) ||
1574 ((result->children->type == XML_DTD_NODE) &&
1575 (result->children->next == NULL)))
1576 return(0);
1578 if ((style->methodURI != NULL) &&
1579 ((style->method == NULL) ||
1580 (!xmlStrEqual(style->method, (const xmlChar *) "xhtml")))) {
1581 xsltGenericError(xsltGenericErrorContext,
1582 "xsltSaveResultTo : unknown output method\n");
1583 return(-1);
1586 base = buf->written;
1588 XSLT_GET_IMPORT_PTR(method, style, method)
1589 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1590 XSLT_GET_IMPORT_INT(indent, style, indent);
1592 if ((method == NULL) && (result->type == XML_HTML_DOCUMENT_NODE))
1593 method = (const xmlChar *) "html";
1595 if ((method != NULL) &&
1596 (xmlStrEqual(method, (const xmlChar *) "html"))) {
1597 if (encoding != NULL) {
1598 htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1599 } else {
1600 htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
1602 if (indent == -1)
1603 indent = 1;
1604 htmlDocContentDumpFormatOutput(buf, result, (const char *) encoding,
1605 indent);
1606 xmlOutputBufferFlush(buf);
1607 } else if ((method != NULL) &&
1608 (xmlStrEqual(method, (const xmlChar *) "xhtml"))) {
1609 if (encoding != NULL) {
1610 htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1611 } else {
1612 htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
1614 htmlDocContentDumpOutput(buf, result, (const char *) encoding);
1615 xmlOutputBufferFlush(buf);
1616 } else if ((method != NULL) &&
1617 (xmlStrEqual(method, (const xmlChar *) "text"))) {
1618 xmlNodePtr cur;
1620 cur = result->children;
1621 while (cur != NULL) {
1622 if (cur->type == XML_TEXT_NODE)
1623 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1626 * Skip to next node
1628 if (cur->children != NULL) {
1629 if ((cur->children->type != XML_ENTITY_DECL) &&
1630 (cur->children->type != XML_ENTITY_REF_NODE) &&
1631 (cur->children->type != XML_ENTITY_NODE)) {
1632 cur = cur->children;
1633 continue;
1636 if (cur->next != NULL) {
1637 cur = cur->next;
1638 continue;
1641 do {
1642 cur = cur->parent;
1643 if (cur == NULL)
1644 break;
1645 if (cur == (xmlNodePtr) style->doc) {
1646 cur = NULL;
1647 break;
1649 if (cur->next != NULL) {
1650 cur = cur->next;
1651 break;
1653 } while (cur != NULL);
1655 xmlOutputBufferFlush(buf);
1656 } else {
1657 int omitXmlDecl;
1658 int standalone;
1660 XSLT_GET_IMPORT_INT(omitXmlDecl, style, omitXmlDeclaration);
1661 XSLT_GET_IMPORT_INT(standalone, style, standalone);
1663 if (omitXmlDecl != 1) {
1664 xmlOutputBufferWriteString(buf, "<?xml version=");
1665 if (result->version != NULL) {
1666 xmlOutputBufferWriteString(buf, "\"");
1667 xmlOutputBufferWriteString(buf, (const char *)result->version);
1668 xmlOutputBufferWriteString(buf, "\"");
1669 } else
1670 xmlOutputBufferWriteString(buf, "\"1.0\"");
1671 if (encoding == NULL) {
1672 if (result->encoding != NULL)
1673 encoding = result->encoding;
1674 else if (result->charset != XML_CHAR_ENCODING_UTF8)
1675 encoding = (const xmlChar *)
1676 xmlGetCharEncodingName((xmlCharEncoding)
1677 result->charset);
1679 if (encoding != NULL) {
1680 xmlOutputBufferWriteString(buf, " encoding=");
1681 xmlOutputBufferWriteString(buf, "\"");
1682 xmlOutputBufferWriteString(buf, (const char *) encoding);
1683 xmlOutputBufferWriteString(buf, "\"");
1685 switch (standalone) {
1686 case 0:
1687 xmlOutputBufferWriteString(buf, " standalone=\"no\"");
1688 break;
1689 case 1:
1690 xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
1691 break;
1692 default:
1693 break;
1695 xmlOutputBufferWriteString(buf, "?>\n");
1697 if (result->children != NULL) {
1698 xmlNodePtr children = result->children;
1699 xmlNodePtr child = children;
1702 * Hack to avoid quadratic behavior when scanning
1703 * result->children in xmlGetIntSubset called by
1704 * xmlNodeDumpOutput.
1706 result->children = NULL;
1708 while (child != NULL) {
1709 xmlNodeDumpOutput(buf, result, child, 0, (indent == 1),
1710 (const char *) encoding);
1711 if (indent && ((child->type == XML_DTD_NODE) ||
1712 ((child->type == XML_COMMENT_NODE) &&
1713 (child->next != NULL))))
1714 xmlOutputBufferWriteString(buf, "\n");
1715 child = child->next;
1717 if (indent)
1718 xmlOutputBufferWriteString(buf, "\n");
1720 result->children = children;
1722 xmlOutputBufferFlush(buf);
1724 return(buf->written - base);
1728 * xsltSaveResultToFilename:
1729 * @URL: a filename or URL
1730 * @result: the result xmlDocPtr
1731 * @style: the stylesheet
1732 * @compression: the compression factor (0 - 9 included)
1734 * Save the result @result obtained by applying the @style stylesheet
1735 * to a file or @URL
1737 * Returns the number of byte written or -1 in case of failure.
1740 xsltSaveResultToFilename(const char *URL, xmlDocPtr result,
1741 xsltStylesheetPtr style, int compression) {
1742 xmlOutputBufferPtr buf;
1743 const xmlChar *encoding;
1744 int ret;
1746 if ((URL == NULL) || (result == NULL) || (style == NULL))
1747 return(-1);
1748 if (result->children == NULL)
1749 return(0);
1751 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1752 if (encoding != NULL) {
1753 xmlCharEncodingHandlerPtr encoder;
1755 encoder = xmlFindCharEncodingHandler((char *)encoding);
1756 if ((encoder != NULL) &&
1757 (xmlStrEqual((const xmlChar *)encoder->name,
1758 (const xmlChar *) "UTF-8")))
1759 encoder = NULL;
1760 buf = xmlOutputBufferCreateFilename(URL, encoder, compression);
1761 } else {
1762 buf = xmlOutputBufferCreateFilename(URL, NULL, compression);
1764 if (buf == NULL)
1765 return(-1);
1766 xsltSaveResultTo(buf, result, style);
1767 ret = xmlOutputBufferClose(buf);
1768 return(ret);
1772 * xsltSaveResultToFile:
1773 * @file: a FILE * I/O
1774 * @result: the result xmlDocPtr
1775 * @style: the stylesheet
1777 * Save the result @result obtained by applying the @style stylesheet
1778 * to an open FILE * I/O.
1779 * This does not close the FILE @file
1781 * Returns the number of bytes written or -1 in case of failure.
1784 xsltSaveResultToFile(FILE *file, xmlDocPtr result, xsltStylesheetPtr style) {
1785 xmlOutputBufferPtr buf;
1786 const xmlChar *encoding;
1787 int ret;
1789 if ((file == NULL) || (result == NULL) || (style == NULL))
1790 return(-1);
1791 if (result->children == NULL)
1792 return(0);
1794 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1795 if (encoding != NULL) {
1796 xmlCharEncodingHandlerPtr encoder;
1798 encoder = xmlFindCharEncodingHandler((char *)encoding);
1799 if ((encoder != NULL) &&
1800 (xmlStrEqual((const xmlChar *)encoder->name,
1801 (const xmlChar *) "UTF-8")))
1802 encoder = NULL;
1803 buf = xmlOutputBufferCreateFile(file, encoder);
1804 } else {
1805 buf = xmlOutputBufferCreateFile(file, NULL);
1808 if (buf == NULL)
1809 return(-1);
1810 xsltSaveResultTo(buf, result, style);
1811 ret = xmlOutputBufferClose(buf);
1812 return(ret);
1816 * xsltSaveResultToFd:
1817 * @fd: a file descriptor
1818 * @result: the result xmlDocPtr
1819 * @style: the stylesheet
1821 * Save the result @result obtained by applying the @style stylesheet
1822 * to an open file descriptor
1823 * This does not close the descriptor.
1825 * Returns the number of bytes written or -1 in case of failure.
1828 xsltSaveResultToFd(int fd, xmlDocPtr result, xsltStylesheetPtr style) {
1829 xmlOutputBufferPtr buf;
1830 const xmlChar *encoding;
1831 int ret;
1833 if ((fd < 0) || (result == NULL) || (style == NULL))
1834 return(-1);
1835 if (result->children == NULL)
1836 return(0);
1838 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1839 if (encoding != NULL) {
1840 xmlCharEncodingHandlerPtr encoder;
1842 encoder = xmlFindCharEncodingHandler((char *)encoding);
1843 if ((encoder != NULL) &&
1844 (xmlStrEqual((const xmlChar *)encoder->name,
1845 (const xmlChar *) "UTF-8")))
1846 encoder = NULL;
1847 buf = xmlOutputBufferCreateFd(fd, encoder);
1848 } else {
1849 buf = xmlOutputBufferCreateFd(fd, NULL);
1851 if (buf == NULL)
1852 return(-1);
1853 xsltSaveResultTo(buf, result, style);
1854 ret = xmlOutputBufferClose(buf);
1855 return(ret);
1859 * xsltSaveResultToString:
1860 * @doc_txt_ptr: Memory pointer for allocated XML text
1861 * @doc_txt_len: Length of the generated XML text
1862 * @result: the result xmlDocPtr
1863 * @style: the stylesheet
1865 * Save the result @result obtained by applying the @style stylesheet
1866 * to a new allocated string.
1868 * Returns 0 in case of success and -1 in case of error
1871 xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len,
1872 xmlDocPtr result, xsltStylesheetPtr style) {
1873 xmlOutputBufferPtr buf;
1874 const xmlChar *encoding;
1876 *doc_txt_ptr = NULL;
1877 *doc_txt_len = 0;
1878 if (result->children == NULL)
1879 return(0);
1881 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1882 if (encoding != NULL) {
1883 xmlCharEncodingHandlerPtr encoder;
1885 encoder = xmlFindCharEncodingHandler((char *)encoding);
1886 if ((encoder != NULL) &&
1887 (xmlStrEqual((const xmlChar *)encoder->name,
1888 (const xmlChar *) "UTF-8")))
1889 encoder = NULL;
1890 buf = xmlAllocOutputBuffer(encoder);
1891 if (buf == NULL)
1892 xmlCharEncCloseFunc(encoder);
1893 } else {
1894 buf = xmlAllocOutputBuffer(NULL);
1896 if (buf == NULL)
1897 return(-1);
1898 xsltSaveResultTo(buf, result, style);
1899 #ifdef LIBXML2_NEW_BUFFER
1900 if (buf->conv != NULL) {
1901 *doc_txt_len = xmlBufUse(buf->conv);
1902 *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->conv), *doc_txt_len);
1903 } else {
1904 *doc_txt_len = xmlBufUse(buf->buffer);
1905 *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->buffer), *doc_txt_len);
1907 #else
1908 if (buf->conv != NULL) {
1909 *doc_txt_len = buf->conv->use;
1910 *doc_txt_ptr = xmlStrndup(buf->conv->content, *doc_txt_len);
1911 } else {
1912 *doc_txt_len = buf->buffer->use;
1913 *doc_txt_ptr = xmlStrndup(buf->buffer->content, *doc_txt_len);
1915 #endif
1916 (void)xmlOutputBufferClose(buf);
1917 return 0;
1921 * xsltGetSourceNodeFlags:
1922 * @node: Node from source document
1924 * Returns the flags for a source node.
1927 xsltGetSourceNodeFlags(xmlNodePtr node) {
1929 * Squeeze the bit flags into the upper bits of
1931 * - 'int properties' member in struct _xmlDoc
1932 * - 'xmlAttributeType atype' member in struct _xmlAttr
1933 * - 'unsigned short extra' member in struct _xmlNode
1935 switch (node->type) {
1936 case XML_DOCUMENT_NODE:
1937 case XML_HTML_DOCUMENT_NODE:
1938 return ((xmlDocPtr) node)->properties >> 27;
1940 case XML_ATTRIBUTE_NODE:
1941 return ((xmlAttrPtr) node)->atype >> 27;
1943 case XML_ELEMENT_NODE:
1944 case XML_TEXT_NODE:
1945 case XML_CDATA_SECTION_NODE:
1946 case XML_PI_NODE:
1947 case XML_COMMENT_NODE:
1948 return node->extra >> 12;
1950 default:
1951 return 0;
1956 * xsltSetSourceNodeFlags:
1957 * @node: Node from source document
1958 * @flags: Flags
1960 * Sets the specified flags to 1.
1962 * Returns 0 on success, -1 on error.
1965 xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
1966 int flags) {
1967 if (node->doc == ctxt->initialContextDoc)
1968 ctxt->sourceDocDirty = 1;
1970 switch (node->type) {
1971 case XML_DOCUMENT_NODE:
1972 case XML_HTML_DOCUMENT_NODE:
1973 ((xmlDocPtr) node)->properties |= flags << 27;
1974 return 0;
1976 case XML_ATTRIBUTE_NODE:
1977 ((xmlAttrPtr) node)->atype |= flags << 27;
1978 return 0;
1980 case XML_ELEMENT_NODE:
1981 case XML_TEXT_NODE:
1982 case XML_CDATA_SECTION_NODE:
1983 case XML_PI_NODE:
1984 case XML_COMMENT_NODE:
1985 node->extra |= flags << 12;
1986 return 0;
1988 default:
1989 return -1;
1994 * xsltClearSourceNodeFlags:
1995 * @node: Node from source document
1996 * @flags: Flags
1998 * Sets the specified flags to 0.
2000 * Returns 0 on success, -1 on error.
2003 xsltClearSourceNodeFlags(xmlNodePtr node, int flags) {
2004 switch (node->type) {
2005 case XML_DOCUMENT_NODE:
2006 case XML_HTML_DOCUMENT_NODE:
2007 ((xmlDocPtr) node)->properties &= ~(flags << 27);
2008 return 0;
2010 case XML_ATTRIBUTE_NODE:
2011 ((xmlAttrPtr) node)->atype &= ~(flags << 27);
2012 return 0;
2014 case XML_ELEMENT_NODE:
2015 case XML_TEXT_NODE:
2016 case XML_CDATA_SECTION_NODE:
2017 case XML_PI_NODE:
2018 case XML_COMMENT_NODE:
2019 node->extra &= ~(flags << 12);
2020 return 0;
2022 default:
2023 return -1;
2028 * xsltGetPSVIPtr:
2029 * @cur: Node
2031 * Returns a pointer to the psvi member of a node or NULL on error.
2033 void **
2034 xsltGetPSVIPtr(xmlNodePtr cur) {
2035 switch (cur->type) {
2036 case XML_DOCUMENT_NODE:
2037 case XML_HTML_DOCUMENT_NODE:
2038 return &((xmlDocPtr) cur)->psvi;
2040 case XML_ATTRIBUTE_NODE:
2041 return &((xmlAttrPtr) cur)->psvi;
2043 case XML_ELEMENT_NODE:
2044 case XML_TEXT_NODE:
2045 case XML_CDATA_SECTION_NODE:
2046 case XML_PI_NODE:
2047 case XML_COMMENT_NODE:
2048 return &cur->psvi;
2050 default:
2051 return NULL;
2055 #ifdef WITH_PROFILER
2057 /************************************************************************
2059 * Generating profiling information *
2061 ************************************************************************/
2063 static long calibration = -1;
2066 * xsltCalibrateTimestamps:
2068 * Used for to calibrate the xsltTimestamp() function
2069 * Should work if launched at startup and we don't loose our quantum :-)
2071 * Returns the number of milliseconds used by xsltTimestamp()
2073 #if !defined(XSLT_WIN32_PERFORMANCE_COUNTER) && \
2074 (defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY))
2075 static long
2076 xsltCalibrateTimestamps(void) {
2077 register int i;
2079 for (i = 0;i < 999;i++)
2080 xsltTimestamp();
2081 return(xsltTimestamp() / 1000);
2083 #endif
2086 * xsltCalibrateAdjust:
2087 * @delta: a negative dealy value found
2089 * Used for to correct the calibration for xsltTimestamp()
2091 void
2092 xsltCalibrateAdjust(long delta) {
2093 calibration += delta;
2097 * xsltTimestamp:
2099 * Used for gathering profiling data
2101 * Returns the number of tenth of milliseconds since the beginning of the
2102 * profiling
2104 long
2105 xsltTimestamp(void)
2107 #ifdef XSLT_WIN32_PERFORMANCE_COUNTER
2108 BOOL ok;
2109 LARGE_INTEGER performanceCount;
2110 LARGE_INTEGER performanceFrequency;
2111 LONGLONG quadCount;
2112 double seconds;
2113 static LONGLONG startupQuadCount = 0;
2114 static LONGLONG startupQuadFreq = 0;
2116 ok = QueryPerformanceCounter(&performanceCount);
2117 if (!ok)
2118 return 0;
2119 quadCount = performanceCount.QuadPart;
2120 if (calibration < 0) {
2121 calibration = 0;
2122 ok = QueryPerformanceFrequency(&performanceFrequency);
2123 if (!ok)
2124 return 0;
2125 startupQuadFreq = performanceFrequency.QuadPart;
2126 startupQuadCount = quadCount;
2127 return (0);
2129 if (startupQuadFreq == 0)
2130 return 0;
2131 seconds = (quadCount - startupQuadCount) / (double) startupQuadFreq;
2132 return (long) (seconds * XSLT_TIMESTAMP_TICS_PER_SEC);
2134 #else /* XSLT_WIN32_PERFORMANCE_COUNTER */
2135 #ifdef HAVE_CLOCK_GETTIME
2136 # if defined(CLOCK_MONOTONIC)
2137 # define XSLT_CLOCK CLOCK_MONOTONIC
2138 # elif defined(CLOCK_HIGHRES)
2139 # define XSLT_CLOCK CLOCK_HIGHRES
2140 # else
2141 # define XSLT_CLOCK CLOCK_REALTIME
2142 # endif
2143 static struct timespec startup;
2144 struct timespec cur;
2145 long tics;
2147 if (calibration < 0) {
2148 clock_gettime(XSLT_CLOCK, &startup);
2149 calibration = 0;
2150 calibration = xsltCalibrateTimestamps();
2151 clock_gettime(XSLT_CLOCK, &startup);
2152 return (0);
2155 clock_gettime(XSLT_CLOCK, &cur);
2156 tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
2157 tics += (cur.tv_nsec - startup.tv_nsec) /
2158 (1000000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
2160 tics -= calibration;
2161 return(tics);
2163 #elif HAVE_GETTIMEOFDAY
2164 static struct timeval startup;
2165 struct timeval cur;
2166 long tics;
2168 if (calibration < 0) {
2169 gettimeofday(&startup, NULL);
2170 calibration = 0;
2171 calibration = xsltCalibrateTimestamps();
2172 gettimeofday(&startup, NULL);
2173 return (0);
2176 gettimeofday(&cur, NULL);
2177 tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
2178 tics += (cur.tv_usec - startup.tv_usec) /
2179 (1000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
2181 tics -= calibration;
2182 return(tics);
2183 #else
2185 /* Neither gettimeofday() nor Win32 performance counter available */
2187 return (0);
2189 #endif /* HAVE_GETTIMEOFDAY */
2190 #endif /* XSLT_WIN32_PERFORMANCE_COUNTER */
2193 static char *
2194 pretty_templ_match(xsltTemplatePtr templ) {
2195 static char dst[1001];
2196 char *src = (char *)templ->match;
2197 int i=0,j;
2199 /* strip white spaces */
2200 for (j=0; i<1000 && src[j]; i++,j++) {
2201 for(;src[j]==' ';j++);
2202 dst[i]=src[j];
2204 if(i<998 && templ->mode) {
2205 /* append [mode] */
2206 dst[i++]='[';
2207 src=(char *)templ->mode;
2208 for (j=0; i<999 && src[j]; i++,j++) {
2209 dst[i]=src[j];
2211 dst[i++]=']';
2213 dst[i]='\0';
2214 return dst;
2217 #define MAX_TEMPLATES 10000
2220 * xsltSaveProfiling:
2221 * @ctxt: an XSLT context
2222 * @output: a FILE * for saving the information
2224 * Save the profiling information on @output
2226 void
2227 xsltSaveProfiling(xsltTransformContextPtr ctxt, FILE *output) {
2228 int nb, i,j,k,l;
2229 int max;
2230 int total;
2231 unsigned long totalt;
2232 xsltTemplatePtr *templates;
2233 xsltStylesheetPtr style;
2234 xsltTemplatePtr templ1,templ2;
2235 int *childt;
2237 if ((output == NULL) || (ctxt == NULL))
2238 return;
2239 if (ctxt->profile == 0)
2240 return;
2242 nb = 0;
2243 max = MAX_TEMPLATES;
2244 templates = xmlMalloc(max * sizeof(xsltTemplatePtr));
2245 if (templates == NULL)
2246 return;
2248 style = ctxt->style;
2249 while (style != NULL) {
2250 templ1 = style->templates;
2251 while (templ1 != NULL) {
2252 if (nb >= max)
2253 break;
2255 if (templ1->nbCalls > 0)
2256 templates[nb++] = templ1;
2257 templ1 = templ1->next;
2260 style = xsltNextImport(style);
2263 for (i = 0;i < nb -1;i++) {
2264 for (j = i + 1; j < nb; j++) {
2265 if ((templates[i]->time <= templates[j]->time) ||
2266 ((templates[i]->time == templates[j]->time) &&
2267 (templates[i]->nbCalls <= templates[j]->nbCalls))) {
2268 templ1 = templates[j];
2269 templates[j] = templates[i];
2270 templates[i] = templ1;
2276 /* print flat profile */
2278 fprintf(output, "%6s%20s%20s%10s Calls Tot 100us Avg\n\n",
2279 "number", "match", "name", "mode");
2280 total = 0;
2281 totalt = 0;
2282 for (i = 0;i < nb;i++) {
2283 templ1 = templates[i];
2284 fprintf(output, "%5d ", i);
2285 if (templ1->match != NULL) {
2286 if (xmlStrlen(templ1->match) > 20)
2287 fprintf(output, "%s\n%26s", templ1->match, "");
2288 else
2289 fprintf(output, "%20s", templ1->match);
2290 } else {
2291 fprintf(output, "%20s", "");
2293 if (templ1->name != NULL) {
2294 if (xmlStrlen(templ1->name) > 20)
2295 fprintf(output, "%s\n%46s", templ1->name, "");
2296 else
2297 fprintf(output, "%20s", templ1->name);
2298 } else {
2299 fprintf(output, "%20s", "");
2301 if (templ1->mode != NULL) {
2302 if (xmlStrlen(templ1->mode) > 10)
2303 fprintf(output, "%s\n%56s", templ1->mode, "");
2304 else
2305 fprintf(output, "%10s", templ1->mode);
2306 } else {
2307 fprintf(output, "%10s", "");
2309 fprintf(output, " %6d", templ1->nbCalls);
2310 fprintf(output, " %6ld %6ld\n", templ1->time,
2311 templ1->time / templ1->nbCalls);
2312 total += templ1->nbCalls;
2313 totalt += templ1->time;
2315 fprintf(output, "\n%30s%26s %6d %6ld\n", "Total", "", total, totalt);
2318 /* print call graph */
2320 childt = xmlMalloc((nb + 1) * sizeof(int));
2321 if (childt == NULL)
2322 return;
2324 /* precalculate children times */
2325 for (i = 0; i < nb; i++) {
2326 templ1 = templates[i];
2328 childt[i] = 0;
2329 for (k = 0; k < nb; k++) {
2330 templ2 = templates[k];
2331 for (l = 0; l < templ2->templNr; l++) {
2332 if (templ2->templCalledTab[l] == templ1) {
2333 childt[i] +=templ2->time;
2338 childt[i] = 0;
2340 fprintf(output, "\nindex %% time self children called name\n");
2342 for (i = 0; i < nb; i++) {
2343 char ix_str[20], timep_str[20], times_str[20], timec_str[20], called_str[20];
2344 unsigned long t;
2346 templ1 = templates[i];
2347 /* callers */
2348 for (j = 0; j < templ1->templNr; j++) {
2349 templ2 = templ1->templCalledTab[j];
2350 for (k = 0; k < nb; k++) {
2351 if (templates[k] == templ2)
2352 break;
2354 t=templ2?templ2->time:totalt;
2355 snprintf(times_str,sizeof(times_str),"%8.3f",(float)t/XSLT_TIMESTAMP_TICS_PER_SEC);
2356 snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC);
2357 snprintf(called_str,sizeof(called_str),"%6d/%d",
2358 templ1->templCountTab[j], /* number of times caller calls 'this' */
2359 templ1->nbCalls); /* total number of calls to 'this' */
2361 fprintf(output, " %-8s %-8s %-12s %s [%d]\n",
2362 times_str,timec_str,called_str,
2363 (templ2?(templ2->name?(char *)templ2->name:pretty_templ_match(templ2)):"-"),k);
2365 /* this */
2366 snprintf(ix_str,sizeof(ix_str),"[%d]",i);
2367 snprintf(timep_str,sizeof(timep_str),"%6.2f",(float)templ1->time*100.0/totalt);
2368 snprintf(times_str,sizeof(times_str),"%8.3f",(float)templ1->time/XSLT_TIMESTAMP_TICS_PER_SEC);
2369 snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[i]/XSLT_TIMESTAMP_TICS_PER_SEC);
2370 fprintf(output, "%-5s %-6s %-8s %-8s %6d %s [%d]\n",
2371 ix_str, timep_str,times_str,timec_str,
2372 templ1->nbCalls,
2373 templ1->name?(char *)templ1->name:pretty_templ_match(templ1),i);
2374 /* callees
2375 * - go over templates[0..nb] and their templCalledTab[]
2376 * - print those where we in the the call-stack
2378 total = 0;
2379 for (k = 0; k < nb; k++) {
2380 templ2 = templates[k];
2381 for (l = 0; l < templ2->templNr; l++) {
2382 if (templ2->templCalledTab[l] == templ1) {
2383 total+=templ2->templCountTab[l];
2387 for (k = 0; k < nb; k++) {
2388 templ2 = templates[k];
2389 for (l = 0; l < templ2->templNr; l++) {
2390 if (templ2->templCalledTab[l] == templ1) {
2391 snprintf(times_str,sizeof(times_str),"%8.3f",(float)templ2->time/XSLT_TIMESTAMP_TICS_PER_SEC);
2392 snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC);
2393 snprintf(called_str,sizeof(called_str),"%6d/%d",
2394 templ2->templCountTab[l], /* number of times 'this' calls callee */
2395 total); /* total number of calls from 'this' */
2396 fprintf(output, " %-8s %-8s %-12s %s [%d]\n",
2397 times_str,timec_str,called_str,
2398 templ2->name?(char *)templ2->name:pretty_templ_match(templ2),k);
2402 fprintf(output, "-----------------------------------------------\n");
2405 fprintf(output, "\f\nIndex by function name\n");
2406 for (i = 0; i < nb; i++) {
2407 templ1 = templates[i];
2408 fprintf(output, "[%d] %s (%s:%d)\n",
2409 i, templ1->name?(char *)templ1->name:pretty_templ_match(templ1),
2410 templ1->style->doc->URL,templ1->elem->line);
2413 fprintf(output, "\f\n");
2414 xmlFree(childt);
2416 xmlFree(templates);
2419 /************************************************************************
2421 * Fetching profiling information *
2423 ************************************************************************/
2426 * xsltGetProfileInformation:
2427 * @ctxt: a transformation context
2429 * This function should be called after the transformation completed
2430 * to extract template processing profiling information if available.
2431 * The information is returned as an XML document tree like
2432 * <?xml version="1.0"?>
2433 * <profile>
2434 * <template rank="1" match="*" name=""
2435 * mode="" calls="6" time="48" average="8"/>
2436 * <template rank="2" match="item2|item3" name=""
2437 * mode="" calls="10" time="30" average="3"/>
2438 * <template rank="3" match="item1" name=""
2439 * mode="" calls="5" time="17" average="3"/>
2440 * </profile>
2441 * The caller will need to free up the returned tree with xmlFreeDoc()
2443 * Returns the xmlDocPtr corresponding to the result or NULL if not available.
2446 xmlDocPtr
2447 xsltGetProfileInformation(xsltTransformContextPtr ctxt)
2449 xmlDocPtr ret = NULL;
2450 xmlNodePtr root, child;
2451 char buf[100];
2453 xsltStylesheetPtr style;
2454 xsltTemplatePtr *templates;
2455 xsltTemplatePtr templ;
2456 int nb = 0, max = 0, i, j;
2458 if (!ctxt)
2459 return NULL;
2461 if (!ctxt->profile)
2462 return NULL;
2464 nb = 0;
2465 max = 10000;
2466 templates =
2467 (xsltTemplatePtr *) xmlMalloc(max * sizeof(xsltTemplatePtr));
2468 if (templates == NULL)
2469 return NULL;
2472 * collect all the templates in an array
2474 style = ctxt->style;
2475 while (style != NULL) {
2476 templ = style->templates;
2477 while (templ != NULL) {
2478 if (nb >= max)
2479 break;
2481 if (templ->nbCalls > 0)
2482 templates[nb++] = templ;
2483 templ = templ->next;
2486 style = (xsltStylesheetPtr) xsltNextImport(style);
2490 * Sort the array by time spent
2492 for (i = 0; i < nb - 1; i++) {
2493 for (j = i + 1; j < nb; j++) {
2494 if ((templates[i]->time <= templates[j]->time) ||
2495 ((templates[i]->time == templates[j]->time) &&
2496 (templates[i]->nbCalls <= templates[j]->nbCalls))) {
2497 templ = templates[j];
2498 templates[j] = templates[i];
2499 templates[i] = templ;
2505 * Generate a document corresponding to the results.
2507 ret = xmlNewDoc(BAD_CAST "1.0");
2508 root = xmlNewDocNode(ret, NULL, BAD_CAST "profile", NULL);
2509 xmlDocSetRootElement(ret, root);
2511 for (i = 0; i < nb; i++) {
2512 child = xmlNewChild(root, NULL, BAD_CAST "template", NULL);
2513 snprintf(buf, sizeof(buf), "%d", i + 1);
2514 xmlSetProp(child, BAD_CAST "rank", BAD_CAST buf);
2515 xmlSetProp(child, BAD_CAST "match", BAD_CAST templates[i]->match);
2516 xmlSetProp(child, BAD_CAST "name", BAD_CAST templates[i]->name);
2517 xmlSetProp(child, BAD_CAST "mode", BAD_CAST templates[i]->mode);
2519 snprintf(buf, sizeof(buf), "%d", templates[i]->nbCalls);
2520 xmlSetProp(child, BAD_CAST "calls", BAD_CAST buf);
2522 snprintf(buf, sizeof(buf), "%ld", templates[i]->time);
2523 xmlSetProp(child, BAD_CAST "time", BAD_CAST buf);
2525 snprintf(buf, sizeof(buf), "%ld", templates[i]->time / templates[i]->nbCalls);
2526 xmlSetProp(child, BAD_CAST "average", BAD_CAST buf);
2529 xmlFree(templates);
2531 return ret;
2534 #endif /* WITH_PROFILER */
2536 /************************************************************************
2538 * Hooks for libxml2 XPath *
2540 ************************************************************************/
2543 * xsltXPathCompileFlags:
2544 * @style: the stylesheet
2545 * @str: the XPath expression
2546 * @flags: extra compilation flags to pass down to libxml2 XPath
2548 * Compile an XPath expression
2550 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2551 * the caller has to free the object.
2553 xmlXPathCompExprPtr
2554 xsltXPathCompileFlags(xsltStylesheetPtr style, const xmlChar *str, int flags) {
2555 xmlXPathContextPtr xpathCtxt;
2556 xmlXPathCompExprPtr ret;
2558 if (style != NULL) {
2559 xpathCtxt = style->principal->xpathCtxt;
2560 if (xpathCtxt == NULL)
2561 return NULL;
2562 xpathCtxt->dict = style->dict;
2563 } else {
2564 xpathCtxt = xmlXPathNewContext(NULL);
2565 if (xpathCtxt == NULL)
2566 return NULL;
2568 xpathCtxt->flags = flags;
2571 * Compile the expression.
2573 ret = xmlXPathCtxtCompile(xpathCtxt, str);
2575 if (style == NULL) {
2576 xmlXPathFreeContext(xpathCtxt);
2579 * TODO: there is a lot of optimizations which should be possible
2580 * like variable slot precomputations, function precomputations, etc.
2583 return(ret);
2587 * xsltXPathCompile:
2588 * @style: the stylesheet
2589 * @str: the XPath expression
2591 * Compile an XPath expression
2593 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2594 * the caller has to free the object.
2596 xmlXPathCompExprPtr
2597 xsltXPathCompile(xsltStylesheetPtr style, const xmlChar *str) {
2598 return(xsltXPathCompileFlags(style, str, 0));
2601 /************************************************************************
2603 * Hooks for the debugger *
2605 ************************************************************************/
2607 int xslDebugStatus;
2610 * xsltGetDebuggerStatus:
2612 * Get xslDebugStatus.
2614 * Returns the value of xslDebugStatus.
2617 xsltGetDebuggerStatus(void)
2619 return(xslDebugStatus);
2622 #ifdef WITH_DEBUGGER
2625 * There is currently only 3 debugging callback defined
2626 * Debugger callbacks are disabled by default
2628 #define XSLT_CALLBACK_NUMBER 3
2630 typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks;
2631 typedef xsltDebuggerCallbacks *xsltDebuggerCallbacksPtr;
2632 struct _xsltDebuggerCallbacks {
2633 xsltHandleDebuggerCallback handler;
2634 xsltAddCallCallback add;
2635 xsltDropCallCallback drop;
2638 static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks = {
2639 NULL, /* handler */
2640 NULL, /* add */
2641 NULL /* drop */
2645 * xsltSetDebuggerStatus:
2646 * @value : the value to be set
2648 * This function sets the value of xslDebugStatus.
2650 void
2651 xsltSetDebuggerStatus(int value)
2653 xslDebugStatus = value;
2657 * xsltSetDebuggerCallbacks:
2658 * @no : number of callbacks
2659 * @block : the block of callbacks
2661 * This function allow to plug a debugger into the XSLT library
2662 * @block points to a block of memory containing the address of @no
2663 * callback routines.
2665 * Returns 0 in case of success and -1 in case of error
2668 xsltSetDebuggerCallbacks(int no, void *block)
2670 xsltDebuggerCallbacksPtr callbacks;
2672 if ((block == NULL) || (no != XSLT_CALLBACK_NUMBER))
2673 return(-1);
2675 callbacks = (xsltDebuggerCallbacksPtr) block;
2676 xsltDebuggerCurrentCallbacks.handler = callbacks->handler;
2677 xsltDebuggerCurrentCallbacks.add = callbacks->add;
2678 xsltDebuggerCurrentCallbacks.drop = callbacks->drop;
2679 return(0);
2683 * xslHandleDebugger:
2684 * @cur : source node being executed
2685 * @node : data node being processed
2686 * @templ : temlate that applies to node
2687 * @ctxt : the xslt transform context
2689 * If either cur or node are a breakpoint, or xslDebugStatus in state
2690 * where debugging must occcur at this time then transfer control
2691 * to the xslDebugBreak function
2693 void
2694 xslHandleDebugger(xmlNodePtr cur, xmlNodePtr node, xsltTemplatePtr templ,
2695 xsltTransformContextPtr ctxt)
2697 if (xsltDebuggerCurrentCallbacks.handler != NULL)
2698 xsltDebuggerCurrentCallbacks.handler(cur, node, templ, ctxt);
2702 * xslAddCall:
2703 * @templ : current template being applied
2704 * @source : the source node being processed
2706 * Add template "call" to call stack
2707 * Returns : 1 on sucess 0 otherwise an error may be printed if
2708 * WITH_XSLT_DEBUG_BREAKPOINTS is defined
2711 xslAddCall(xsltTemplatePtr templ, xmlNodePtr source)
2713 if (xsltDebuggerCurrentCallbacks.add != NULL)
2714 return(xsltDebuggerCurrentCallbacks.add(templ, source));
2715 return(0);
2719 * xslDropCall:
2721 * Drop the topmost item off the call stack
2723 void
2724 xslDropCall(void)
2726 if (xsltDebuggerCurrentCallbacks.drop != NULL)
2727 xsltDebuggerCurrentCallbacks.drop();
2730 #endif /* WITH_DEBUGGER */