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