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