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