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