xref: /reactos/dll/3rdparty/libxslt/xsltutils.c (revision 21ab4d93)
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)
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 *
xsltGetCNsProp(xsltStylesheetPtr style,xmlNodePtr node,const xmlChar * name,const xmlChar * nameSpace)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 *
xsltGetNsProp(xmlNodePtr node,const xmlChar * name,const xmlChar * nameSpace)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
xsltGetUTF8Char(const unsigned char * utf,int * len)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
xsltPointerListAddSize(xsltPointerListPtr list,void * item,int initialSize)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
xsltPointerListCreate(int initialSize)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
xsltPointerListFree(xsltPointerListPtr list)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
xsltPointerListClear(xsltPointerListPtr list)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
xsltMessage(xsltTransformContextPtr ctxt,xmlNodePtr node,xmlNodePtr inst)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)
xsltGenericErrorDefaultFunc(void * ctx ATTRIBUTE_UNUSED,const char * msg,...)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
xsltSetGenericErrorFunc(void * ctx,xmlGenericErrorFunc handler)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)
xsltGenericDebugDefaultFunc(void * ctx ATTRIBUTE_UNUSED,const char * msg,...)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
xsltSetGenericDebugFunc(void * ctx,xmlGenericErrorFunc handler)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
xsltPrintErrorContext(xsltTransformContextPtr ctxt,xsltStylesheetPtr style,xmlNodePtr node)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
xsltSetTransformErrorFunc(xsltTransformContextPtr ctxt,void * ctx,xmlGenericErrorFunc handler)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
xsltTransformError(xsltTransformContextPtr ctxt,xsltStylesheetPtr style,xmlNodePtr node,const char * msg,...)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 *
xsltSplitQName(xmlDictPtr dict,const xmlChar * name,const xmlChar ** prefix)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 *
xsltGetQNameURI(xmlNodePtr node,xmlChar ** name)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 *
xsltGetQNameURI2(xsltStylesheetPtr style,xmlNodePtr node,const xmlChar ** name)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
xsltDocumentSortFunction(xmlNodeSetPtr list)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  * xsltComputeSortResultiInternal:
928  * @ctxt:  a XSLT process context
929  * @sort:  node list
930  * @xfrm:  Transform strings according to locale
931  *
932  * reorder the current node list accordingly to the set of sorting
933  * requirement provided by the array of nodes.
934  *
935  * Returns a ordered XPath nodeset or NULL in case of error.
936  */
937 static xmlXPathObjectPtr *
xsltComputeSortResultInternal(xsltTransformContextPtr ctxt,xmlNodePtr sort,int xfrm)938 xsltComputeSortResultInternal(xsltTransformContextPtr ctxt, xmlNodePtr sort,
939                               int xfrm) {
940 #ifdef XSLT_REFACTORED
941     xsltStyleItemSortPtr comp;
942 #else
943     xsltStylePreCompPtr comp;
944 #endif
945     xmlXPathObjectPtr *results = NULL;
946     xmlNodeSetPtr list = NULL;
947     xmlXPathObjectPtr res;
948     int len = 0;
949     int i;
950     xmlNodePtr oldNode;
951     xmlNodePtr oldInst;
952     int	oldPos, oldSize ;
953     int oldNsNr;
954     xmlNsPtr *oldNamespaces;
955 
956     comp = sort->psvi;
957     if (comp == NULL) {
958 	xsltGenericError(xsltGenericErrorContext,
959 	     "xsl:sort : compilation failed\n");
960 	return(NULL);
961     }
962 
963     if ((comp->select == NULL) || (comp->comp == NULL))
964 	return(NULL);
965 
966     list = ctxt->nodeList;
967     if ((list == NULL) || (list->nodeNr <= 1))
968 	return(NULL);
969 
970     len = list->nodeNr;
971 
972     /* TODO: xsl:sort lang attribute */
973     /* TODO: xsl:sort case-order attribute */
974 
975 
976     results = xmlMalloc(len * sizeof(xmlXPathObjectPtr));
977     if (results == NULL) {
978 	xsltGenericError(xsltGenericErrorContext,
979 	     "xsltComputeSortResult: memory allocation failure\n");
980 	return(NULL);
981     }
982 
983     oldNode = ctxt->node;
984     oldInst = ctxt->inst;
985     oldPos = ctxt->xpathCtxt->proximityPosition;
986     oldSize = ctxt->xpathCtxt->contextSize;
987     oldNsNr = ctxt->xpathCtxt->nsNr;
988     oldNamespaces = ctxt->xpathCtxt->namespaces;
989     for (i = 0;i < len;i++) {
990 	ctxt->inst = sort;
991 	ctxt->xpathCtxt->contextSize = len;
992 	ctxt->xpathCtxt->proximityPosition = i + 1;
993 	ctxt->node = list->nodeTab[i];
994 	ctxt->xpathCtxt->node = ctxt->node;
995 #ifdef XSLT_REFACTORED
996 	if (comp->inScopeNs != NULL) {
997 	    ctxt->xpathCtxt->namespaces = comp->inScopeNs->list;
998 	    ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber;
999 	} else {
1000 	    ctxt->xpathCtxt->namespaces = NULL;
1001 	    ctxt->xpathCtxt->nsNr = 0;
1002 	}
1003 #else
1004 	ctxt->xpathCtxt->namespaces = comp->nsList;
1005 	ctxt->xpathCtxt->nsNr = comp->nsNr;
1006 #endif
1007 	res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
1008 	if (res != NULL) {
1009 	    if (res->type != XPATH_STRING)
1010 		res = xmlXPathConvertString(res);
1011 	    if (comp->number)
1012 		res = xmlXPathConvertNumber(res);
1013 	    res->index = i;	/* Save original pos for dupl resolv */
1014 	    if (comp->number) {
1015 		if (res->type == XPATH_NUMBER) {
1016 		    results[i] = res;
1017 		} else {
1018 #ifdef WITH_XSLT_DEBUG_PROCESS
1019 		    xsltGenericDebug(xsltGenericDebugContext,
1020 			"xsltComputeSortResult: select didn't evaluate to a number\n");
1021 #endif
1022 		    results[i] = NULL;
1023 		}
1024 	    } else {
1025 		if (res->type == XPATH_STRING) {
1026 		    if ((xfrm) && (comp->locale != (xsltLocale)0)) {
1027 			xmlChar *str = res->stringval;
1028 			res->stringval = (xmlChar *) xsltStrxfrm(comp->locale, str);
1029 			xmlFree(str);
1030 		    }
1031 
1032 		    results[i] = res;
1033 		} else {
1034 #ifdef WITH_XSLT_DEBUG_PROCESS
1035 		    xsltGenericDebug(xsltGenericDebugContext,
1036 			"xsltComputeSortResult: select didn't evaluate to a string\n");
1037 #endif
1038 		    results[i] = NULL;
1039 		}
1040 	    }
1041 	} else {
1042 	    ctxt->state = XSLT_STATE_STOPPED;
1043 	    results[i] = NULL;
1044 	}
1045     }
1046     ctxt->node = oldNode;
1047     ctxt->inst = oldInst;
1048     ctxt->xpathCtxt->contextSize = oldSize;
1049     ctxt->xpathCtxt->proximityPosition = oldPos;
1050     ctxt->xpathCtxt->nsNr = oldNsNr;
1051     ctxt->xpathCtxt->namespaces = oldNamespaces;
1052 
1053     return(results);
1054 }
1055 
1056 /**
1057  * xsltComputeSortResult:
1058  * @ctxt:  a XSLT process context
1059  * @sort:  node list
1060  *
1061  * reorder the current node list accordingly to the set of sorting
1062  * requirement provided by the array of nodes.
1063  *
1064  * Returns a ordered XPath nodeset or NULL in case of error.
1065  */
1066 xmlXPathObjectPtr *
xsltComputeSortResult(xsltTransformContextPtr ctxt,xmlNodePtr sort)1067 xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) {
1068     return xsltComputeSortResultInternal(ctxt, sort, /* xfrm */ 0);
1069 }
1070 
1071 /**
1072  * xsltDefaultSortFunction:
1073  * @ctxt:  a XSLT process context
1074  * @sorts:  array of sort nodes
1075  * @nbsorts:  the number of sorts in the array
1076  *
1077  * reorder the current node list accordingly to the set of sorting
1078  * requirement provided by the arry of nodes.
1079  */
1080 void
xsltDefaultSortFunction(xsltTransformContextPtr ctxt,xmlNodePtr * sorts,int nbsorts)1081 xsltDefaultSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts,
1082 	           int nbsorts) {
1083 #ifdef XSLT_REFACTORED
1084     xsltStyleItemSortPtr comp;
1085 #else
1086     xsltStylePreCompPtr comp;
1087 #endif
1088     xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT];
1089     xmlXPathObjectPtr *results = NULL, *res;
1090     xmlNodeSetPtr list = NULL;
1091     int descending, number, desc, numb;
1092     int len = 0;
1093     int i, j, incr;
1094     int tst;
1095     int depth;
1096     xmlNodePtr node;
1097     xmlXPathObjectPtr tmp;
1098     int tempstype[XSLT_MAX_SORT], temporder[XSLT_MAX_SORT],
1099         templang[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 					  NULL);
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 						    NULL);
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 	templang[j] = 0;
1157 	if ((comp->lang == NULL) && (comp->has_lang != 0)) {
1158             xmlChar *lang = xsltEvalAttrValueTemplate(ctxt, sorts[j],
1159 						      (xmlChar *) "lang",
1160 						      NULL);
1161 	    if (lang != NULL) {
1162 		templang[j] = 1;
1163                 comp->locale = xsltNewLocale(lang);
1164                 xmlFree(lang);
1165             }
1166 	}
1167     }
1168 
1169     len = list->nodeNr;
1170 
1171     resultsTab[0] = xsltComputeSortResultInternal(ctxt, sorts[0],
1172                                                   /* xfrm */ 1);
1173     for (i = 1;i < XSLT_MAX_SORT;i++)
1174 	resultsTab[i] = NULL;
1175 
1176     results = resultsTab[0];
1177 
1178     comp = sorts[0]->psvi;
1179     descending = comp->descending;
1180     number = comp->number;
1181     if (results == NULL)
1182 	goto cleanup;
1183 
1184     /* Shell's sort of node-set */
1185     for (incr = len / 2; incr > 0; incr /= 2) {
1186 	for (i = incr; i < len; i++) {
1187 	    j = i - incr;
1188 	    if (results[i] == NULL)
1189 		continue;
1190 
1191 	    while (j >= 0) {
1192 		if (results[j] == NULL)
1193 		    tst = 1;
1194 		else {
1195 		    if (number) {
1196 			/* We make NaN smaller than number in accordance
1197 			   with XSLT spec */
1198 			if (xmlXPathIsNaN(results[j]->floatval)) {
1199 			    if (xmlXPathIsNaN(results[j + incr]->floatval))
1200 				tst = 0;
1201 			    else
1202 				tst = -1;
1203 			} else if (xmlXPathIsNaN(results[j + incr]->floatval))
1204 			    tst = 1;
1205 			else if (results[j]->floatval ==
1206 				results[j + incr]->floatval)
1207 			    tst = 0;
1208 			else if (results[j]->floatval >
1209 				results[j + incr]->floatval)
1210 			    tst = 1;
1211 			else tst = -1;
1212 		    } else if(comp->locale != (xsltLocale)0) {
1213 			tst = xsltLocaleStrcmp(
1214 			    comp->locale,
1215 			    (xsltLocaleChar *) results[j]->stringval,
1216 			    (xsltLocaleChar *) results[j + incr]->stringval);
1217 		    } else {
1218 			tst = xmlStrcmp(results[j]->stringval,
1219 				     results[j + incr]->stringval);
1220 		    }
1221 		    if (descending)
1222 			tst = -tst;
1223 		}
1224 		if (tst == 0) {
1225 		    /*
1226 		     * Okay we need to use multi level sorts
1227 		     */
1228 		    depth = 1;
1229 		    while (depth < nbsorts) {
1230 			if (sorts[depth] == NULL)
1231 			    break;
1232 			comp = sorts[depth]->psvi;
1233 			if (comp == NULL)
1234 			    break;
1235 			desc = comp->descending;
1236 			numb = comp->number;
1237 
1238 			/*
1239 			 * Compute the result of the next level for the
1240 			 * full set, this might be optimized ... or not
1241 			 */
1242 			if (resultsTab[depth] == NULL)
1243 			    resultsTab[depth] =
1244                                 xsltComputeSortResultInternal(ctxt,
1245                                                               sorts[depth],
1246                                                               /* xfrm */ 1);
1247 			res = resultsTab[depth];
1248 			if (res == NULL)
1249 			    break;
1250 			if (res[j] == NULL) {
1251 			    if (res[j+incr] != NULL)
1252 				tst = 1;
1253 			} else if (res[j+incr] == NULL) {
1254 			    tst = -1;
1255 			} else {
1256 			    if (numb) {
1257 				/* We make NaN smaller than number in
1258 				   accordance with XSLT spec */
1259 				if (xmlXPathIsNaN(res[j]->floatval)) {
1260 				    if (xmlXPathIsNaN(res[j +
1261 						incr]->floatval))
1262 					tst = 0;
1263 				    else
1264 				        tst = -1;
1265 				} else if (xmlXPathIsNaN(res[j + incr]->
1266 						floatval))
1267 				    tst = 1;
1268 				else if (res[j]->floatval == res[j + incr]->
1269 						floatval)
1270 				    tst = 0;
1271 				else if (res[j]->floatval >
1272 					res[j + incr]->floatval)
1273 				    tst = 1;
1274 				else tst = -1;
1275 			    } else if(comp->locale != (xsltLocale)0) {
1276 				tst = xsltLocaleStrcmp(
1277 				    comp->locale,
1278 				    (xsltLocaleChar *) res[j]->stringval,
1279 				    (xsltLocaleChar *) res[j + incr]->stringval);
1280 			    } else {
1281 				tst = xmlStrcmp(res[j]->stringval,
1282 					     res[j + incr]->stringval);
1283 			    }
1284 			    if (desc)
1285 				tst = -tst;
1286 			}
1287 
1288 			/*
1289 			 * if we still can't differenciate at this level
1290 			 * try one level deeper.
1291 			 */
1292 			if (tst != 0)
1293 			    break;
1294 			depth++;
1295 		    }
1296 		}
1297 		if (tst == 0) {
1298 		    tst = results[j]->index > results[j + incr]->index;
1299 		}
1300 		if (tst > 0) {
1301 		    tmp = results[j];
1302 		    results[j] = results[j + incr];
1303 		    results[j + incr] = tmp;
1304 		    node = list->nodeTab[j];
1305 		    list->nodeTab[j] = list->nodeTab[j + incr];
1306 		    list->nodeTab[j + incr] = node;
1307 		    depth = 1;
1308 		    while (depth < nbsorts) {
1309 			if (sorts[depth] == NULL)
1310 			    break;
1311 			if (resultsTab[depth] == NULL)
1312 			    break;
1313 			res = resultsTab[depth];
1314 			tmp = res[j];
1315 			res[j] = res[j + incr];
1316 			res[j + incr] = tmp;
1317 			depth++;
1318 		    }
1319 		    j -= incr;
1320 		} else
1321 		    break;
1322 	    }
1323 	}
1324     }
1325 
1326 cleanup:
1327     for (j = 0; j < nbsorts; j++) {
1328 	comp = sorts[j]->psvi;
1329 	if (tempstype[j] == 1) {
1330 	    /* The data-type needs to be recomputed each time */
1331 	    xmlFree((void *)(comp->stype));
1332 	    comp->stype = NULL;
1333 	}
1334 	if (temporder[j] == 1) {
1335 	    /* The order needs to be recomputed each time */
1336 	    xmlFree((void *)(comp->order));
1337 	    comp->order = NULL;
1338 	}
1339 	if (templang[j] == 1) {
1340 	    xsltFreeLocale(comp->locale);
1341 	    comp->locale = (xsltLocale)0;
1342 	}
1343 	if (resultsTab[j] != NULL) {
1344 	    for (i = 0;i < len;i++)
1345 		xmlXPathFreeObject(resultsTab[j][i]);
1346 	    xmlFree(resultsTab[j]);
1347 	}
1348     }
1349 }
1350 
1351 
1352 static xsltSortFunc xsltSortFunction = xsltDefaultSortFunction;
1353 
1354 /**
1355  * xsltDoSortFunction:
1356  * @ctxt:  a XSLT process context
1357  * @sorts:  array of sort nodes
1358  * @nbsorts:  the number of sorts in the array
1359  *
1360  * reorder the current node list accordingly to the set of sorting
1361  * requirement provided by the arry of nodes.
1362  * This is a wrapper function, the actual function used is specified
1363  * using xsltSetCtxtSortFunc() to set the context specific sort function,
1364  * or xsltSetSortFunc() to set the global sort function.
1365  * If a sort function is set on the context, this will get called.
1366  * Otherwise the global sort function is called.
1367  */
1368 void
xsltDoSortFunction(xsltTransformContextPtr ctxt,xmlNodePtr * sorts,int nbsorts)1369 xsltDoSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr * sorts,
1370                    int nbsorts)
1371 {
1372     if (ctxt->sortfunc != NULL)
1373 	(ctxt->sortfunc)(ctxt, sorts, nbsorts);
1374     else if (xsltSortFunction != NULL)
1375         xsltSortFunction(ctxt, sorts, nbsorts);
1376 }
1377 
1378 /**
1379  * xsltSetSortFunc:
1380  * @handler:  the new handler function
1381  *
1382  * Function to reset the global handler for XSLT sorting.
1383  * If the handler is NULL, the default sort function will be used.
1384  */
1385 void
xsltSetSortFunc(xsltSortFunc handler)1386 xsltSetSortFunc(xsltSortFunc handler) {
1387     if (handler != NULL)
1388 	xsltSortFunction = handler;
1389     else
1390 	xsltSortFunction = xsltDefaultSortFunction;
1391 }
1392 
1393 /**
1394  * xsltSetCtxtSortFunc:
1395  * @ctxt:  a XSLT process context
1396  * @handler:  the new handler function
1397  *
1398  * Function to set the handler for XSLT sorting
1399  * for the specified context.
1400  * If the handler is NULL, then the global
1401  * sort function will be called
1402  */
1403 void
xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt,xsltSortFunc handler)1404 xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt, xsltSortFunc handler) {
1405     ctxt->sortfunc = handler;
1406 }
1407 
1408 /************************************************************************
1409  *									*
1410  *				Parsing options				*
1411  *									*
1412  ************************************************************************/
1413 
1414 /**
1415  * xsltSetCtxtParseOptions:
1416  * @ctxt:  a XSLT process context
1417  * @options:  a combination of libxml2 xmlParserOption
1418  *
1419  * Change the default parser option passed by the XSLT engine to the
1420  * parser when using document() loading.
1421  *
1422  * Returns the previous options or -1 in case of error
1423  */
1424 int
xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt,int options)1425 xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt, int options)
1426 {
1427     int oldopts;
1428 
1429     if (ctxt == NULL)
1430         return(-1);
1431     oldopts = ctxt->parserOptions;
1432     if (ctxt->xinclude)
1433         oldopts |= XML_PARSE_XINCLUDE;
1434     ctxt->parserOptions = options;
1435     if (options & XML_PARSE_XINCLUDE)
1436         ctxt->xinclude = 1;
1437     else
1438         ctxt->xinclude = 0;
1439     return(oldopts);
1440 }
1441 
1442 /************************************************************************
1443  *									*
1444  *				Output					*
1445  *									*
1446  ************************************************************************/
1447 
1448 /**
1449  * xsltSaveResultTo:
1450  * @buf:  an output buffer
1451  * @result:  the result xmlDocPtr
1452  * @style:  the stylesheet
1453  *
1454  * Save the result @result obtained by applying the @style stylesheet
1455  * to an I/O output channel @buf
1456  *
1457  * Returns the number of byte written or -1 in case of failure.
1458  */
1459 int
xsltSaveResultTo(xmlOutputBufferPtr buf,xmlDocPtr result,xsltStylesheetPtr style)1460 xsltSaveResultTo(xmlOutputBufferPtr buf, xmlDocPtr result,
1461 	       xsltStylesheetPtr style) {
1462     const xmlChar *encoding;
1463     int base;
1464     const xmlChar *method;
1465     int indent;
1466 
1467     if ((buf == NULL) || (result == NULL) || (style == NULL))
1468 	return(-1);
1469     if ((result->children == NULL) ||
1470 	((result->children->type == XML_DTD_NODE) &&
1471 	 (result->children->next == NULL)))
1472 	return(0);
1473 
1474     if ((style->methodURI != NULL) &&
1475 	((style->method == NULL) ||
1476 	 (!xmlStrEqual(style->method, (const xmlChar *) "xhtml")))) {
1477         xsltGenericError(xsltGenericErrorContext,
1478 		"xsltSaveResultTo : unknown output method\n");
1479         return(-1);
1480     }
1481 
1482     base = buf->written;
1483 
1484     XSLT_GET_IMPORT_PTR(method, style, method)
1485     XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1486     XSLT_GET_IMPORT_INT(indent, style, indent);
1487 
1488     if ((method == NULL) && (result->type == XML_HTML_DOCUMENT_NODE))
1489 	method = (const xmlChar *) "html";
1490 
1491     if ((method != NULL) &&
1492 	(xmlStrEqual(method, (const xmlChar *) "html"))) {
1493 	if (encoding != NULL) {
1494 	    htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1495 	} else {
1496 	    htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
1497 	}
1498 	if (indent == -1)
1499 	    indent = 1;
1500 	htmlDocContentDumpFormatOutput(buf, result, (const char *) encoding,
1501 		                       indent);
1502 	xmlOutputBufferFlush(buf);
1503     } else if ((method != NULL) &&
1504 	(xmlStrEqual(method, (const xmlChar *) "xhtml"))) {
1505 	if (encoding != NULL) {
1506 	    htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1507 	} else {
1508 	    htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
1509 	}
1510 	htmlDocContentDumpOutput(buf, result, (const char *) encoding);
1511 	xmlOutputBufferFlush(buf);
1512     } else if ((method != NULL) &&
1513 	       (xmlStrEqual(method, (const xmlChar *) "text"))) {
1514 	xmlNodePtr cur;
1515 
1516 	cur = result->children;
1517 	while (cur != NULL) {
1518 	    if (cur->type == XML_TEXT_NODE)
1519 		xmlOutputBufferWriteString(buf, (const char *) cur->content);
1520 
1521 	    /*
1522 	     * Skip to next node
1523 	     */
1524 	    if (cur->children != NULL) {
1525 		if ((cur->children->type != XML_ENTITY_DECL) &&
1526 		    (cur->children->type != XML_ENTITY_REF_NODE) &&
1527 		    (cur->children->type != XML_ENTITY_NODE)) {
1528 		    cur = cur->children;
1529 		    continue;
1530 		}
1531 	    }
1532 	    if (cur->next != NULL) {
1533 		cur = cur->next;
1534 		continue;
1535 	    }
1536 
1537 	    do {
1538 		cur = cur->parent;
1539 		if (cur == NULL)
1540 		    break;
1541 		if (cur == (xmlNodePtr) style->doc) {
1542 		    cur = NULL;
1543 		    break;
1544 		}
1545 		if (cur->next != NULL) {
1546 		    cur = cur->next;
1547 		    break;
1548 		}
1549 	    } while (cur != NULL);
1550 	}
1551 	xmlOutputBufferFlush(buf);
1552     } else {
1553 	int omitXmlDecl;
1554 	int standalone;
1555 
1556 	XSLT_GET_IMPORT_INT(omitXmlDecl, style, omitXmlDeclaration);
1557 	XSLT_GET_IMPORT_INT(standalone, style, standalone);
1558 
1559 	if (omitXmlDecl != 1) {
1560 	    xmlOutputBufferWriteString(buf, "<?xml version=");
1561 	    if (result->version != NULL) {
1562 		xmlOutputBufferWriteString(buf, "\"");
1563 		xmlOutputBufferWriteString(buf, (const char *)result->version);
1564 		xmlOutputBufferWriteString(buf, "\"");
1565 	    } else
1566 		xmlOutputBufferWriteString(buf, "\"1.0\"");
1567 	    if (encoding == NULL) {
1568 		if (result->encoding != NULL)
1569 		    encoding = result->encoding;
1570 		else if (result->charset != XML_CHAR_ENCODING_UTF8)
1571 		    encoding = (const xmlChar *)
1572 			       xmlGetCharEncodingName((xmlCharEncoding)
1573 			                              result->charset);
1574 	    }
1575 	    if (encoding != NULL) {
1576 		xmlOutputBufferWriteString(buf, " encoding=");
1577 		xmlOutputBufferWriteString(buf, "\"");
1578 		xmlOutputBufferWriteString(buf, (const char *) encoding);
1579 		xmlOutputBufferWriteString(buf, "\"");
1580 	    }
1581 	    switch (standalone) {
1582 		case 0:
1583 		    xmlOutputBufferWriteString(buf, " standalone=\"no\"");
1584 		    break;
1585 		case 1:
1586 		    xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
1587 		    break;
1588 		default:
1589 		    break;
1590 	    }
1591 	    xmlOutputBufferWriteString(buf, "?>\n");
1592 	}
1593 	if (result->children != NULL) {
1594             xmlNodePtr children = result->children;
1595 	    xmlNodePtr child = children;
1596 
1597             /*
1598              * Hack to avoid quadratic behavior when scanning
1599              * result->children in xmlGetIntSubset called by
1600              * xmlNodeDumpOutput.
1601              */
1602             result->children = NULL;
1603 
1604 	    while (child != NULL) {
1605 		xmlNodeDumpOutput(buf, result, child, 0, (indent == 1),
1606 			          (const char *) encoding);
1607 		if (indent && ((child->type == XML_DTD_NODE) ||
1608 		    ((child->type == XML_COMMENT_NODE) &&
1609 		     (child->next != NULL))))
1610 		    xmlOutputBufferWriteString(buf, "\n");
1611 		child = child->next;
1612 	    }
1613 	    if (indent)
1614 			xmlOutputBufferWriteString(buf, "\n");
1615 
1616             result->children = children;
1617 	}
1618 	xmlOutputBufferFlush(buf);
1619     }
1620     return(buf->written - base);
1621 }
1622 
1623 /**
1624  * xsltSaveResultToFilename:
1625  * @URL:  a filename or URL
1626  * @result:  the result xmlDocPtr
1627  * @style:  the stylesheet
1628  * @compression:  the compression factor (0 - 9 included)
1629  *
1630  * Save the result @result obtained by applying the @style stylesheet
1631  * to a file or @URL
1632  *
1633  * Returns the number of byte written or -1 in case of failure.
1634  */
1635 int
xsltSaveResultToFilename(const char * URL,xmlDocPtr result,xsltStylesheetPtr style,int compression)1636 xsltSaveResultToFilename(const char *URL, xmlDocPtr result,
1637 			 xsltStylesheetPtr style, int compression) {
1638     xmlOutputBufferPtr buf;
1639     const xmlChar *encoding;
1640     int ret;
1641 
1642     if ((URL == NULL) || (result == NULL) || (style == NULL))
1643 	return(-1);
1644     if (result->children == NULL)
1645 	return(0);
1646 
1647     XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1648     if (encoding != NULL) {
1649 	xmlCharEncodingHandlerPtr encoder;
1650 
1651 	encoder = xmlFindCharEncodingHandler((char *)encoding);
1652 	if ((encoder != NULL) &&
1653 	    (xmlStrEqual((const xmlChar *)encoder->name,
1654 			 (const xmlChar *) "UTF-8")))
1655 	    encoder = NULL;
1656 	buf = xmlOutputBufferCreateFilename(URL, encoder, compression);
1657     } else {
1658 	buf = xmlOutputBufferCreateFilename(URL, NULL, compression);
1659     }
1660     if (buf == NULL)
1661 	return(-1);
1662     xsltSaveResultTo(buf, result, style);
1663     ret = xmlOutputBufferClose(buf);
1664     return(ret);
1665 }
1666 
1667 /**
1668  * xsltSaveResultToFile:
1669  * @file:  a FILE * I/O
1670  * @result:  the result xmlDocPtr
1671  * @style:  the stylesheet
1672  *
1673  * Save the result @result obtained by applying the @style stylesheet
1674  * to an open FILE * I/O.
1675  * This does not close the FILE @file
1676  *
1677  * Returns the number of bytes written or -1 in case of failure.
1678  */
1679 int
xsltSaveResultToFile(FILE * file,xmlDocPtr result,xsltStylesheetPtr style)1680 xsltSaveResultToFile(FILE *file, xmlDocPtr result, xsltStylesheetPtr style) {
1681     xmlOutputBufferPtr buf;
1682     const xmlChar *encoding;
1683     int ret;
1684 
1685     if ((file == NULL) || (result == NULL) || (style == NULL))
1686 	return(-1);
1687     if (result->children == NULL)
1688 	return(0);
1689 
1690     XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1691     if (encoding != NULL) {
1692 	xmlCharEncodingHandlerPtr encoder;
1693 
1694 	encoder = xmlFindCharEncodingHandler((char *)encoding);
1695 	if ((encoder != NULL) &&
1696 	    (xmlStrEqual((const xmlChar *)encoder->name,
1697 			 (const xmlChar *) "UTF-8")))
1698 	    encoder = NULL;
1699 	buf = xmlOutputBufferCreateFile(file, encoder);
1700     } else {
1701 	buf = xmlOutputBufferCreateFile(file, NULL);
1702     }
1703 
1704     if (buf == NULL)
1705 	return(-1);
1706     xsltSaveResultTo(buf, result, style);
1707     ret = xmlOutputBufferClose(buf);
1708     return(ret);
1709 }
1710 
1711 /**
1712  * xsltSaveResultToFd:
1713  * @fd:  a file descriptor
1714  * @result:  the result xmlDocPtr
1715  * @style:  the stylesheet
1716  *
1717  * Save the result @result obtained by applying the @style stylesheet
1718  * to an open file descriptor
1719  * This does not close the descriptor.
1720  *
1721  * Returns the number of bytes written or -1 in case of failure.
1722  */
1723 int
xsltSaveResultToFd(int fd,xmlDocPtr result,xsltStylesheetPtr style)1724 xsltSaveResultToFd(int fd, xmlDocPtr result, xsltStylesheetPtr style) {
1725     xmlOutputBufferPtr buf;
1726     const xmlChar *encoding;
1727     int ret;
1728 
1729     if ((fd < 0) || (result == NULL) || (style == NULL))
1730 	return(-1);
1731     if (result->children == NULL)
1732 	return(0);
1733 
1734     XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1735     if (encoding != NULL) {
1736 	xmlCharEncodingHandlerPtr encoder;
1737 
1738 	encoder = xmlFindCharEncodingHandler((char *)encoding);
1739 	if ((encoder != NULL) &&
1740 	    (xmlStrEqual((const xmlChar *)encoder->name,
1741 			 (const xmlChar *) "UTF-8")))
1742 	    encoder = NULL;
1743 	buf = xmlOutputBufferCreateFd(fd, encoder);
1744     } else {
1745 	buf = xmlOutputBufferCreateFd(fd, NULL);
1746     }
1747     if (buf == NULL)
1748 	return(-1);
1749     xsltSaveResultTo(buf, result, style);
1750     ret = xmlOutputBufferClose(buf);
1751     return(ret);
1752 }
1753 
1754 /**
1755  * xsltSaveResultToString:
1756  * @doc_txt_ptr:  Memory pointer for allocated XML text
1757  * @doc_txt_len:  Length of the generated XML text
1758  * @result:  the result xmlDocPtr
1759  * @style:  the stylesheet
1760  *
1761  * Save the result @result obtained by applying the @style stylesheet
1762  * to a new allocated string.
1763  *
1764  * Returns 0 in case of success and -1 in case of error
1765  */
1766 int
xsltSaveResultToString(xmlChar ** doc_txt_ptr,int * doc_txt_len,xmlDocPtr result,xsltStylesheetPtr style)1767 xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len,
1768 		       xmlDocPtr result, xsltStylesheetPtr style) {
1769     xmlOutputBufferPtr buf;
1770     const xmlChar *encoding;
1771 
1772     *doc_txt_ptr = NULL;
1773     *doc_txt_len = 0;
1774     if (result->children == NULL)
1775 	return(0);
1776 
1777     XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1778     if (encoding != NULL) {
1779 	xmlCharEncodingHandlerPtr encoder;
1780 
1781 	encoder = xmlFindCharEncodingHandler((char *)encoding);
1782 	if ((encoder != NULL) &&
1783 	    (xmlStrEqual((const xmlChar *)encoder->name,
1784 			 (const xmlChar *) "UTF-8")))
1785 	    encoder = NULL;
1786 	buf = xmlAllocOutputBuffer(encoder);
1787     } else {
1788 	buf = xmlAllocOutputBuffer(NULL);
1789     }
1790     if (buf == NULL)
1791 	return(-1);
1792     xsltSaveResultTo(buf, result, style);
1793 #ifdef LIBXML2_NEW_BUFFER
1794     if (buf->conv != NULL) {
1795 	*doc_txt_len = xmlBufUse(buf->conv);
1796 	*doc_txt_ptr = xmlStrndup(xmlBufContent(buf->conv), *doc_txt_len);
1797     } else {
1798 	*doc_txt_len = xmlBufUse(buf->buffer);
1799 	*doc_txt_ptr = xmlStrndup(xmlBufContent(buf->buffer), *doc_txt_len);
1800     }
1801 #else
1802     if (buf->conv != NULL) {
1803 	*doc_txt_len = buf->conv->use;
1804 	*doc_txt_ptr = xmlStrndup(buf->conv->content, *doc_txt_len);
1805     } else {
1806 	*doc_txt_len = buf->buffer->use;
1807 	*doc_txt_ptr = xmlStrndup(buf->buffer->content, *doc_txt_len);
1808     }
1809 #endif
1810     (void)xmlOutputBufferClose(buf);
1811     return 0;
1812 }
1813 
1814 #ifdef WITH_PROFILER
1815 
1816 /************************************************************************
1817  *									*
1818  *		Generating profiling information			*
1819  *									*
1820  ************************************************************************/
1821 
1822 static long calibration = -1;
1823 
1824 /**
1825  * xsltCalibrateTimestamps:
1826  *
1827  * Used for to calibrate the xsltTimestamp() function
1828  * Should work if launched at startup and we don't loose our quantum :-)
1829  *
1830  * Returns the number of milliseconds used by xsltTimestamp()
1831  */
1832 #if !defined(XSLT_WIN32_PERFORMANCE_COUNTER) && \
1833     (defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY))
1834 static long
xsltCalibrateTimestamps(void)1835 xsltCalibrateTimestamps(void) {
1836     register int i;
1837 
1838     for (i = 0;i < 999;i++)
1839 	xsltTimestamp();
1840     return(xsltTimestamp() / 1000);
1841 }
1842 #endif
1843 
1844 /**
1845  * xsltCalibrateAdjust:
1846  * @delta:  a negative dealy value found
1847  *
1848  * Used for to correct the calibration for xsltTimestamp()
1849  */
1850 void
xsltCalibrateAdjust(long delta)1851 xsltCalibrateAdjust(long delta) {
1852     calibration += delta;
1853 }
1854 
1855 /**
1856  * xsltTimestamp:
1857  *
1858  * Used for gathering profiling data
1859  *
1860  * Returns the number of tenth of milliseconds since the beginning of the
1861  * profiling
1862  */
1863 long
xsltTimestamp(void)1864 xsltTimestamp(void)
1865 {
1866 #ifdef XSLT_WIN32_PERFORMANCE_COUNTER
1867     BOOL ok;
1868     LARGE_INTEGER performanceCount;
1869     LARGE_INTEGER performanceFrequency;
1870     LONGLONG quadCount;
1871     double seconds;
1872     static LONGLONG startupQuadCount = 0;
1873     static LONGLONG startupQuadFreq = 0;
1874 
1875     ok = QueryPerformanceCounter(&performanceCount);
1876     if (!ok)
1877         return 0;
1878     quadCount = performanceCount.QuadPart;
1879     if (calibration < 0) {
1880         calibration = 0;
1881         ok = QueryPerformanceFrequency(&performanceFrequency);
1882         if (!ok)
1883             return 0;
1884         startupQuadFreq = performanceFrequency.QuadPart;
1885         startupQuadCount = quadCount;
1886         return (0);
1887     }
1888     if (startupQuadFreq == 0)
1889         return 0;
1890     seconds = (quadCount - startupQuadCount) / (double) startupQuadFreq;
1891     return (long) (seconds * XSLT_TIMESTAMP_TICS_PER_SEC);
1892 
1893 #else /* XSLT_WIN32_PERFORMANCE_COUNTER */
1894 #ifdef HAVE_CLOCK_GETTIME
1895 #  if defined(CLOCK_MONOTONIC)
1896 #    define XSLT_CLOCK CLOCK_MONOTONIC
1897 #  elif defined(CLOCK_HIGHRES)
1898 #    define XSLT_CLOCK CLOCK_HIGHRES
1899 #  else
1900 #    define XSLT_CLOCK CLOCK_REALTIME
1901 #  endif
1902     static struct timespec startup;
1903     struct timespec cur;
1904     long tics;
1905 
1906     if (calibration < 0) {
1907         clock_gettime(XSLT_CLOCK, &startup);
1908         calibration = 0;
1909         calibration = xsltCalibrateTimestamps();
1910         clock_gettime(XSLT_CLOCK, &startup);
1911         return (0);
1912     }
1913 
1914     clock_gettime(XSLT_CLOCK, &cur);
1915     tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
1916     tics += (cur.tv_nsec - startup.tv_nsec) /
1917                           (1000000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
1918 
1919     tics -= calibration;
1920     return(tics);
1921 
1922 #elif HAVE_GETTIMEOFDAY
1923     static struct timeval startup;
1924     struct timeval cur;
1925     long tics;
1926 
1927     if (calibration < 0) {
1928         gettimeofday(&startup, NULL);
1929         calibration = 0;
1930         calibration = xsltCalibrateTimestamps();
1931         gettimeofday(&startup, NULL);
1932         return (0);
1933     }
1934 
1935     gettimeofday(&cur, NULL);
1936     tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
1937     tics += (cur.tv_usec - startup.tv_usec) /
1938                           (1000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
1939 
1940     tics -= calibration;
1941     return(tics);
1942 #else
1943 
1944     /* Neither gettimeofday() nor Win32 performance counter available */
1945 
1946     return (0);
1947 
1948 #endif /* HAVE_GETTIMEOFDAY */
1949 #endif /* XSLT_WIN32_PERFORMANCE_COUNTER */
1950 }
1951 
1952 static char *
pretty_templ_match(xsltTemplatePtr templ)1953 pretty_templ_match(xsltTemplatePtr templ) {
1954   static char dst[1001];
1955   char *src = (char *)templ->match;
1956   int i=0,j;
1957 
1958   /* strip white spaces */
1959   for (j=0; i<1000 && src[j]; i++,j++) {
1960       for(;src[j]==' ';j++);
1961       dst[i]=src[j];
1962   }
1963   if(i<998 && templ->mode) {
1964     /* append [mode] */
1965     dst[i++]='[';
1966     src=(char *)templ->mode;
1967     for (j=0; i<999 && src[j]; i++,j++) {
1968       dst[i]=src[j];
1969     }
1970     dst[i++]=']';
1971   }
1972   dst[i]='\0';
1973   return dst;
1974 }
1975 
1976 #define MAX_TEMPLATES 10000
1977 
1978 /**
1979  * xsltSaveProfiling:
1980  * @ctxt:  an XSLT context
1981  * @output:  a FILE * for saving the information
1982  *
1983  * Save the profiling information on @output
1984  */
1985 void
xsltSaveProfiling(xsltTransformContextPtr ctxt,FILE * output)1986 xsltSaveProfiling(xsltTransformContextPtr ctxt, FILE *output) {
1987     int nb, i,j,k,l;
1988     int max;
1989     int total;
1990     unsigned long totalt;
1991     xsltTemplatePtr *templates;
1992     xsltStylesheetPtr style;
1993     xsltTemplatePtr templ1,templ2;
1994     int *childt;
1995 
1996     if ((output == NULL) || (ctxt == NULL))
1997 	return;
1998     if (ctxt->profile == 0)
1999 	return;
2000 
2001     nb = 0;
2002     max = MAX_TEMPLATES;
2003     templates = xmlMalloc(max * sizeof(xsltTemplatePtr));
2004     if (templates == NULL)
2005 	return;
2006 
2007     style = ctxt->style;
2008     while (style != NULL) {
2009 	templ1 = style->templates;
2010 	while (templ1 != NULL) {
2011 	    if (nb >= max)
2012 		break;
2013 
2014 	    if (templ1->nbCalls > 0)
2015 		templates[nb++] = templ1;
2016 	    templ1 = templ1->next;
2017 	}
2018 
2019 	style = xsltNextImport(style);
2020     }
2021 
2022     for (i = 0;i < nb -1;i++) {
2023 	for (j = i + 1; j < nb; j++) {
2024 	    if ((templates[i]->time <= templates[j]->time) ||
2025 		((templates[i]->time == templates[j]->time) &&
2026 	         (templates[i]->nbCalls <= templates[j]->nbCalls))) {
2027 		templ1 = templates[j];
2028 		templates[j] = templates[i];
2029 		templates[i] = templ1;
2030 	    }
2031 	}
2032     }
2033 
2034 
2035     /* print flat profile */
2036 
2037     fprintf(output, "%6s%20s%20s%10s  Calls Tot 100us Avg\n\n",
2038 	    "number", "match", "name", "mode");
2039     total = 0;
2040     totalt = 0;
2041     for (i = 0;i < nb;i++) {
2042          templ1 = templates[i];
2043 	fprintf(output, "%5d ", i);
2044 	if (templ1->match != NULL) {
2045 	    if (xmlStrlen(templ1->match) > 20)
2046 		fprintf(output, "%s\n%26s", templ1->match, "");
2047 	    else
2048 		fprintf(output, "%20s", templ1->match);
2049 	} else {
2050 	    fprintf(output, "%20s", "");
2051 	}
2052 	if (templ1->name != NULL) {
2053 	    if (xmlStrlen(templ1->name) > 20)
2054 		fprintf(output, "%s\n%46s", templ1->name, "");
2055 	    else
2056 		fprintf(output, "%20s", templ1->name);
2057 	} else {
2058 	    fprintf(output, "%20s", "");
2059 	}
2060 	if (templ1->mode != NULL) {
2061 	    if (xmlStrlen(templ1->mode) > 10)
2062 		fprintf(output, "%s\n%56s", templ1->mode, "");
2063 	    else
2064 		fprintf(output, "%10s", templ1->mode);
2065 	} else {
2066 	    fprintf(output, "%10s", "");
2067 	}
2068 	fprintf(output, " %6d", templ1->nbCalls);
2069 	fprintf(output, " %6ld %6ld\n", templ1->time,
2070 		templ1->time / templ1->nbCalls);
2071 	total += templ1->nbCalls;
2072 	totalt += templ1->time;
2073     }
2074     fprintf(output, "\n%30s%26s %6d %6ld\n", "Total", "", total, totalt);
2075 
2076 
2077     /* print call graph */
2078 
2079     childt = xmlMalloc((nb + 1) * sizeof(int));
2080     if (childt == NULL)
2081 	return;
2082 
2083     /* precalculate children times */
2084     for (i = 0; i < nb; i++) {
2085         templ1 = templates[i];
2086 
2087         childt[i] = 0;
2088         for (k = 0; k < nb; k++) {
2089             templ2 = templates[k];
2090             for (l = 0; l < templ2->templNr; l++) {
2091                 if (templ2->templCalledTab[l] == templ1) {
2092                     childt[i] +=templ2->time;
2093                 }
2094             }
2095         }
2096     }
2097     childt[i] = 0;
2098 
2099     fprintf(output, "\nindex %% time    self  children    called     name\n");
2100 
2101     for (i = 0; i < nb; i++) {
2102         char ix_str[20], timep_str[20], times_str[20], timec_str[20], called_str[20];
2103         unsigned long t;
2104 
2105         templ1 = templates[i];
2106         /* callers */
2107         for (j = 0; j < templ1->templNr; j++) {
2108             templ2 = templ1->templCalledTab[j];
2109             for (k = 0; k < nb; k++) {
2110               if (templates[k] == templ2)
2111                 break;
2112             }
2113             t=templ2?templ2->time:totalt;
2114             snprintf(times_str,sizeof(times_str),"%8.3f",(float)t/XSLT_TIMESTAMP_TICS_PER_SEC);
2115             snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC);
2116             snprintf(called_str,sizeof(called_str),"%6d/%d",
2117                 templ1->templCountTab[j], /* number of times caller calls 'this' */
2118                 templ1->nbCalls);         /* total number of calls to 'this' */
2119 
2120             fprintf(output, "             %-8s %-8s %-12s     %s [%d]\n",
2121                 times_str,timec_str,called_str,
2122                 (templ2?(templ2->name?(char *)templ2->name:pretty_templ_match(templ2)):"-"),k);
2123         }
2124         /* this */
2125         snprintf(ix_str,sizeof(ix_str),"[%d]",i);
2126         snprintf(timep_str,sizeof(timep_str),"%6.2f",(float)templ1->time*100.0/totalt);
2127         snprintf(times_str,sizeof(times_str),"%8.3f",(float)templ1->time/XSLT_TIMESTAMP_TICS_PER_SEC);
2128         snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[i]/XSLT_TIMESTAMP_TICS_PER_SEC);
2129         fprintf(output, "%-5s %-6s %-8s %-8s %6d     %s [%d]\n",
2130             ix_str, timep_str,times_str,timec_str,
2131             templ1->nbCalls,
2132             templ1->name?(char *)templ1->name:pretty_templ_match(templ1),i);
2133         /* callees
2134          * - go over templates[0..nb] and their templCalledTab[]
2135          * - print those where we in the the call-stack
2136          */
2137         total = 0;
2138         for (k = 0; k < nb; k++) {
2139             templ2 = templates[k];
2140             for (l = 0; l < templ2->templNr; l++) {
2141                 if (templ2->templCalledTab[l] == templ1) {
2142                     total+=templ2->templCountTab[l];
2143                 }
2144             }
2145         }
2146         for (k = 0; k < nb; k++) {
2147             templ2 = templates[k];
2148             for (l = 0; l < templ2->templNr; l++) {
2149                 if (templ2->templCalledTab[l] == templ1) {
2150                     snprintf(times_str,sizeof(times_str),"%8.3f",(float)templ2->time/XSLT_TIMESTAMP_TICS_PER_SEC);
2151                     snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC);
2152                     snprintf(called_str,sizeof(called_str),"%6d/%d",
2153                         templ2->templCountTab[l], /* number of times 'this' calls callee */
2154                         total);                   /* total number of calls from 'this' */
2155                     fprintf(output, "             %-8s %-8s %-12s     %s [%d]\n",
2156                         times_str,timec_str,called_str,
2157                         templ2->name?(char *)templ2->name:pretty_templ_match(templ2),k);
2158                 }
2159             }
2160         }
2161         fprintf(output, "-----------------------------------------------\n");
2162     }
2163 
2164     fprintf(output, "\f\nIndex by function name\n");
2165     for (i = 0; i < nb; i++) {
2166         templ1 = templates[i];
2167         fprintf(output, "[%d] %s (%s:%d)\n",
2168             i, templ1->name?(char *)templ1->name:pretty_templ_match(templ1),
2169             templ1->style->doc->URL,templ1->elem->line);
2170     }
2171 
2172     fprintf(output, "\f\n");
2173     xmlFree(childt);
2174 
2175     xmlFree(templates);
2176 }
2177 
2178 /************************************************************************
2179  *									*
2180  *		Fetching profiling information				*
2181  *									*
2182  ************************************************************************/
2183 
2184 /**
2185  * xsltGetProfileInformation:
2186  * @ctxt:  a transformation context
2187  *
2188  * This function should be called after the transformation completed
2189  * to extract template processing profiling information if available.
2190  * The information is returned as an XML document tree like
2191  * <?xml version="1.0"?>
2192  * <profile>
2193  * <template rank="1" match="*" name=""
2194  *         mode="" calls="6" time="48" average="8"/>
2195  * <template rank="2" match="item2|item3" name=""
2196  *         mode="" calls="10" time="30" average="3"/>
2197  * <template rank="3" match="item1" name=""
2198  *         mode="" calls="5" time="17" average="3"/>
2199  * </profile>
2200  * The caller will need to free up the returned tree with xmlFreeDoc()
2201  *
2202  * Returns the xmlDocPtr corresponding to the result or NULL if not available.
2203  */
2204 
2205 xmlDocPtr
xsltGetProfileInformation(xsltTransformContextPtr ctxt)2206 xsltGetProfileInformation(xsltTransformContextPtr ctxt)
2207 {
2208     xmlDocPtr ret = NULL;
2209     xmlNodePtr root, child;
2210     char buf[100];
2211 
2212     xsltStylesheetPtr style;
2213     xsltTemplatePtr *templates;
2214     xsltTemplatePtr templ;
2215     int nb = 0, max = 0, i, j;
2216 
2217     if (!ctxt)
2218         return NULL;
2219 
2220     if (!ctxt->profile)
2221         return NULL;
2222 
2223     nb = 0;
2224     max = 10000;
2225     templates =
2226         (xsltTemplatePtr *) xmlMalloc(max * sizeof(xsltTemplatePtr));
2227     if (templates == NULL)
2228         return NULL;
2229 
2230     /*
2231      * collect all the templates in an array
2232      */
2233     style = ctxt->style;
2234     while (style != NULL) {
2235         templ = style->templates;
2236         while (templ != NULL) {
2237             if (nb >= max)
2238                 break;
2239 
2240             if (templ->nbCalls > 0)
2241                 templates[nb++] = templ;
2242             templ = templ->next;
2243         }
2244 
2245         style = (xsltStylesheetPtr) xsltNextImport(style);
2246     }
2247 
2248     /*
2249      * Sort the array by time spent
2250      */
2251     for (i = 0; i < nb - 1; i++) {
2252         for (j = i + 1; j < nb; j++) {
2253             if ((templates[i]->time <= templates[j]->time) ||
2254                 ((templates[i]->time == templates[j]->time) &&
2255                  (templates[i]->nbCalls <= templates[j]->nbCalls))) {
2256                 templ = templates[j];
2257                 templates[j] = templates[i];
2258                 templates[i] = templ;
2259             }
2260         }
2261     }
2262 
2263     /*
2264      * Generate a document corresponding to the results.
2265      */
2266     ret = xmlNewDoc(BAD_CAST "1.0");
2267     root = xmlNewDocNode(ret, NULL, BAD_CAST "profile", NULL);
2268     xmlDocSetRootElement(ret, root);
2269 
2270     for (i = 0; i < nb; i++) {
2271         child = xmlNewChild(root, NULL, BAD_CAST "template", NULL);
2272         snprintf(buf, sizeof(buf), "%d", i + 1);
2273         xmlSetProp(child, BAD_CAST "rank", BAD_CAST buf);
2274         xmlSetProp(child, BAD_CAST "match", BAD_CAST templates[i]->match);
2275         xmlSetProp(child, BAD_CAST "name", BAD_CAST templates[i]->name);
2276         xmlSetProp(child, BAD_CAST "mode", BAD_CAST templates[i]->mode);
2277 
2278         snprintf(buf, sizeof(buf), "%d", templates[i]->nbCalls);
2279         xmlSetProp(child, BAD_CAST "calls", BAD_CAST buf);
2280 
2281         snprintf(buf, sizeof(buf), "%ld", templates[i]->time);
2282         xmlSetProp(child, BAD_CAST "time", BAD_CAST buf);
2283 
2284         snprintf(buf, sizeof(buf), "%ld", templates[i]->time / templates[i]->nbCalls);
2285         xmlSetProp(child, BAD_CAST "average", BAD_CAST buf);
2286     };
2287 
2288     xmlFree(templates);
2289 
2290     return ret;
2291 }
2292 
2293 #endif /* WITH_PROFILER */
2294 
2295 /************************************************************************
2296  *									*
2297  *		Hooks for libxml2 XPath					*
2298  *									*
2299  ************************************************************************/
2300 
2301 /**
2302  * xsltXPathCompileFlags:
2303  * @style: the stylesheet
2304  * @str:  the XPath expression
2305  * @flags: extra compilation flags to pass down to libxml2 XPath
2306  *
2307  * Compile an XPath expression
2308  *
2309  * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2310  *         the caller has to free the object.
2311  */
2312 xmlXPathCompExprPtr
xsltXPathCompileFlags(xsltStylesheetPtr style,const xmlChar * str,int flags)2313 xsltXPathCompileFlags(xsltStylesheetPtr style, const xmlChar *str, int flags) {
2314     xmlXPathContextPtr xpathCtxt;
2315     xmlXPathCompExprPtr ret;
2316 
2317     if (style != NULL) {
2318         xpathCtxt = style->principal->xpathCtxt;
2319 	if (xpathCtxt == NULL)
2320 	    return NULL;
2321 	xpathCtxt->dict = style->dict;
2322     } else {
2323 	xpathCtxt = xmlXPathNewContext(NULL);
2324 	if (xpathCtxt == NULL)
2325 	    return NULL;
2326     }
2327     xpathCtxt->flags = flags;
2328 
2329     /*
2330     * Compile the expression.
2331     */
2332     ret = xmlXPathCtxtCompile(xpathCtxt, str);
2333 
2334     if (style == NULL) {
2335 	xmlXPathFreeContext(xpathCtxt);
2336     }
2337     /*
2338      * TODO: there is a lot of optimizations which should be possible
2339      *       like variable slot precomputations, function precomputations, etc.
2340      */
2341 
2342     return(ret);
2343 }
2344 
2345 /**
2346  * xsltXPathCompile:
2347  * @style: the stylesheet
2348  * @str:  the XPath expression
2349  *
2350  * Compile an XPath expression
2351  *
2352  * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2353  *         the caller has to free the object.
2354  */
2355 xmlXPathCompExprPtr
xsltXPathCompile(xsltStylesheetPtr style,const xmlChar * str)2356 xsltXPathCompile(xsltStylesheetPtr style, const xmlChar *str) {
2357     return(xsltXPathCompileFlags(style, str, 0));
2358 }
2359 
2360 /************************************************************************
2361  *									*
2362  *		Hooks for the debugger					*
2363  *									*
2364  ************************************************************************/
2365 
2366 int xslDebugStatus;
2367 
2368 /**
2369  * xsltGetDebuggerStatus:
2370  *
2371  * Get xslDebugStatus.
2372  *
2373  * Returns the value of xslDebugStatus.
2374  */
2375 int
xsltGetDebuggerStatus(void)2376 xsltGetDebuggerStatus(void)
2377 {
2378     return(xslDebugStatus);
2379 }
2380 
2381 #ifdef WITH_DEBUGGER
2382 
2383 /*
2384  * There is currently only 3 debugging callback defined
2385  * Debugger callbacks are disabled by default
2386  */
2387 #define XSLT_CALLBACK_NUMBER 3
2388 
2389 typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks;
2390 typedef xsltDebuggerCallbacks *xsltDebuggerCallbacksPtr;
2391 struct _xsltDebuggerCallbacks {
2392     xsltHandleDebuggerCallback handler;
2393     xsltAddCallCallback add;
2394     xsltDropCallCallback drop;
2395 };
2396 
2397 static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks = {
2398     NULL, /* handler */
2399     NULL, /* add */
2400     NULL  /* drop */
2401 };
2402 
2403 /**
2404  * xsltSetDebuggerStatus:
2405  * @value : the value to be set
2406  *
2407  * This function sets the value of xslDebugStatus.
2408  */
2409 void
xsltSetDebuggerStatus(int value)2410 xsltSetDebuggerStatus(int value)
2411 {
2412     xslDebugStatus = value;
2413 }
2414 
2415 /**
2416  * xsltSetDebuggerCallbacks:
2417  * @no : number of callbacks
2418  * @block : the block of callbacks
2419  *
2420  * This function allow to plug a debugger into the XSLT library
2421  * @block points to a block of memory containing the address of @no
2422  * callback routines.
2423  *
2424  * Returns 0 in case of success and -1 in case of error
2425  */
2426 int
xsltSetDebuggerCallbacks(int no,void * block)2427 xsltSetDebuggerCallbacks(int no, void *block)
2428 {
2429     xsltDebuggerCallbacksPtr callbacks;
2430 
2431     if ((block == NULL) || (no != XSLT_CALLBACK_NUMBER))
2432 	return(-1);
2433 
2434     callbacks = (xsltDebuggerCallbacksPtr) block;
2435     xsltDebuggerCurrentCallbacks.handler = callbacks->handler;
2436     xsltDebuggerCurrentCallbacks.add  = callbacks->add;
2437     xsltDebuggerCurrentCallbacks.drop  = callbacks->drop;
2438     return(0);
2439 }
2440 
2441 /**
2442  * xslHandleDebugger:
2443  * @cur : source node being executed
2444  * @node : data node being processed
2445  * @templ : temlate that applies to node
2446  * @ctxt : the xslt transform context
2447  *
2448  * If either cur or node are a breakpoint, or xslDebugStatus in state
2449  *   where debugging must occcur at this time then transfer control
2450  *   to the xslDebugBreak function
2451  */
2452 void
xslHandleDebugger(xmlNodePtr cur,xmlNodePtr node,xsltTemplatePtr templ,xsltTransformContextPtr ctxt)2453 xslHandleDebugger(xmlNodePtr cur, xmlNodePtr node, xsltTemplatePtr templ,
2454 	          xsltTransformContextPtr ctxt)
2455 {
2456     if (xsltDebuggerCurrentCallbacks.handler != NULL)
2457 	xsltDebuggerCurrentCallbacks.handler(cur, node, templ, ctxt);
2458 }
2459 
2460 /**
2461  * xslAddCall:
2462  * @templ : current template being applied
2463  * @source : the source node being processed
2464  *
2465  * Add template "call" to call stack
2466  * Returns : 1 on sucess 0 otherwise an error may be printed if
2467  *            WITH_XSLT_DEBUG_BREAKPOINTS is defined
2468  */
2469 int
xslAddCall(xsltTemplatePtr templ,xmlNodePtr source)2470 xslAddCall(xsltTemplatePtr templ, xmlNodePtr source)
2471 {
2472     if (xsltDebuggerCurrentCallbacks.add != NULL)
2473 	return(xsltDebuggerCurrentCallbacks.add(templ, source));
2474     return(0);
2475 }
2476 
2477 /**
2478  * xslDropCall:
2479  *
2480  * Drop the topmost item off the call stack
2481  */
2482 void
xslDropCall(void)2483 xslDropCall(void)
2484 {
2485     if (xsltDebuggerCurrentCallbacks.drop != NULL)
2486 	xsltDebuggerCurrentCallbacks.drop();
2487 }
2488 
2489 #endif /* WITH_DEBUGGER */
2490 
2491