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