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