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