Merge branch 'tizen_base' into tizen
[platform/upstream/libxslt.git] / libxslt / xsltutils.c
1 /*
2  * xsltutils.c: Utilities for the XSL Transformation 1.0 engine
3  *
4  * Reference:
5  *   http://www.w3.org/TR/1999/REC-xslt-19991116
6  *
7  * See Copyright for the status of this software.
8  *
9  * daniel@veillard.com
10  */
11
12 #define IN_LIBXSLT
13 #include "libxslt.h"
14
15 #ifndef XSLT_NEED_TRIO
16 #include <stdio.h>
17 #else
18 #include <trio.h>
19 #endif
20
21 #include <string.h>
22 #include <time.h>
23 #ifdef HAVE_SYS_TIME_H
24 #include <sys/time.h>
25 #endif
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29 #ifdef HAVE_STDLIB_H
30 #include <stdlib.h>
31 #endif
32 #include <stdarg.h>
33
34 #include <libxml/xmlmemory.h>
35 #include <libxml/tree.h>
36 #include <libxml/HTMLtree.h>
37 #include <libxml/xmlerror.h>
38 #include <libxml/xmlIO.h>
39 #include "xsltutils.h"
40 #include "templates.h"
41 #include "xsltInternals.h"
42 #include "imports.h"
43 #include "transform.h"
44
45 #if defined(_WIN32) && !defined(__CYGWIN__)
46 #define XSLT_WIN32_PERFORMANCE_COUNTER
47 #endif
48
49 /************************************************************************
50  *                                                                      *
51  *                      Convenience function                            *
52  *                                                                      *
53  ************************************************************************/
54
55 /**
56  * xsltGetCNsProp:
57  * @style: the stylesheet
58  * @node:  the node
59  * @name:  the attribute name
60  * @nameSpace:  the URI of the namespace
61  *
62  * Similar to xmlGetNsProp() but with a slightly different semantic
63  *
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.
67  *
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.
71  *
72  * Returns the attribute value or NULL if not found. The string is allocated
73  *         in the stylesheet dictionary.
74  */
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;
83
84     if ((node == NULL) || (style == NULL) || (style->dict == NULL))
85         return(NULL);
86
87     if (nameSpace == NULL)
88         return xmlGetProp(node, name);
89
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) {
97         /*
98          * One need to have
99          *   - same attribute names
100          *   - and the attribute carrying that namespace
101          */
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))))) {
107
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);
114             }
115             return ret;
116         }
117         prop = prop->next;
118     }
119     tmp = NULL;
120     /*
121      * Check if there is a default declaration in the internal
122      * or external subsets
123      */
124     doc =  node->doc;
125     if (doc != NULL) {
126         if (doc->intSubset != NULL) {
127             xmlAttributePtr attrDecl;
128
129             attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
130             if ((attrDecl == NULL) && (doc->extSubset != NULL))
131                 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
132
133             if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) {
134                 /*
135                  * The DTD declaration only allows a prefix search
136                  */
137                 ns = xmlSearchNs(doc, node, attrDecl->prefix);
138                 if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace)))
139                     return(xmlDictLookup(style->dict,
140                                          attrDecl->defaultValue, -1));
141             }
142         }
143     }
144     return(NULL);
145 }
146 /**
147  * xsltGetNsProp:
148  * @node:  the node
149  * @name:  the attribute name
150  * @nameSpace:  the URI of the namespace
151  *
152  * Similar to xmlGetNsProp() but with a slightly different semantic
153  *
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.
157  *
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.
161  *
162  * Returns the attribute value or NULL if not found.
163  *     It's up to the caller to free the memory.
164  */
165 xmlChar *
166 xsltGetNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) {
167     xmlAttrPtr prop;
168     xmlDocPtr doc;
169     xmlNsPtr ns;
170
171     if (node == NULL)
172         return(NULL);
173
174     if (nameSpace == NULL)
175         return xmlGetProp(node, name);
176
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;
183     /*
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.
191     */
192     while (prop != NULL) {
193         /*
194          * One need to have
195          *   - same attribute names
196          *   - and the attribute carrying that namespace
197          */
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;
204
205             ret = xmlNodeListGetString(node->doc, prop->children, 1);
206             if (ret == NULL) return(xmlStrdup((xmlChar *)""));
207             return(ret);
208         }
209         prop = prop->next;
210     }
211
212     /*
213      * Check if there is a default declaration in the internal
214      * or external subsets
215      */
216     doc =  node->doc;
217     if (doc != NULL) {
218         if (doc->intSubset != NULL) {
219             xmlAttributePtr attrDecl;
220
221             attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
222             if ((attrDecl == NULL) && (doc->extSubset != NULL))
223                 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
224
225             if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) {
226                 /*
227                  * The DTD declaration only allows a prefix search
228                  */
229                 ns = xmlSearchNs(doc, node, attrDecl->prefix);
230                 if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace)))
231                     return(xmlStrdup(attrDecl->defaultValue));
232             }
233         }
234     }
235     return(NULL);
236 }
237
238 /**
239  * xsltGetUTF8Char:
240  * @utf:  a sequence of UTF-8 encoded bytes
241  * @len:  a pointer to @bytes len
242  *
243  * Read one UTF8 Char from @utf
244  * Function copied from libxml2 xmlGetUTF8Char() ... to discard ultimately
245  * and use the original API
246  *
247  * Returns the char value or -1 in case of error and update @len with the
248  *        number of bytes used
249  */
250 int
251 xsltGetUTF8Char(const unsigned char *utf, int *len) {
252     unsigned int c;
253
254     if (utf == NULL)
255         goto error;
256     if (len == NULL)
257         goto error;
258     if (*len < 1)
259         goto error;
260
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;
289             }
290         } else {
291           /* 2-byte code */
292             *len = 2;
293             c = (utf[0] & 0x1f) << 6;
294             c |= utf[1] & 0x3f;
295         }
296     } else {
297         /* 1-byte code */
298         *len = 1;
299     }
300     return(c);
301
302 error:
303     if (len != NULL)
304         *len = 0;
305     return(-1);
306 }
307
308 #ifdef XSLT_REFACTORED
309
310 /**
311  * xsltPointerListAddSize:
312  * @list: the pointer list structure
313  * @item: the item to be stored
314  * @initialSize: the initial size of the list
315  *
316  * Adds an item to the list.
317  *
318  * Returns the position of the added item in the list or
319  *         -1 in case of an error.
320  */
321 int
322 xsltPointerListAddSize(xsltPointerListPtr list,
323                        void *item,
324                        int initialSize)
325 {
326     if (list->items == NULL) {
327         if (initialSize <= 0)
328             initialSize = 1;
329         list->items = (void **) xmlMalloc(
330             initialSize * sizeof(void *));
331         if (list->items == NULL) {
332             xsltGenericError(xsltGenericErrorContext,
333              "xsltPointerListAddSize: memory allocation failure.\n");
334             return(-1);
335         }
336         list->number = 0;
337         list->size = initialSize;
338     } else if (list->size <= list->number) {
339         list->size *= 2;
340         list->items = (void **) xmlRealloc(list->items,
341             list->size * sizeof(void *));
342         if (list->items == NULL) {
343             xsltGenericError(xsltGenericErrorContext,
344              "xsltPointerListAddSize: memory re-allocation failure.\n");
345             list->size = 0;
346             return(-1);
347         }
348     }
349     list->items[list->number++] = item;
350     return(0);
351 }
352
353 /**
354  * xsltPointerListCreate:
355  * @initialSize: the initial size for the list
356  *
357  * Creates an xsltPointerList structure.
358  *
359  * Returns a xsltPointerList structure or NULL in case of an error.
360  */
361 xsltPointerListPtr
362 xsltPointerListCreate(int initialSize)
363 {
364     xsltPointerListPtr ret;
365
366     ret = xmlMalloc(sizeof(xsltPointerList));
367     if (ret == NULL) {
368         xsltGenericError(xsltGenericErrorContext,
369              "xsltPointerListCreate: memory allocation failure.\n");
370         return (NULL);
371     }
372     memset(ret, 0, sizeof(xsltPointerList));
373     if (initialSize > 0) {
374         xsltPointerListAddSize(ret, NULL, initialSize);
375         ret->number = 0;
376     }
377     return (ret);
378 }
379
380 /**
381  * xsltPointerListFree:
382  * @list: pointer to the list to be freed
383  *
384  * Frees the xsltPointerList structure. This does not free
385  * the content of the list.
386  */
387 void
388 xsltPointerListFree(xsltPointerListPtr list)
389 {
390     if (list == NULL)
391         return;
392     if (list->items != NULL)
393         xmlFree(list->items);
394     xmlFree(list);
395 }
396
397 /**
398  * xsltPointerListClear:
399  * @list: pointer to the list to be cleared
400  *
401  * Resets the list, but does not free the allocated array
402  * and does not free the content of the list.
403  */
404 void
405 xsltPointerListClear(xsltPointerListPtr list)
406 {
407     if (list->items != NULL) {
408         xmlFree(list->items);
409         list->items = NULL;
410     }
411     list->number = 0;
412     list->size = 0;
413 }
414
415 #endif /* XSLT_REFACTORED */
416
417 /************************************************************************
418  *                                                                      *
419  *              Handling of XSLT stylesheets messages                   *
420  *                                                                      *
421  ************************************************************************/
422
423 /**
424  * xsltMessage:
425  * @ctxt:  an XSLT processing context
426  * @node:  The current node
427  * @inst:  The node containing the message instruction
428  *
429  * Process and xsl:message construct
430  */
431 void
432 xsltMessage(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst) {
433     xmlGenericErrorFunc error = xsltGenericError;
434     void *errctx = xsltGenericErrorContext;
435     xmlChar *prop, *message;
436     int terminate = 0;
437
438     if ((ctxt == NULL) || (inst == NULL))
439         return;
440
441     if (ctxt->error != NULL) {
442         error = ctxt->error;
443         errctx = ctxt->errctx;
444     }
445
446     prop = xmlGetNsProp(inst, (const xmlChar *)"terminate", NULL);
447     if (prop != NULL) {
448         if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
449             terminate = 1;
450         } else if (xmlStrEqual(prop, (const xmlChar *)"no")) {
451             terminate = 0;
452         } else {
453             xsltTransformError(ctxt, NULL, inst,
454                 "xsl:message : terminate expecting 'yes' or 'no'\n");
455         }
456         xmlFree(prop);
457     }
458     message = xsltEvalTemplateString(ctxt, node, inst);
459     if (message != NULL) {
460         int len = xmlStrlen(message);
461
462         error(errctx, "%s", (const char *)message);
463         if ((len > 0) && (message[len - 1] != '\n'))
464             error(errctx, "\n");
465         xmlFree(message);
466     }
467     if (terminate)
468         ctxt->state = XSLT_STATE_STOPPED;
469 }
470
471 /************************************************************************
472  *                                                                      *
473  *              Handling of out of context errors                       *
474  *                                                                      *
475  ************************************************************************/
476
477 #define XSLT_GET_VAR_STR(msg, str) {                            \
478     int       size;                                             \
479     int       chars;                                            \
480     char      *larger;                                          \
481     va_list   ap;                                               \
482                                                                 \
483     str = (char *) xmlMalloc(150);                              \
484     if (str == NULL)                                            \
485         return;                                                 \
486                                                                 \
487     size = 150;                                                 \
488                                                                 \
489     while (size < 64000) {                                      \
490         va_start(ap, msg);                                      \
491         chars = vsnprintf(str, size, msg, ap);                  \
492         va_end(ap);                                             \
493         if ((chars > -1) && (chars < size))                     \
494             break;                                              \
495         if (chars > -1)                                         \
496             size += chars + 1;                                  \
497         else                                                    \
498             size += 100;                                        \
499         if ((larger = (char *) xmlRealloc(str, size)) == NULL) {\
500             xmlFree(str);                                       \
501             return;                                             \
502         }                                                       \
503         str = larger;                                           \
504     }                                                           \
505 }
506 /**
507  * xsltGenericErrorDefaultFunc:
508  * @ctx:  an error context
509  * @msg:  the message to display/transmit
510  * @...:  extra parameters for the message display
511  *
512  * Default handler for out of context error messages.
513  */
514 static void LIBXSLT_ATTR_FORMAT(2,3)
515 xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
516     va_list args;
517
518     if (xsltGenericErrorContext == NULL)
519         xsltGenericErrorContext = (void *) stderr;
520
521     va_start(args, msg);
522     vfprintf((FILE *)xsltGenericErrorContext, msg, args);
523     va_end(args);
524 }
525
526 xmlGenericErrorFunc xsltGenericError = xsltGenericErrorDefaultFunc;
527 void *xsltGenericErrorContext = NULL;
528
529
530 /**
531  * xsltSetGenericErrorFunc:
532  * @ctx:  the new error handling context
533  * @handler:  the new handler function
534  *
535  * Function to reset the handler and the error context for out of
536  * context error messages.
537  * This simply means that @handler will be called for subsequent
538  * error messages while not parsing nor validating. And @ctx will
539  * be passed as first argument to @handler
540  * One can simply force messages to be emitted to another FILE * than
541  * stderr by setting @ctx to this file handle and @handler to NULL.
542  */
543 void
544 xsltSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) {
545     xsltGenericErrorContext = ctx;
546     if (handler != NULL)
547         xsltGenericError = handler;
548     else
549         xsltGenericError = xsltGenericErrorDefaultFunc;
550 }
551
552 /**
553  * xsltGenericDebugDefaultFunc:
554  * @ctx:  an error context
555  * @msg:  the message to display/transmit
556  * @...:  extra parameters for the message display
557  *
558  * Default handler for out of context error messages.
559  */
560 static void LIBXSLT_ATTR_FORMAT(2,3)
561 xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
562     va_list args;
563
564     if (xsltGenericDebugContext == NULL)
565         return;
566
567     va_start(args, msg);
568     vfprintf((FILE *)xsltGenericDebugContext, msg, args);
569     va_end(args);
570 }
571
572 xmlGenericErrorFunc xsltGenericDebug = xsltGenericDebugDefaultFunc;
573 void *xsltGenericDebugContext = NULL;
574
575
576 /**
577  * xsltSetGenericDebugFunc:
578  * @ctx:  the new error handling context
579  * @handler:  the new handler function
580  *
581  * Function to reset the handler and the error context for out of
582  * context error messages.
583  * This simply means that @handler will be called for subsequent
584  * error messages while not parsing or validating. And @ctx will
585  * be passed as first argument to @handler
586  * One can simply force messages to be emitted to another FILE * than
587  * stderr by setting @ctx to this file handle and @handler to NULL.
588  */
589 void
590 xsltSetGenericDebugFunc(void *ctx, xmlGenericErrorFunc handler) {
591     xsltGenericDebugContext = ctx;
592     if (handler != NULL)
593         xsltGenericDebug = handler;
594     else
595         xsltGenericDebug = xsltGenericDebugDefaultFunc;
596 }
597
598 /**
599  * xsltPrintErrorContext:
600  * @ctxt:  the transformation context
601  * @style:  the stylesheet
602  * @node:  the current node being processed
603  *
604  * Display the context of an error.
605  */
606 void
607 xsltPrintErrorContext(xsltTransformContextPtr ctxt,
608                       xsltStylesheetPtr style, xmlNodePtr node) {
609     int line = 0;
610     const xmlChar *file = NULL;
611     const xmlChar *name = NULL;
612     const char *type = "error";
613     xmlGenericErrorFunc error = xsltGenericError;
614     void *errctx = xsltGenericErrorContext;
615
616     if (ctxt != NULL) {
617         if (ctxt->state == XSLT_STATE_OK)
618             ctxt->state = XSLT_STATE_ERROR;
619         if (ctxt->error != NULL) {
620             error = ctxt->error;
621             errctx = ctxt->errctx;
622         }
623     }
624     if ((node == NULL) && (ctxt != NULL))
625         node = ctxt->inst;
626
627     if (node != NULL)  {
628         if ((node->type == XML_DOCUMENT_NODE) ||
629             (node->type == XML_HTML_DOCUMENT_NODE)) {
630             xmlDocPtr doc = (xmlDocPtr) node;
631
632             file = doc->URL;
633         } else {
634             line = xmlGetLineNo(node);
635             if ((node->doc != NULL) && (node->doc->URL != NULL))
636                 file = node->doc->URL;
637             if (node->name != NULL)
638                 name = node->name;
639         }
640     }
641
642     if (ctxt != NULL)
643         type = "runtime error";
644     else if (style != NULL) {
645 #ifdef XSLT_REFACTORED
646         if (XSLT_CCTXT(style)->errSeverity == XSLT_ERROR_SEVERITY_WARNING)
647             type = "compilation warning";
648         else
649             type = "compilation error";
650 #else
651         type = "compilation error";
652 #endif
653     }
654
655     if ((file != NULL) && (line != 0) && (name != NULL))
656         error(errctx, "%s: file %s line %d element %s\n",
657               type, file, line, name);
658     else if ((file != NULL) && (name != NULL))
659         error(errctx, "%s: file %s element %s\n", type, file, name);
660     else if ((file != NULL) && (line != 0))
661         error(errctx, "%s: file %s line %d\n", type, file, line);
662     else if (file != NULL)
663         error(errctx, "%s: file %s\n", type, file);
664     else if (name != NULL)
665         error(errctx, "%s: element %s\n", type, name);
666     else
667         error(errctx, "%s\n", type);
668 }
669
670 /**
671  * xsltSetTransformErrorFunc:
672  * @ctxt:  the XSLT transformation context
673  * @ctx:  the new error handling context
674  * @handler:  the new handler function
675  *
676  * Function to reset the handler and the error context for out of
677  * context error messages specific to a given XSLT transromation.
678  *
679  * This simply means that @handler will be called for subsequent
680  * error messages while running the transformation.
681  */
682 void
683 xsltSetTransformErrorFunc(xsltTransformContextPtr ctxt,
684                           void *ctx, xmlGenericErrorFunc handler)
685 {
686     ctxt->error = handler;
687     ctxt->errctx = ctx;
688 }
689
690 /**
691  * xsltTransformError:
692  * @ctxt:  an XSLT transformation context
693  * @style:  the XSLT stylesheet used
694  * @node:  the current node in the stylesheet
695  * @msg:  the message to display/transmit
696  * @...:  extra parameters for the message display
697  *
698  * Display and format an error messages, gives file, line, position and
699  * extra parameters, will use the specific transformation context if available
700  */
701 void
702 xsltTransformError(xsltTransformContextPtr ctxt,
703                    xsltStylesheetPtr style,
704                    xmlNodePtr node,
705                    const char *msg, ...) {
706     xmlGenericErrorFunc error = xsltGenericError;
707     void *errctx = xsltGenericErrorContext;
708     char * str;
709
710     if (ctxt != NULL) {
711         if (ctxt->state == XSLT_STATE_OK)
712             ctxt->state = XSLT_STATE_ERROR;
713         if (ctxt->error != NULL) {
714             error = ctxt->error;
715             errctx = ctxt->errctx;
716         }
717     }
718     if ((node == NULL) && (ctxt != NULL))
719         node = ctxt->inst;
720     xsltPrintErrorContext(ctxt, style, node);
721     XSLT_GET_VAR_STR(msg, str);
722     error(errctx, "%s", str);
723     if (str != NULL)
724         xmlFree(str);
725 }
726
727 /************************************************************************
728  *                                                                      *
729  *                              QNames                                  *
730  *                                                                      *
731  ************************************************************************/
732
733 /**
734  * xsltSplitQName:
735  * @dict: a dictionary
736  * @name:  the full QName
737  * @prefix: the return value
738  *
739  * Split QNames into prefix and local names, both allocated from a dictionary.
740  *
741  * Returns: the localname or NULL in case of error.
742  */
743 const xmlChar *
744 xsltSplitQName(xmlDictPtr dict, const xmlChar *name, const xmlChar **prefix) {
745     int len = 0;
746     const xmlChar *ret = NULL;
747
748     *prefix = NULL;
749     if ((name == NULL) || (dict == NULL)) return(NULL);
750     if (name[0] == ':')
751         return(xmlDictLookup(dict, name, -1));
752     while ((name[len] != 0) && (name[len] != ':')) len++;
753     if (name[len] == 0) return(xmlDictLookup(dict, name, -1));
754     *prefix = xmlDictLookup(dict, name, len);
755     ret = xmlDictLookup(dict, &name[len + 1], -1);
756     return(ret);
757 }
758
759 /**
760  * xsltGetQNameURI:
761  * @node:  the node holding the QName
762  * @name:  pointer to the initial QName value
763  *
764  * This function analyzes @name, if the name contains a prefix,
765  * the function seaches the associated namespace in scope for it.
766  * It will also replace @name value with the NCName, the old value being
767  * freed.
768  * Errors in the prefix lookup are signalled by setting @name to NULL.
769  *
770  * NOTE: the namespace returned is a pointer to the place where it is
771  *       defined and hence has the same lifespan as the document holding it.
772  *
773  * Returns the namespace URI if there is a prefix, or NULL if @name is
774  *         not prefixed.
775  */
776 const xmlChar *
777 xsltGetQNameURI(xmlNodePtr node, xmlChar ** name)
778 {
779     int len = 0;
780     xmlChar *qname;
781     xmlNsPtr ns;
782
783     if (name == NULL)
784         return(NULL);
785     qname = *name;
786     if ((qname == NULL) || (*qname == 0))
787         return(NULL);
788     if (node == NULL) {
789         xsltGenericError(xsltGenericErrorContext,
790                          "QName: no element for namespace lookup %s\n",
791                          qname);
792         xmlFree(qname);
793         *name = NULL;
794         return(NULL);
795     }
796
797     /* nasty but valid */
798     if (qname[0] == ':')
799         return(NULL);
800
801     /*
802      * we are not trying to validate but just to cut, and yes it will
803      * work even if this is a set of UTF-8 encoded chars
804      */
805     while ((qname[len] != 0) && (qname[len] != ':'))
806         len++;
807
808     if (qname[len] == 0)
809         return(NULL);
810
811     /*
812      * handle xml: separately, this one is magical
813      */
814     if ((qname[0] == 'x') && (qname[1] == 'm') &&
815         (qname[2] == 'l') && (qname[3] == ':')) {
816         if (qname[4] == 0)
817             return(NULL);
818         *name = xmlStrdup(&qname[4]);
819         xmlFree(qname);
820         return(XML_XML_NAMESPACE);
821     }
822
823     qname[len] = 0;
824     ns = xmlSearchNs(node->doc, node, qname);
825     if (ns == NULL) {
826         xsltGenericError(xsltGenericErrorContext,
827                 "%s:%s : no namespace bound to prefix %s\n",
828                          qname, &qname[len + 1], qname);
829         *name = NULL;
830         xmlFree(qname);
831         return(NULL);
832     }
833     *name = xmlStrdup(&qname[len + 1]);
834     xmlFree(qname);
835     return(ns->href);
836 }
837
838 /**
839  * xsltGetQNameURI2:
840  * @style:  stylesheet pointer
841  * @node:   the node holding the QName
842  * @name:   pointer to the initial QName value
843  *
844  * This function is similar to xsltGetQNameURI, but is used when
845  * @name is a dictionary entry.
846  *
847  * Returns the namespace URI if there is a prefix, or NULL if @name is
848  * not prefixed.
849  */
850 const xmlChar *
851 xsltGetQNameURI2(xsltStylesheetPtr style, xmlNodePtr node,
852                  const xmlChar **name) {
853     int len = 0;
854     xmlChar *qname;
855     xmlNsPtr ns;
856
857     if (name == NULL)
858         return(NULL);
859     qname = (xmlChar *)*name;
860     if ((qname == NULL) || (*qname == 0))
861         return(NULL);
862     if (node == NULL) {
863         xsltGenericError(xsltGenericErrorContext,
864                          "QName: no element for namespace lookup %s\n",
865                           qname);
866         *name = NULL;
867         return(NULL);
868     }
869
870     /*
871      * we are not trying to validate but just to cut, and yes it will
872      * work even if this is a set of UTF-8 encoded chars
873      */
874     while ((qname[len] != 0) && (qname[len] != ':'))
875         len++;
876
877     if (qname[len] == 0)
878         return(NULL);
879
880     /*
881      * handle xml: separately, this one is magical
882      */
883     if ((qname[0] == 'x') && (qname[1] == 'm') &&
884         (qname[2] == 'l') && (qname[3] == ':')) {
885         if (qname[4] == 0)
886             return(NULL);
887         *name = xmlDictLookup(style->dict, &qname[4], -1);
888         return(XML_XML_NAMESPACE);
889     }
890
891     qname = xmlStrndup(*name, len);
892     ns = xmlSearchNs(node->doc, node, qname);
893     if (ns == NULL) {
894         if (style) {
895             xsltTransformError(NULL, style, node,
896                 "No namespace bound to prefix '%s'.\n",
897                 qname);
898             style->errors++;
899         } else {
900             xsltGenericError(xsltGenericErrorContext,
901                 "%s : no namespace bound to prefix %s\n",
902                 *name, qname);
903         }
904         *name = NULL;
905         xmlFree(qname);
906         return(NULL);
907     }
908     *name = xmlDictLookup(style->dict, (*name)+len+1, -1);
909     xmlFree(qname);
910     return(ns->href);
911 }
912
913 /************************************************************************
914  *                                                                      *
915  *                              Sorting                                 *
916  *                                                                      *
917  ************************************************************************/
918
919 /**
920  * xsltDocumentSortFunction:
921  * @list:  the node set
922  *
923  * reorder the current node list @list accordingly to the document order
924  * This function is slow, obsolete and should not be used anymore.
925  */
926 void
927 xsltDocumentSortFunction(xmlNodeSetPtr list) {
928     int i, j;
929     int len, tst;
930     xmlNodePtr node;
931
932     if (list == NULL)
933         return;
934     len = list->nodeNr;
935     if (len <= 1)
936         return;
937     /* TODO: sort is really not optimized, does it needs to ? */
938     for (i = 0;i < len -1;i++) {
939         for (j = i + 1; j < len; j++) {
940             tst = xmlXPathCmpNodes(list->nodeTab[i], list->nodeTab[j]);
941             if (tst == -1) {
942                 node = list->nodeTab[i];
943                 list->nodeTab[i] = list->nodeTab[j];
944                 list->nodeTab[j] = node;
945             }
946         }
947     }
948 }
949
950 /**
951  * xsltComputeSortResult:
952  * @ctxt:  a XSLT process context
953  * @sort:  node list
954  *
955  * reorder the current node list accordingly to the set of sorting
956  * requirement provided by the array of nodes.
957  *
958  * Returns a ordered XPath nodeset or NULL in case of error.
959  */
960 xmlXPathObjectPtr *
961 xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) {
962 #ifdef XSLT_REFACTORED
963     xsltStyleItemSortPtr comp;
964 #else
965     xsltStylePreCompPtr comp;
966 #endif
967     xmlXPathObjectPtr *results = NULL;
968     xmlNodeSetPtr list = NULL;
969     xmlXPathObjectPtr res;
970     int len = 0;
971     int i;
972     xmlNodePtr oldNode;
973     xmlNodePtr oldInst;
974     int oldPos, oldSize ;
975     int oldNsNr;
976     xmlNsPtr *oldNamespaces;
977
978     comp = sort->psvi;
979     if (comp == NULL) {
980         xsltGenericError(xsltGenericErrorContext,
981              "xsl:sort : compilation failed\n");
982         return(NULL);
983     }
984
985     if ((comp->select == NULL) || (comp->comp == NULL))
986         return(NULL);
987
988     list = ctxt->nodeList;
989     if ((list == NULL) || (list->nodeNr <= 1))
990         return(NULL);
991
992     len = list->nodeNr;
993
994     /* TODO: xsl:sort lang attribute */
995     /* TODO: xsl:sort case-order attribute */
996
997
998     results = xmlMalloc(len * sizeof(xmlXPathObjectPtr));
999     if (results == NULL) {
1000         xsltGenericError(xsltGenericErrorContext,
1001              "xsltComputeSortResult: memory allocation failure\n");
1002         return(NULL);
1003     }
1004
1005     oldNode = ctxt->node;
1006     oldInst = ctxt->inst;
1007     oldPos = ctxt->xpathCtxt->proximityPosition;
1008     oldSize = ctxt->xpathCtxt->contextSize;
1009     oldNsNr = ctxt->xpathCtxt->nsNr;
1010     oldNamespaces = ctxt->xpathCtxt->namespaces;
1011     for (i = 0;i < len;i++) {
1012         ctxt->inst = sort;
1013         ctxt->xpathCtxt->contextSize = len;
1014         ctxt->xpathCtxt->proximityPosition = i + 1;
1015         ctxt->node = list->nodeTab[i];
1016         ctxt->xpathCtxt->node = ctxt->node;
1017 #ifdef XSLT_REFACTORED
1018         if (comp->inScopeNs != NULL) {
1019             ctxt->xpathCtxt->namespaces = comp->inScopeNs->list;
1020             ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber;
1021         } else {
1022             ctxt->xpathCtxt->namespaces = NULL;
1023             ctxt->xpathCtxt->nsNr = 0;
1024         }
1025 #else
1026         ctxt->xpathCtxt->namespaces = comp->nsList;
1027         ctxt->xpathCtxt->nsNr = comp->nsNr;
1028 #endif
1029         res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
1030         if (res != NULL) {
1031             if (res->type != XPATH_STRING)
1032                 res = xmlXPathConvertString(res);
1033             if (comp->number)
1034                 res = xmlXPathConvertNumber(res);
1035             res->index = i;     /* Save original pos for dupl resolv */
1036             if (comp->number) {
1037                 if (res->type == XPATH_NUMBER) {
1038                     results[i] = res;
1039                 } else {
1040 #ifdef WITH_XSLT_DEBUG_PROCESS
1041                     xsltGenericDebug(xsltGenericDebugContext,
1042                         "xsltComputeSortResult: select didn't evaluate to a number\n");
1043 #endif
1044                     results[i] = NULL;
1045                 }
1046             } else {
1047                 if (res->type == XPATH_STRING) {
1048                     if (comp->locale != (xsltLocale)0) {
1049                         xmlChar *str = res->stringval;
1050                         res->stringval = (xmlChar *) xsltStrxfrm(comp->locale, str);
1051                         xmlFree(str);
1052                     }
1053
1054                     results[i] = res;
1055                 } else {
1056 #ifdef WITH_XSLT_DEBUG_PROCESS
1057                     xsltGenericDebug(xsltGenericDebugContext,
1058                         "xsltComputeSortResult: select didn't evaluate to a string\n");
1059 #endif
1060                     results[i] = NULL;
1061                 }
1062             }
1063         } else {
1064             ctxt->state = XSLT_STATE_STOPPED;
1065             results[i] = NULL;
1066         }
1067     }
1068     ctxt->node = oldNode;
1069     ctxt->inst = oldInst;
1070     ctxt->xpathCtxt->contextSize = oldSize;
1071     ctxt->xpathCtxt->proximityPosition = oldPos;
1072     ctxt->xpathCtxt->nsNr = oldNsNr;
1073     ctxt->xpathCtxt->namespaces = oldNamespaces;
1074
1075     return(results);
1076 }
1077
1078 /**
1079  * xsltDefaultSortFunction:
1080  * @ctxt:  a XSLT process context
1081  * @sorts:  array of sort nodes
1082  * @nbsorts:  the number of sorts in the array
1083  *
1084  * reorder the current node list accordingly to the set of sorting
1085  * requirement provided by the arry of nodes.
1086  */
1087 void
1088 xsltDefaultSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts,
1089                    int nbsorts) {
1090 #ifdef XSLT_REFACTORED
1091     xsltStyleItemSortPtr comp;
1092 #else
1093     xsltStylePreCompPtr comp;
1094 #endif
1095     xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT];
1096     xmlXPathObjectPtr *results = NULL, *res;
1097     xmlNodeSetPtr list = NULL;
1098     int descending, number, desc, numb;
1099     int len = 0;
1100     int i, j, incr;
1101     int tst;
1102     int depth;
1103     xmlNodePtr node;
1104     xmlXPathObjectPtr tmp;
1105     int tempstype[XSLT_MAX_SORT], temporder[XSLT_MAX_SORT];
1106
1107     if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) ||
1108         (nbsorts >= XSLT_MAX_SORT))
1109         return;
1110     if (sorts[0] == NULL)
1111         return;
1112     comp = sorts[0]->psvi;
1113     if (comp == NULL)
1114         return;
1115
1116     list = ctxt->nodeList;
1117     if ((list == NULL) || (list->nodeNr <= 1))
1118         return; /* nothing to do */
1119
1120     for (j = 0; j < nbsorts; j++) {
1121         comp = sorts[j]->psvi;
1122         tempstype[j] = 0;
1123         if ((comp->stype == NULL) && (comp->has_stype != 0)) {
1124             comp->stype =
1125                 xsltEvalAttrValueTemplate(ctxt, sorts[j],
1126                                           (const xmlChar *) "data-type",
1127                                           XSLT_NAMESPACE);
1128             if (comp->stype != NULL) {
1129                 tempstype[j] = 1;
1130                 if (xmlStrEqual(comp->stype, (const xmlChar *) "text"))
1131                     comp->number = 0;
1132                 else if (xmlStrEqual(comp->stype, (const xmlChar *) "number"))
1133                     comp->number = 1;
1134                 else {
1135                     xsltTransformError(ctxt, NULL, sorts[j],
1136                           "xsltDoSortFunction: no support for data-type = %s\n",
1137                                      comp->stype);
1138                     comp->number = 0; /* use default */
1139                 }
1140             }
1141         }
1142         temporder[j] = 0;
1143         if ((comp->order == NULL) && (comp->has_order != 0)) {
1144             comp->order = xsltEvalAttrValueTemplate(ctxt, sorts[j],
1145                                                     (const xmlChar *) "order",
1146                                                     XSLT_NAMESPACE);
1147             if (comp->order != NULL) {
1148                 temporder[j] = 1;
1149                 if (xmlStrEqual(comp->order, (const xmlChar *) "ascending"))
1150                     comp->descending = 0;
1151                 else if (xmlStrEqual(comp->order,
1152                                      (const xmlChar *) "descending"))
1153                     comp->descending = 1;
1154                 else {
1155                     xsltTransformError(ctxt, NULL, sorts[j],
1156                              "xsltDoSortFunction: invalid value %s for order\n",
1157                                      comp->order);
1158                     comp->descending = 0; /* use default */
1159                 }
1160             }
1161         }
1162     }
1163
1164     len = list->nodeNr;
1165
1166     resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]);
1167     for (i = 1;i < XSLT_MAX_SORT;i++)
1168         resultsTab[i] = NULL;
1169
1170     results = resultsTab[0];
1171
1172     comp = sorts[0]->psvi;
1173     descending = comp->descending;
1174     number = comp->number;
1175     if (results == NULL)
1176         return;
1177
1178     /* Shell's sort of node-set */
1179     for (incr = len / 2; incr > 0; incr /= 2) {
1180         for (i = incr; i < len; i++) {
1181             j = i - incr;
1182             if (results[i] == NULL)
1183                 continue;
1184
1185             while (j >= 0) {
1186                 if (results[j] == NULL)
1187                     tst = 1;
1188                 else {
1189                     if (number) {
1190                         /* We make NaN smaller than number in accordance
1191                            with XSLT spec */
1192                         if (xmlXPathIsNaN(results[j]->floatval)) {
1193                             if (xmlXPathIsNaN(results[j + incr]->floatval))
1194                                 tst = 0;
1195                             else
1196                                 tst = -1;
1197                         } else if (xmlXPathIsNaN(results[j + incr]->floatval))
1198                             tst = 1;
1199                         else if (results[j]->floatval ==
1200                                 results[j + incr]->floatval)
1201                             tst = 0;
1202                         else if (results[j]->floatval >
1203                                 results[j + incr]->floatval)
1204                             tst = 1;
1205                         else tst = -1;
1206                     } else if(comp->locale != (xsltLocale)0) {
1207                         tst = xsltLocaleStrcmp(
1208                             comp->locale,
1209                             (xsltLocaleChar *) results[j]->stringval,
1210                             (xsltLocaleChar *) results[j + incr]->stringval);
1211                     } else {
1212                         tst = xmlStrcmp(results[j]->stringval,
1213                                      results[j + incr]->stringval);
1214                     }
1215                     if (descending)
1216                         tst = -tst;
1217                 }
1218                 if (tst == 0) {
1219                     /*
1220                      * Okay we need to use multi level sorts
1221                      */
1222                     depth = 1;
1223                     while (depth < nbsorts) {
1224                         if (sorts[depth] == NULL)
1225                             break;
1226                         comp = sorts[depth]->psvi;
1227                         if (comp == NULL)
1228                             break;
1229                         desc = comp->descending;
1230                         numb = comp->number;
1231
1232                         /*
1233                          * Compute the result of the next level for the
1234                          * full set, this might be optimized ... or not
1235                          */
1236                         if (resultsTab[depth] == NULL)
1237                             resultsTab[depth] = xsltComputeSortResult(ctxt,
1238                                                         sorts[depth]);
1239                         res = resultsTab[depth];
1240                         if (res == NULL)
1241                             break;
1242                         if (res[j] == NULL) {
1243                             if (res[j+incr] != NULL)
1244                                 tst = 1;
1245                         } else if (res[j+incr] == NULL) {
1246                             tst = -1;
1247                         } else {
1248                             if (numb) {
1249                                 /* We make NaN smaller than number in
1250                                    accordance with XSLT spec */
1251                                 if (xmlXPathIsNaN(res[j]->floatval)) {
1252                                     if (xmlXPathIsNaN(res[j +
1253                                                 incr]->floatval))
1254                                         tst = 0;
1255                                     else
1256                                         tst = -1;
1257                                 } else if (xmlXPathIsNaN(res[j + incr]->
1258                                                 floatval))
1259                                     tst = 1;
1260                                 else if (res[j]->floatval == res[j + incr]->
1261                                                 floatval)
1262                                     tst = 0;
1263                                 else if (res[j]->floatval >
1264                                         res[j + incr]->floatval)
1265                                     tst = 1;
1266                                 else tst = -1;
1267                             } else if(comp->locale != (xsltLocale)0) {
1268                                 tst = xsltLocaleStrcmp(
1269                                     comp->locale,
1270                                     (xsltLocaleChar *) res[j]->stringval,
1271                                     (xsltLocaleChar *) res[j + incr]->stringval);
1272                             } else {
1273                                 tst = xmlStrcmp(res[j]->stringval,
1274                                              res[j + incr]->stringval);
1275                             }
1276                             if (desc)
1277                                 tst = -tst;
1278                         }
1279
1280                         /*
1281                          * if we still can't differenciate at this level
1282                          * try one level deeper.
1283                          */
1284                         if (tst != 0)
1285                             break;
1286                         depth++;
1287                     }
1288                 }
1289                 if (tst == 0) {
1290                     tst = results[j]->index > results[j + incr]->index;
1291                 }
1292                 if (tst > 0) {
1293                     tmp = results[j];
1294                     results[j] = results[j + incr];
1295                     results[j + incr] = tmp;
1296                     node = list->nodeTab[j];
1297                     list->nodeTab[j] = list->nodeTab[j + incr];
1298                     list->nodeTab[j + incr] = node;
1299                     depth = 1;
1300                     while (depth < nbsorts) {
1301                         if (sorts[depth] == NULL)
1302                             break;
1303                         if (resultsTab[depth] == NULL)
1304                             break;
1305                         res = resultsTab[depth];
1306                         tmp = res[j];
1307                         res[j] = res[j + incr];
1308                         res[j + incr] = tmp;
1309                         depth++;
1310                     }
1311                     j -= incr;
1312                 } else
1313                     break;
1314             }
1315         }
1316     }
1317
1318     for (j = 0; j < nbsorts; j++) {
1319         comp = sorts[j]->psvi;
1320         if (tempstype[j] == 1) {
1321             /* The data-type needs to be recomputed each time */
1322             xmlFree((void *)(comp->stype));
1323             comp->stype = NULL;
1324         }
1325         if (temporder[j] == 1) {
1326             /* The order needs to be recomputed each time */
1327             xmlFree((void *)(comp->order));
1328             comp->order = NULL;
1329         }
1330         if (resultsTab[j] != NULL) {
1331             for (i = 0;i < len;i++)
1332                 xmlXPathFreeObject(resultsTab[j][i]);
1333             xmlFree(resultsTab[j]);
1334         }
1335     }
1336 }
1337
1338
1339 static xsltSortFunc xsltSortFunction = xsltDefaultSortFunction;
1340
1341 /**
1342  * xsltDoSortFunction:
1343  * @ctxt:  a XSLT process context
1344  * @sorts:  array of sort nodes
1345  * @nbsorts:  the number of sorts in the array
1346  *
1347  * reorder the current node list accordingly to the set of sorting
1348  * requirement provided by the arry of nodes.
1349  * This is a wrapper function, the actual function used is specified
1350  * using xsltSetCtxtSortFunc() to set the context specific sort function,
1351  * or xsltSetSortFunc() to set the global sort function.
1352  * If a sort function is set on the context, this will get called.
1353  * Otherwise the global sort function is called.
1354  */
1355 void
1356 xsltDoSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr * sorts,
1357                    int nbsorts)
1358 {
1359     if (ctxt->sortfunc != NULL)
1360         (ctxt->sortfunc)(ctxt, sorts, nbsorts);
1361     else if (xsltSortFunction != NULL)
1362         xsltSortFunction(ctxt, sorts, nbsorts);
1363 }
1364
1365 /**
1366  * xsltSetSortFunc:
1367  * @handler:  the new handler function
1368  *
1369  * Function to reset the global handler for XSLT sorting.
1370  * If the handler is NULL, the default sort function will be used.
1371  */
1372 void
1373 xsltSetSortFunc(xsltSortFunc handler) {
1374     if (handler != NULL)
1375         xsltSortFunction = handler;
1376     else
1377         xsltSortFunction = xsltDefaultSortFunction;
1378 }
1379
1380 /**
1381  * xsltSetCtxtSortFunc:
1382  * @ctxt:  a XSLT process context
1383  * @handler:  the new handler function
1384  *
1385  * Function to set the handler for XSLT sorting
1386  * for the specified context.
1387  * If the handler is NULL, then the global
1388  * sort function will be called
1389  */
1390 void
1391 xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt, xsltSortFunc handler) {
1392     ctxt->sortfunc = handler;
1393 }
1394
1395 /************************************************************************
1396  *                                                                      *
1397  *                              Parsing options                         *
1398  *                                                                      *
1399  ************************************************************************/
1400
1401 /**
1402  * xsltSetCtxtParseOptions:
1403  * @ctxt:  a XSLT process context
1404  * @options:  a combination of libxml2 xmlParserOption
1405  *
1406  * Change the default parser option passed by the XSLT engine to the
1407  * parser when using document() loading.
1408  *
1409  * Returns the previous options or -1 in case of error
1410  */
1411 int
1412 xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt, int options)
1413 {
1414     int oldopts;
1415
1416     if (ctxt == NULL)
1417         return(-1);
1418     oldopts = ctxt->parserOptions;
1419     if (ctxt->xinclude)
1420         oldopts |= XML_PARSE_XINCLUDE;
1421     ctxt->parserOptions = options;
1422     if (options & XML_PARSE_XINCLUDE)
1423         ctxt->xinclude = 1;
1424     else
1425         ctxt->xinclude = 0;
1426     return(oldopts);
1427 }
1428
1429 /************************************************************************
1430  *                                                                      *
1431  *                              Output                                  *
1432  *                                                                      *
1433  ************************************************************************/
1434
1435 /**
1436  * xsltSaveResultTo:
1437  * @buf:  an output buffer
1438  * @result:  the result xmlDocPtr
1439  * @style:  the stylesheet
1440  *
1441  * Save the result @result obtained by applying the @style stylesheet
1442  * to an I/O output channel @buf
1443  *
1444  * Returns the number of byte written or -1 in case of failure.
1445  */
1446 int
1447 xsltSaveResultTo(xmlOutputBufferPtr buf, xmlDocPtr result,
1448                xsltStylesheetPtr style) {
1449     const xmlChar *encoding;
1450     int base;
1451     const xmlChar *method;
1452     int indent;
1453
1454     if ((buf == NULL) || (result == NULL) || (style == NULL))
1455         return(-1);
1456     if ((result->children == NULL) ||
1457         ((result->children->type == XML_DTD_NODE) &&
1458          (result->children->next == NULL)))
1459         return(0);
1460
1461     if ((style->methodURI != NULL) &&
1462         ((style->method == NULL) ||
1463          (!xmlStrEqual(style->method, (const xmlChar *) "xhtml")))) {
1464         xsltGenericError(xsltGenericErrorContext,
1465                 "xsltSaveResultTo : unknown ouput method\n");
1466         return(-1);
1467     }
1468
1469     base = buf->written;
1470
1471     XSLT_GET_IMPORT_PTR(method, style, method)
1472     XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1473     XSLT_GET_IMPORT_INT(indent, style, indent);
1474
1475     if ((method == NULL) && (result->type == XML_HTML_DOCUMENT_NODE))
1476         method = (const xmlChar *) "html";
1477
1478     if ((method != NULL) &&
1479         (xmlStrEqual(method, (const xmlChar *) "html"))) {
1480         if (encoding != NULL) {
1481             htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1482         } else {
1483             htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
1484         }
1485         if (indent == -1)
1486             indent = 1;
1487         htmlDocContentDumpFormatOutput(buf, result, (const char *) encoding,
1488                                        indent);
1489         xmlOutputBufferFlush(buf);
1490     } else if ((method != NULL) &&
1491         (xmlStrEqual(method, (const xmlChar *) "xhtml"))) {
1492         if (encoding != NULL) {
1493             htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1494         } else {
1495             htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
1496         }
1497         htmlDocContentDumpOutput(buf, result, (const char *) encoding);
1498         xmlOutputBufferFlush(buf);
1499     } else if ((method != NULL) &&
1500                (xmlStrEqual(method, (const xmlChar *) "text"))) {
1501         xmlNodePtr cur;
1502
1503         cur = result->children;
1504         while (cur != NULL) {
1505             if (cur->type == XML_TEXT_NODE)
1506                 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1507
1508             /*
1509              * Skip to next node
1510              */
1511             if (cur->children != NULL) {
1512                 if ((cur->children->type != XML_ENTITY_DECL) &&
1513                     (cur->children->type != XML_ENTITY_REF_NODE) &&
1514                     (cur->children->type != XML_ENTITY_NODE)) {
1515                     cur = cur->children;
1516                     continue;
1517                 }
1518             }
1519             if (cur->next != NULL) {
1520                 cur = cur->next;
1521                 continue;
1522             }
1523
1524             do {
1525                 cur = cur->parent;
1526                 if (cur == NULL)
1527                     break;
1528                 if (cur == (xmlNodePtr) style->doc) {
1529                     cur = NULL;
1530                     break;
1531                 }
1532                 if (cur->next != NULL) {
1533                     cur = cur->next;
1534                     break;
1535                 }
1536             } while (cur != NULL);
1537         }
1538         xmlOutputBufferFlush(buf);
1539     } else {
1540         int omitXmlDecl;
1541         int standalone;
1542
1543         XSLT_GET_IMPORT_INT(omitXmlDecl, style, omitXmlDeclaration);
1544         XSLT_GET_IMPORT_INT(standalone, style, standalone);
1545
1546         if (omitXmlDecl != 1) {
1547             xmlOutputBufferWriteString(buf, "<?xml version=");
1548             if (result->version != NULL) {
1549                 xmlOutputBufferWriteString(buf, "\"");
1550                 xmlOutputBufferWriteString(buf, (const char *)result->version);
1551                 xmlOutputBufferWriteString(buf, "\"");
1552             } else
1553                 xmlOutputBufferWriteString(buf, "\"1.0\"");
1554             if (encoding == NULL) {
1555                 if (result->encoding != NULL)
1556                     encoding = result->encoding;
1557                 else if (result->charset != XML_CHAR_ENCODING_UTF8)
1558                     encoding = (const xmlChar *)
1559                                xmlGetCharEncodingName((xmlCharEncoding)
1560                                                       result->charset);
1561             }
1562             if (encoding != NULL) {
1563                 xmlOutputBufferWriteString(buf, " encoding=");
1564                 xmlOutputBufferWriteString(buf, "\"");
1565                 xmlOutputBufferWriteString(buf, (const char *) encoding);
1566                 xmlOutputBufferWriteString(buf, "\"");
1567             }
1568             switch (standalone) {
1569                 case 0:
1570                     xmlOutputBufferWriteString(buf, " standalone=\"no\"");
1571                     break;
1572                 case 1:
1573                     xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
1574                     break;
1575                 default:
1576                     break;
1577             }
1578             xmlOutputBufferWriteString(buf, "?>\n");
1579         }
1580         if (result->children != NULL) {
1581             xmlNodePtr child = result->children;
1582
1583             while (child != NULL) {
1584                 xmlNodeDumpOutput(buf, result, child, 0, (indent == 1),
1585                                   (const char *) encoding);
1586                 if (indent && ((child->type == XML_DTD_NODE) ||
1587                     ((child->type == XML_COMMENT_NODE) &&
1588                      (child->next != NULL))))
1589                     xmlOutputBufferWriteString(buf, "\n");
1590                 child = child->next;
1591             }
1592             if (indent)
1593                         xmlOutputBufferWriteString(buf, "\n");
1594         }
1595         xmlOutputBufferFlush(buf);
1596     }
1597     return(buf->written - base);
1598 }
1599
1600 /**
1601  * xsltSaveResultToFilename:
1602  * @URL:  a filename or URL
1603  * @result:  the result xmlDocPtr
1604  * @style:  the stylesheet
1605  * @compression:  the compression factor (0 - 9 included)
1606  *
1607  * Save the result @result obtained by applying the @style stylesheet
1608  * to a file or @URL
1609  *
1610  * Returns the number of byte written or -1 in case of failure.
1611  */
1612 int
1613 xsltSaveResultToFilename(const char *URL, xmlDocPtr result,
1614                          xsltStylesheetPtr style, int compression) {
1615     xmlOutputBufferPtr buf;
1616     const xmlChar *encoding;
1617     int ret;
1618
1619     if ((URL == NULL) || (result == NULL) || (style == NULL))
1620         return(-1);
1621     if (result->children == NULL)
1622         return(0);
1623
1624     XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1625     if (encoding != NULL) {
1626         xmlCharEncodingHandlerPtr encoder;
1627
1628         encoder = xmlFindCharEncodingHandler((char *)encoding);
1629         if ((encoder != NULL) &&
1630             (xmlStrEqual((const xmlChar *)encoder->name,
1631                          (const xmlChar *) "UTF-8")))
1632             encoder = NULL;
1633         buf = xmlOutputBufferCreateFilename(URL, encoder, compression);
1634     } else {
1635         buf = xmlOutputBufferCreateFilename(URL, NULL, compression);
1636     }
1637     if (buf == NULL)
1638         return(-1);
1639     xsltSaveResultTo(buf, result, style);
1640     ret = xmlOutputBufferClose(buf);
1641     return(ret);
1642 }
1643
1644 /**
1645  * xsltSaveResultToFile:
1646  * @file:  a FILE * I/O
1647  * @result:  the result xmlDocPtr
1648  * @style:  the stylesheet
1649  *
1650  * Save the result @result obtained by applying the @style stylesheet
1651  * to an open FILE * I/O.
1652  * This does not close the FILE @file
1653  *
1654  * Returns the number of bytes written or -1 in case of failure.
1655  */
1656 int
1657 xsltSaveResultToFile(FILE *file, xmlDocPtr result, xsltStylesheetPtr style) {
1658     xmlOutputBufferPtr buf;
1659     const xmlChar *encoding;
1660     int ret;
1661
1662     if ((file == NULL) || (result == NULL) || (style == NULL))
1663         return(-1);
1664     if (result->children == NULL)
1665         return(0);
1666
1667     XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1668     if (encoding != NULL) {
1669         xmlCharEncodingHandlerPtr encoder;
1670
1671         encoder = xmlFindCharEncodingHandler((char *)encoding);
1672         if ((encoder != NULL) &&
1673             (xmlStrEqual((const xmlChar *)encoder->name,
1674                          (const xmlChar *) "UTF-8")))
1675             encoder = NULL;
1676         buf = xmlOutputBufferCreateFile(file, encoder);
1677     } else {
1678         buf = xmlOutputBufferCreateFile(file, NULL);
1679     }
1680
1681     if (buf == NULL)
1682         return(-1);
1683     xsltSaveResultTo(buf, result, style);
1684     ret = xmlOutputBufferClose(buf);
1685     return(ret);
1686 }
1687
1688 /**
1689  * xsltSaveResultToFd:
1690  * @fd:  a file descriptor
1691  * @result:  the result xmlDocPtr
1692  * @style:  the stylesheet
1693  *
1694  * Save the result @result obtained by applying the @style stylesheet
1695  * to an open file descriptor
1696  * This does not close the descriptor.
1697  *
1698  * Returns the number of bytes written or -1 in case of failure.
1699  */
1700 int
1701 xsltSaveResultToFd(int fd, xmlDocPtr result, xsltStylesheetPtr style) {
1702     xmlOutputBufferPtr buf;
1703     const xmlChar *encoding;
1704     int ret;
1705
1706     if ((fd < 0) || (result == NULL) || (style == NULL))
1707         return(-1);
1708     if (result->children == NULL)
1709         return(0);
1710
1711     XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1712     if (encoding != NULL) {
1713         xmlCharEncodingHandlerPtr encoder;
1714
1715         encoder = xmlFindCharEncodingHandler((char *)encoding);
1716         if ((encoder != NULL) &&
1717             (xmlStrEqual((const xmlChar *)encoder->name,
1718                          (const xmlChar *) "UTF-8")))
1719             encoder = NULL;
1720         buf = xmlOutputBufferCreateFd(fd, encoder);
1721     } else {
1722         buf = xmlOutputBufferCreateFd(fd, NULL);
1723     }
1724     if (buf == NULL)
1725         return(-1);
1726     xsltSaveResultTo(buf, result, style);
1727     ret = xmlOutputBufferClose(buf);
1728     return(ret);
1729 }
1730
1731 /**
1732  * xsltSaveResultToString:
1733  * @doc_txt_ptr:  Memory pointer for allocated XML text
1734  * @doc_txt_len:  Length of the generated XML text
1735  * @result:  the result xmlDocPtr
1736  * @style:  the stylesheet
1737  *
1738  * Save the result @result obtained by applying the @style stylesheet
1739  * to a new allocated string.
1740  *
1741  * Returns 0 in case of success and -1 in case of error
1742  */
1743 int
1744 xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len,
1745                        xmlDocPtr result, xsltStylesheetPtr style) {
1746     xmlOutputBufferPtr buf;
1747     const xmlChar *encoding;
1748
1749     *doc_txt_ptr = NULL;
1750     *doc_txt_len = 0;
1751     if (result->children == NULL)
1752         return(0);
1753
1754     XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1755     if (encoding != NULL) {
1756         xmlCharEncodingHandlerPtr encoder;
1757
1758         encoder = xmlFindCharEncodingHandler((char *)encoding);
1759         if ((encoder != NULL) &&
1760             (xmlStrEqual((const xmlChar *)encoder->name,
1761                          (const xmlChar *) "UTF-8")))
1762             encoder = NULL;
1763         buf = xmlAllocOutputBuffer(encoder);
1764     } else {
1765         buf = xmlAllocOutputBuffer(NULL);
1766     }
1767     if (buf == NULL)
1768         return(-1);
1769     xsltSaveResultTo(buf, result, style);
1770 #ifdef LIBXML2_NEW_BUFFER
1771     if (buf->conv != NULL) {
1772         *doc_txt_len = xmlBufUse(buf->conv);
1773         *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->conv), *doc_txt_len);
1774     } else {
1775         *doc_txt_len = xmlBufUse(buf->buffer);
1776         *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->buffer), *doc_txt_len);
1777     }
1778 #else
1779     if (buf->conv != NULL) {
1780         *doc_txt_len = buf->conv->use;
1781         *doc_txt_ptr = xmlStrndup(buf->conv->content, *doc_txt_len);
1782     } else {
1783         *doc_txt_len = buf->buffer->use;
1784         *doc_txt_ptr = xmlStrndup(buf->buffer->content, *doc_txt_len);
1785     }
1786 #endif
1787     (void)xmlOutputBufferClose(buf);
1788     return 0;
1789 }
1790
1791 /************************************************************************
1792  *                                                                      *
1793  *              Generating profiling informations                       *
1794  *                                                                      *
1795  ************************************************************************/
1796
1797 static long calibration = -1;
1798
1799 /**
1800  * xsltCalibrateTimestamps:
1801  *
1802  * Used for to calibrate the xsltTimestamp() function
1803  * Should work if launched at startup and we don't loose our quantum :-)
1804  *
1805  * Returns the number of milliseconds used by xsltTimestamp()
1806  */
1807 #if !defined(XSLT_WIN32_PERFORMANCE_COUNTER) && \
1808     (defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY))
1809 static long
1810 xsltCalibrateTimestamps(void) {
1811     register int i;
1812
1813     for (i = 0;i < 999;i++)
1814         xsltTimestamp();
1815     return(xsltTimestamp() / 1000);
1816 }
1817 #endif
1818
1819 /**
1820  * xsltCalibrateAdjust:
1821  * @delta:  a negative dealy value found
1822  *
1823  * Used for to correct the calibration for xsltTimestamp()
1824  */
1825 void
1826 xsltCalibrateAdjust(long delta) {
1827     calibration += delta;
1828 }
1829
1830 /**
1831  * xsltTimestamp:
1832  *
1833  * Used for gathering profiling data
1834  *
1835  * Returns the number of tenth of milliseconds since the beginning of the
1836  * profiling
1837  */
1838 long
1839 xsltTimestamp(void)
1840 {
1841 #ifdef XSLT_WIN32_PERFORMANCE_COUNTER
1842     BOOL ok;
1843     LARGE_INTEGER performanceCount;
1844     LARGE_INTEGER performanceFrequency;
1845     LONGLONG quadCount;
1846     double seconds;
1847     static LONGLONG startupQuadCount = 0;
1848     static LONGLONG startupQuadFreq = 0;
1849
1850     ok = QueryPerformanceCounter(&performanceCount);
1851     if (!ok)
1852         return 0;
1853     quadCount = performanceCount.QuadPart;
1854     if (calibration < 0) {
1855         calibration = 0;
1856         ok = QueryPerformanceFrequency(&performanceFrequency);
1857         if (!ok)
1858             return 0;
1859         startupQuadFreq = performanceFrequency.QuadPart;
1860         startupQuadCount = quadCount;
1861         return (0);
1862     }
1863     if (startupQuadFreq == 0)
1864         return 0;
1865     seconds = (quadCount - startupQuadCount) / (double) startupQuadFreq;
1866     return (long) (seconds * XSLT_TIMESTAMP_TICS_PER_SEC);
1867
1868 #else /* XSLT_WIN32_PERFORMANCE_COUNTER */
1869 #ifdef HAVE_CLOCK_GETTIME
1870 #  if defined(CLOCK_MONOTONIC)
1871 #    define XSLT_CLOCK CLOCK_MONOTONIC
1872 #  elif defined(CLOCK_HIGHRES)
1873 #    define XSLT_CLOCK CLOCK_HIGHRES
1874 #  else
1875 #    define XSLT_CLOCK CLOCK_REALTIME
1876 #  endif
1877     static struct timespec startup;
1878     struct timespec cur;
1879     long tics;
1880
1881     if (calibration < 0) {
1882         clock_gettime(XSLT_CLOCK, &startup);
1883         calibration = 0;
1884         calibration = xsltCalibrateTimestamps();
1885         clock_gettime(XSLT_CLOCK, &startup);
1886         return (0);
1887     }
1888
1889     clock_gettime(XSLT_CLOCK, &cur);
1890     tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
1891     tics += (cur.tv_nsec - startup.tv_nsec) /
1892                           (1000000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
1893
1894     tics -= calibration;
1895     return(tics);
1896
1897 #elif HAVE_GETTIMEOFDAY
1898     static struct timeval startup;
1899     struct timeval cur;
1900     long tics;
1901
1902     if (calibration < 0) {
1903         gettimeofday(&startup, NULL);
1904         calibration = 0;
1905         calibration = xsltCalibrateTimestamps();
1906         gettimeofday(&startup, NULL);
1907         return (0);
1908     }
1909
1910     gettimeofday(&cur, NULL);
1911     tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
1912     tics += (cur.tv_usec - startup.tv_usec) /
1913                           (1000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
1914
1915     tics -= calibration;
1916     return(tics);
1917 #else
1918
1919     /* Neither gettimeofday() nor Win32 performance counter available */
1920
1921     return (0);
1922
1923 #endif /* HAVE_GETTIMEOFDAY */
1924 #endif /* XSLT_WIN32_PERFORMANCE_COUNTER */
1925 }
1926
1927 static char *
1928 pretty_templ_match(xsltTemplatePtr templ) {
1929   static char dst[1001];
1930   char *src = (char *)templ->match;
1931   int i=0,j;
1932
1933   /* strip white spaces */
1934   for (j=0; i<1000 && src[j]; i++,j++) {
1935       for(;src[j]==' ';j++);
1936       dst[i]=src[j];
1937   }
1938   if(i<998 && templ->mode) {
1939     /* append [mode] */
1940     dst[i++]='[';
1941     src=(char *)templ->mode;
1942     for (j=0; i<999 && src[j]; i++,j++) {
1943       dst[i]=src[j];
1944     }
1945     dst[i++]=']';
1946   }
1947   dst[i]='\0';
1948   return dst;
1949 }
1950
1951 #define MAX_TEMPLATES 10000
1952
1953 /**
1954  * xsltSaveProfiling:
1955  * @ctxt:  an XSLT context
1956  * @output:  a FILE * for saving the informations
1957  *
1958  * Save the profiling informations on @output
1959  */
1960 void
1961 xsltSaveProfiling(xsltTransformContextPtr ctxt, FILE *output) {
1962     int nb, i,j,k,l;
1963     int max;
1964     int total;
1965     unsigned long totalt;
1966     xsltTemplatePtr *templates;
1967     xsltStylesheetPtr style;
1968     xsltTemplatePtr templ1,templ2;
1969     int *childt;
1970
1971     if ((output == NULL) || (ctxt == NULL))
1972         return;
1973     if (ctxt->profile == 0)
1974         return;
1975
1976     nb = 0;
1977     max = MAX_TEMPLATES;
1978     templates = xmlMalloc(max * sizeof(xsltTemplatePtr));
1979     if (templates == NULL)
1980         return;
1981
1982     style = ctxt->style;
1983     while (style != NULL) {
1984         templ1 = style->templates;
1985         while (templ1 != NULL) {
1986             if (nb >= max)
1987                 break;
1988
1989             if (templ1->nbCalls > 0)
1990                 templates[nb++] = templ1;
1991             templ1 = templ1->next;
1992         }
1993
1994         style = xsltNextImport(style);
1995     }
1996
1997     for (i = 0;i < nb -1;i++) {
1998         for (j = i + 1; j < nb; j++) {
1999             if ((templates[i]->time <= templates[j]->time) ||
2000                 ((templates[i]->time == templates[j]->time) &&
2001                  (templates[i]->nbCalls <= templates[j]->nbCalls))) {
2002                 templ1 = templates[j];
2003                 templates[j] = templates[i];
2004                 templates[i] = templ1;
2005             }
2006         }
2007     }
2008
2009
2010     /* print flat profile */
2011
2012     fprintf(output, "%6s%20s%20s%10s  Calls Tot 100us Avg\n\n",
2013             "number", "match", "name", "mode");
2014     total = 0;
2015     totalt = 0;
2016     for (i = 0;i < nb;i++) {
2017          templ1 = templates[i];
2018         fprintf(output, "%5d ", i);
2019         if (templ1->match != NULL) {
2020             if (xmlStrlen(templ1->match) > 20)
2021                 fprintf(output, "%s\n%26s", templ1->match, "");
2022             else
2023                 fprintf(output, "%20s", templ1->match);
2024         } else {
2025             fprintf(output, "%20s", "");
2026         }
2027         if (templ1->name != NULL) {
2028             if (xmlStrlen(templ1->name) > 20)
2029                 fprintf(output, "%s\n%46s", templ1->name, "");
2030             else
2031                 fprintf(output, "%20s", templ1->name);
2032         } else {
2033             fprintf(output, "%20s", "");
2034         }
2035         if (templ1->mode != NULL) {
2036             if (xmlStrlen(templ1->mode) > 10)
2037                 fprintf(output, "%s\n%56s", templ1->mode, "");
2038             else
2039                 fprintf(output, "%10s", templ1->mode);
2040         } else {
2041             fprintf(output, "%10s", "");
2042         }
2043         fprintf(output, " %6d", templ1->nbCalls);
2044         fprintf(output, " %6ld %6ld\n", templ1->time,
2045                 templ1->time / templ1->nbCalls);
2046         total += templ1->nbCalls;
2047         totalt += templ1->time;
2048     }
2049     fprintf(output, "\n%30s%26s %6d %6ld\n", "Total", "", total, totalt);
2050
2051
2052     /* print call graph */
2053
2054     childt = xmlMalloc((nb + 1) * sizeof(int));
2055     if (childt == NULL)
2056         return;
2057
2058     /* precalculate children times */
2059     for (i = 0; i < nb; i++) {
2060         templ1 = templates[i];
2061
2062         childt[i] = 0;
2063         for (k = 0; k < nb; k++) {
2064             templ2 = templates[k];
2065             for (l = 0; l < templ2->templNr; l++) {
2066                 if (templ2->templCalledTab[l] == templ1) {
2067                     childt[i] +=templ2->time;
2068                 }
2069             }
2070         }
2071     }
2072     childt[i] = 0;
2073
2074     fprintf(output, "\nindex %% time    self  children    called     name\n");
2075
2076     for (i = 0; i < nb; i++) {
2077         char ix_str[20], timep_str[20], times_str[20], timec_str[20], called_str[20];
2078         unsigned long t;
2079
2080         templ1 = templates[i];
2081         /* callers */
2082         for (j = 0; j < templ1->templNr; j++) {
2083             templ2 = templ1->templCalledTab[j];
2084             for (k = 0; k < nb; k++) {
2085               if (templates[k] == templ2)
2086                 break;
2087             }
2088             t=templ2?templ2->time:totalt;
2089             snprintf(times_str,sizeof(times_str),"%8.3f",(float)t/XSLT_TIMESTAMP_TICS_PER_SEC);
2090             snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC);
2091             snprintf(called_str,sizeof(called_str),"%6d/%d",
2092                 templ1->templCountTab[j], /* number of times caller calls 'this' */
2093                 templ1->nbCalls);         /* total number of calls to 'this' */
2094
2095             fprintf(output, "             %-8s %-8s %-12s     %s [%d]\n",
2096                 times_str,timec_str,called_str,
2097                 (templ2?(templ2->name?(char *)templ2->name:pretty_templ_match(templ2)):"-"),k);
2098         }
2099         /* this */
2100         snprintf(ix_str,sizeof(ix_str),"[%d]",i);
2101         snprintf(timep_str,sizeof(timep_str),"%6.2f",(float)templ1->time*100.0/totalt);
2102         snprintf(times_str,sizeof(times_str),"%8.3f",(float)templ1->time/XSLT_TIMESTAMP_TICS_PER_SEC);
2103         snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[i]/XSLT_TIMESTAMP_TICS_PER_SEC);
2104         fprintf(output, "%-5s %-6s %-8s %-8s %6d     %s [%d]\n",
2105             ix_str, timep_str,times_str,timec_str,
2106             templ1->nbCalls,
2107             templ1->name?(char *)templ1->name:pretty_templ_match(templ1),i);
2108         /* callees
2109          * - go over templates[0..nb] and their templCalledTab[]
2110          * - print those where we in the the call-stack
2111          */
2112         total = 0;
2113         for (k = 0; k < nb; k++) {
2114             templ2 = templates[k];
2115             for (l = 0; l < templ2->templNr; l++) {
2116                 if (templ2->templCalledTab[l] == templ1) {
2117                     total+=templ2->templCountTab[l];
2118                 }
2119             }
2120         }
2121         for (k = 0; k < nb; k++) {
2122             templ2 = templates[k];
2123             for (l = 0; l < templ2->templNr; l++) {
2124                 if (templ2->templCalledTab[l] == templ1) {
2125                     snprintf(times_str,sizeof(times_str),"%8.3f",(float)templ2->time/XSLT_TIMESTAMP_TICS_PER_SEC);
2126                     snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC);
2127                     snprintf(called_str,sizeof(called_str),"%6d/%d",
2128                         templ2->templCountTab[l], /* number of times 'this' calls callee */
2129                         total);                   /* total number of calls from 'this' */
2130                     fprintf(output, "             %-8s %-8s %-12s     %s [%d]\n",
2131                         times_str,timec_str,called_str,
2132                         templ2->name?(char *)templ2->name:pretty_templ_match(templ2),k);
2133                 }
2134             }
2135         }
2136         fprintf(output, "-----------------------------------------------\n");
2137     }
2138
2139     fprintf(output, "\f\nIndex by function name\n");
2140     for (i = 0; i < nb; i++) {
2141         templ1 = templates[i];
2142         fprintf(output, "[%d] %s (%s:%d)\n",
2143             i, templ1->name?(char *)templ1->name:pretty_templ_match(templ1),
2144             templ1->style->doc->URL,templ1->elem->line);
2145     }
2146
2147     fprintf(output, "\f\n");
2148     xmlFree(childt);
2149
2150     xmlFree(templates);
2151 }
2152
2153 /************************************************************************
2154  *                                                                      *
2155  *              Fetching profiling informations                         *
2156  *                                                                      *
2157  ************************************************************************/
2158
2159 /**
2160  * xsltGetProfileInformation:
2161  * @ctxt:  a transformation context
2162  *
2163  * This function should be called after the transformation completed
2164  * to extract template processing profiling informations if availble.
2165  * The informations are returned as an XML document tree like
2166  * <?xml version="1.0"?>
2167  * <profile>
2168  * <template rank="1" match="*" name=""
2169  *         mode="" calls="6" time="48" average="8"/>
2170  * <template rank="2" match="item2|item3" name=""
2171  *         mode="" calls="10" time="30" average="3"/>
2172  * <template rank="3" match="item1" name=""
2173  *         mode="" calls="5" time="17" average="3"/>
2174  * </profile>
2175  * The caller will need to free up the returned tree with xmlFreeDoc()
2176  *
2177  * Returns the xmlDocPtr corresponding to the result or NULL if not available.
2178  */
2179
2180 xmlDocPtr
2181 xsltGetProfileInformation(xsltTransformContextPtr ctxt)
2182 {
2183     xmlDocPtr ret = NULL;
2184     xmlNodePtr root, child;
2185     char buf[100];
2186
2187     xsltStylesheetPtr style;
2188     xsltTemplatePtr *templates;
2189     xsltTemplatePtr templ;
2190     int nb = 0, max = 0, i, j;
2191
2192     if (!ctxt)
2193         return NULL;
2194
2195     if (!ctxt->profile)
2196         return NULL;
2197
2198     nb = 0;
2199     max = 10000;
2200     templates =
2201         (xsltTemplatePtr *) xmlMalloc(max * sizeof(xsltTemplatePtr));
2202     if (templates == NULL)
2203         return NULL;
2204
2205     /*
2206      * collect all the templates in an array
2207      */
2208     style = ctxt->style;
2209     while (style != NULL) {
2210         templ = style->templates;
2211         while (templ != NULL) {
2212             if (nb >= max)
2213                 break;
2214
2215             if (templ->nbCalls > 0)
2216                 templates[nb++] = templ;
2217             templ = templ->next;
2218         }
2219
2220         style = (xsltStylesheetPtr) xsltNextImport(style);
2221     }
2222
2223     /*
2224      * Sort the array by time spent
2225      */
2226     for (i = 0; i < nb - 1; i++) {
2227         for (j = i + 1; j < nb; j++) {
2228             if ((templates[i]->time <= templates[j]->time) ||
2229                 ((templates[i]->time == templates[j]->time) &&
2230                  (templates[i]->nbCalls <= templates[j]->nbCalls))) {
2231                 templ = templates[j];
2232                 templates[j] = templates[i];
2233                 templates[i] = templ;
2234             }
2235         }
2236     }
2237
2238     /*
2239      * Generate a document corresponding to the results.
2240      */
2241     ret = xmlNewDoc(BAD_CAST "1.0");
2242     root = xmlNewDocNode(ret, NULL, BAD_CAST "profile", NULL);
2243     xmlDocSetRootElement(ret, root);
2244
2245     for (i = 0; i < nb; i++) {
2246         child = xmlNewChild(root, NULL, BAD_CAST "template", NULL);
2247         snprintf(buf, sizeof(buf), "%d", i + 1);
2248         xmlSetProp(child, BAD_CAST "rank", BAD_CAST buf);
2249         xmlSetProp(child, BAD_CAST "match", BAD_CAST templates[i]->match);
2250         xmlSetProp(child, BAD_CAST "name", BAD_CAST templates[i]->name);
2251         xmlSetProp(child, BAD_CAST "mode", BAD_CAST templates[i]->mode);
2252
2253         snprintf(buf, sizeof(buf), "%d", templates[i]->nbCalls);
2254         xmlSetProp(child, BAD_CAST "calls", BAD_CAST buf);
2255
2256         snprintf(buf, sizeof(buf), "%ld", templates[i]->time);
2257         xmlSetProp(child, BAD_CAST "time", BAD_CAST buf);
2258
2259         snprintf(buf, sizeof(buf), "%ld", templates[i]->time / templates[i]->nbCalls);
2260         xmlSetProp(child, BAD_CAST "average", BAD_CAST buf);
2261     };
2262
2263     xmlFree(templates);
2264
2265     return ret;
2266 }
2267
2268 /************************************************************************
2269  *                                                                      *
2270  *              Hooks for libxml2 XPath                                 *
2271  *                                                                      *
2272  ************************************************************************/
2273
2274 /**
2275  * xsltXPathCompileFlags:
2276  * @style: the stylesheet
2277  * @str:  the XPath expression
2278  * @flags: extra compilation flags to pass down to libxml2 XPath
2279  *
2280  * Compile an XPath expression
2281  *
2282  * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2283  *         the caller has to free the object.
2284  */
2285 xmlXPathCompExprPtr
2286 xsltXPathCompileFlags(xsltStylesheetPtr style, const xmlChar *str, int flags) {
2287     xmlXPathContextPtr xpathCtxt;
2288     xmlXPathCompExprPtr ret;
2289
2290     if (style != NULL) {
2291 #ifdef XSLT_REFACTORED_XPATHCOMP
2292         if (XSLT_CCTXT(style)) {
2293             /*
2294             * Proposed by Jerome Pesenti
2295             * --------------------------
2296             * For better efficiency we'll reuse the compilation
2297             * context's XPath context. For the common stylesheet using
2298             * XPath expressions this will reduce compilation time to
2299             * about 50%.
2300             *
2301             * See http://mail.gnome.org/archives/xslt/2006-April/msg00037.html
2302             */
2303             xpathCtxt = XSLT_CCTXT(style)->xpathCtxt;
2304             xpathCtxt->doc = style->doc;
2305         } else
2306             xpathCtxt = xmlXPathNewContext(style->doc);
2307 #else
2308         xpathCtxt = xmlXPathNewContext(style->doc);
2309 #endif
2310         if (xpathCtxt == NULL)
2311             return NULL;
2312         xpathCtxt->dict = style->dict;
2313     } else {
2314         xpathCtxt = xmlXPathNewContext(NULL);
2315         if (xpathCtxt == NULL)
2316             return NULL;
2317     }
2318     xpathCtxt->flags = flags;
2319
2320     /*
2321     * Compile the expression.
2322     */
2323     ret = xmlXPathCtxtCompile(xpathCtxt, str);
2324
2325 #ifdef XSLT_REFACTORED_XPATHCOMP
2326     if ((style == NULL) || (! XSLT_CCTXT(style))) {
2327         xmlXPathFreeContext(xpathCtxt);
2328     }
2329 #else
2330     xmlXPathFreeContext(xpathCtxt);
2331 #endif
2332     /*
2333      * TODO: there is a lot of optimizations which should be possible
2334      *       like variable slot precomputations, function precomputations, etc.
2335      */
2336
2337     return(ret);
2338 }
2339
2340 /**
2341  * xsltXPathCompile:
2342  * @style: the stylesheet
2343  * @str:  the XPath expression
2344  *
2345  * Compile an XPath expression
2346  *
2347  * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2348  *         the caller has to free the object.
2349  */
2350 xmlXPathCompExprPtr
2351 xsltXPathCompile(xsltStylesheetPtr style, const xmlChar *str) {
2352     return(xsltXPathCompileFlags(style, str, 0));
2353 }
2354
2355 /************************************************************************
2356  *                                                                      *
2357  *              Hooks for the debugger                                  *
2358  *                                                                      *
2359  ************************************************************************/
2360
2361 /*
2362  * There is currently only 3 debugging callback defined
2363  * Debugger callbacks are disabled by default
2364  */
2365 #define XSLT_CALLBACK_NUMBER 3
2366
2367 typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks;
2368 typedef xsltDebuggerCallbacks *xsltDebuggerCallbacksPtr;
2369 struct _xsltDebuggerCallbacks {
2370     xsltHandleDebuggerCallback handler;
2371     xsltAddCallCallback add;
2372     xsltDropCallCallback drop;
2373 };
2374
2375 static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks = {
2376     NULL, /* handler */
2377     NULL, /* add */
2378     NULL  /* drop */
2379 };
2380
2381 int xslDebugStatus;
2382
2383 /**
2384  * xsltSetDebuggerStatus:
2385  * @value : the value to be set
2386  *
2387  * This function sets the value of xslDebugStatus.
2388  */
2389 void
2390 xsltSetDebuggerStatus(int value)
2391 {
2392     xslDebugStatus = value;
2393 }
2394
2395 /**
2396  * xsltGetDebuggerStatus:
2397  *
2398  * Get xslDebugStatus.
2399  *
2400  * Returns the value of xslDebugStatus.
2401  */
2402 int
2403 xsltGetDebuggerStatus(void)
2404 {
2405     return(xslDebugStatus);
2406 }
2407
2408 /**
2409  * xsltSetDebuggerCallbacks:
2410  * @no : number of callbacks
2411  * @block : the block of callbacks
2412  *
2413  * This function allow to plug a debugger into the XSLT library
2414  * @block points to a block of memory containing the address of @no
2415  * callback routines.
2416  *
2417  * Returns 0 in case of success and -1 in case of error
2418  */
2419 int
2420 xsltSetDebuggerCallbacks(int no, void *block)
2421 {
2422     xsltDebuggerCallbacksPtr callbacks;
2423
2424     if ((block == NULL) || (no != XSLT_CALLBACK_NUMBER))
2425         return(-1);
2426
2427     callbacks = (xsltDebuggerCallbacksPtr) block;
2428     xsltDebuggerCurrentCallbacks.handler = callbacks->handler;
2429     xsltDebuggerCurrentCallbacks.add  = callbacks->add;
2430     xsltDebuggerCurrentCallbacks.drop  = callbacks->drop;
2431     return(0);
2432 }
2433
2434 /**
2435  * xslHandleDebugger:
2436  * @cur : source node being executed
2437  * @node : data node being processed
2438  * @templ : temlate that applies to node
2439  * @ctxt : the xslt transform context
2440  *
2441  * If either cur or node are a breakpoint, or xslDebugStatus in state
2442  *   where debugging must occcur at this time then transfer control
2443  *   to the xslDebugBreak function
2444  */
2445 void
2446 xslHandleDebugger(xmlNodePtr cur, xmlNodePtr node, xsltTemplatePtr templ,
2447                   xsltTransformContextPtr ctxt)
2448 {
2449     if (xsltDebuggerCurrentCallbacks.handler != NULL)
2450         xsltDebuggerCurrentCallbacks.handler(cur, node, templ, ctxt);
2451 }
2452
2453 /**
2454  * xslAddCall:
2455  * @templ : current template being applied
2456  * @source : the source node being processed
2457  *
2458  * Add template "call" to call stack
2459  * Returns : 1 on sucess 0 otherwise an error may be printed if
2460  *            WITH_XSLT_DEBUG_BREAKPOINTS is defined
2461  */
2462 int
2463 xslAddCall(xsltTemplatePtr templ, xmlNodePtr source)
2464 {
2465     if (xsltDebuggerCurrentCallbacks.add != NULL)
2466         return(xsltDebuggerCurrentCallbacks.add(templ, source));
2467     return(0);
2468 }
2469
2470 /**
2471  * xslDropCall:
2472  *
2473  * Drop the topmost item off the call stack
2474  */
2475 void
2476 xslDropCall(void)
2477 {
2478     if (xsltDebuggerCurrentCallbacks.drop != NULL)
2479         xsltDebuggerCurrentCallbacks.drop();
2480 }
2481