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