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