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