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