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