| /* |
| * xsltutils.c: Utilities for the XSL Transformation 1.0 engine |
| * |
| * Reference: |
| * http://www.w3.org/TR/1999/REC-xslt-19991116 |
| * |
| * See Copyright for the status of this software. |
| * |
| * daniel@veillard.com |
| */ |
| |
| #define IN_LIBXSLT |
| #include "libxslt.h" |
| |
| #ifndef XSLT_NEED_TRIO |
| #include <stdio.h> |
| #else |
| #include <trio.h> |
| #endif |
| |
| #include <string.h> |
| #ifdef HAVE_SYS_TIME_H |
| #include <sys/time.h> |
| #endif |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #ifdef HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #include <stdarg.h> |
| |
| #include <libxml/xmlmemory.h> |
| #include <libxml/tree.h> |
| #include <libxml/HTMLtree.h> |
| #include <libxml/xmlerror.h> |
| #include <libxml/xmlIO.h> |
| #include "xsltutils.h" |
| #include "templates.h" |
| #include "xsltInternals.h" |
| #include "imports.h" |
| #include "transform.h" |
| |
| /* gettimeofday on Windows ??? */ |
| #if defined(WIN32) && !defined(__CYGWIN__) |
| #ifdef _MSC_VER |
| #include <winsock2.h> |
| #pragma comment(lib, "ws2_32.lib") |
| #define gettimeofday(p1,p2) |
| #define HAVE_GETTIMEOFDAY |
| #define XSLT_WIN32_PERFORMANCE_COUNTER |
| #endif /* _MS_VER */ |
| #endif /* WIN32 */ |
| |
| /************************************************************************ |
| * * |
| * Convenience function * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xsltGetCNsProp: |
| * @style: the stylesheet |
| * @node: the node |
| * @name: the attribute name |
| * @nameSpace: the URI of the namespace |
| * |
| * Similar to xmlGetNsProp() but with a slightly different semantic |
| * |
| * Search and get the value of an attribute associated to a node |
| * This attribute has to be anchored in the namespace specified, |
| * or has no namespace and the element is in that namespace. |
| * |
| * This does the entity substitution. |
| * This function looks in DTD attribute declaration for #FIXED or |
| * default declaration values unless DTD use has been turned off. |
| * |
| * Returns the attribute value or NULL if not found. The string is allocated |
| * in the stylesheet dictionary. |
| */ |
| const xmlChar * |
| xsltGetCNsProp(xsltStylesheetPtr style, xmlNodePtr node, |
| const xmlChar *name, const xmlChar *nameSpace) { |
| xmlAttrPtr prop; |
| xmlDocPtr doc; |
| xmlNsPtr ns; |
| xmlChar *tmp; |
| const xmlChar *ret; |
| |
| if ((node == NULL) || (style == NULL) || (style->dict == NULL)) |
| return(NULL); |
| |
| prop = node->properties; |
| if (nameSpace == NULL) { |
| return xmlGetProp(node, name); |
| } |
| while (prop != NULL) { |
| /* |
| * One need to have |
| * - same attribute names |
| * - and the attribute carrying that namespace |
| */ |
| if ((xmlStrEqual(prop->name, name)) && |
| (((prop->ns == NULL) && (node->ns != NULL) && |
| (xmlStrEqual(node->ns->href, nameSpace))) || |
| ((prop->ns != NULL) && |
| (xmlStrEqual(prop->ns->href, nameSpace))))) { |
| |
| tmp = xmlNodeListGetString(node->doc, prop->children, 1); |
| if (tmp == NULL) |
| ret = xmlDictLookup(style->dict, BAD_CAST "", 0); |
| else { |
| ret = xmlDictLookup(style->dict, tmp, -1); |
| xmlFree(tmp); |
| } |
| return ret; |
| } |
| prop = prop->next; |
| } |
| tmp = NULL; |
| /* |
| * Check if there is a default declaration in the internal |
| * or external subsets |
| */ |
| doc = node->doc; |
| if (doc != NULL) { |
| if (doc->intSubset != NULL) { |
| xmlAttributePtr attrDecl; |
| |
| attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name); |
| if ((attrDecl == NULL) && (doc->extSubset != NULL)) |
| attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name); |
| |
| if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) { |
| /* |
| * The DTD declaration only allows a prefix search |
| */ |
| ns = xmlSearchNs(doc, node, attrDecl->prefix); |
| if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace))) |
| return(xmlDictLookup(style->dict, |
| attrDecl->defaultValue, -1)); |
| } |
| } |
| } |
| return(NULL); |
| } |
| /** |
| * xsltGetNsProp: |
| * @node: the node |
| * @name: the attribute name |
| * @nameSpace: the URI of the namespace |
| * |
| * Similar to xmlGetNsProp() but with a slightly different semantic |
| * |
| * Search and get the value of an attribute associated to a node |
| * This attribute has to be anchored in the namespace specified, |
| * or has no namespace and the element is in that namespace. |
| * |
| * This does the entity substitution. |
| * This function looks in DTD attribute declaration for #FIXED or |
| * default declaration values unless DTD use has been turned off. |
| * |
| * Returns the attribute value or NULL if not found. |
| * It's up to the caller to free the memory. |
| */ |
| xmlChar * |
| xsltGetNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) { |
| xmlAttrPtr prop; |
| xmlDocPtr doc; |
| xmlNsPtr ns; |
| |
| if (node == NULL) |
| return(NULL); |
| |
| prop = node->properties; |
| /* |
| * TODO: Substitute xmlGetProp() for xmlGetNsProp(), since the former |
| * is not namespace-aware and will return an attribute with equal |
| * name regardless of its namespace. |
| * Example: |
| * <xsl:element foo:name="myName"/> |
| * So this would return "myName" even if an attribute @name |
| * in the XSLT was requested. |
| */ |
| if (nameSpace == NULL) |
| return(xmlGetProp(node, name)); |
| while (prop != NULL) { |
| /* |
| * One need to have |
| * - same attribute names |
| * - and the attribute carrying that namespace |
| */ |
| if ((xmlStrEqual(prop->name, name)) && |
| (((prop->ns == NULL) && (node->ns != NULL) && |
| (xmlStrEqual(node->ns->href, nameSpace))) || |
| ((prop->ns != NULL) && |
| (xmlStrEqual(prop->ns->href, nameSpace))))) { |
| xmlChar *ret; |
| |
| ret = xmlNodeListGetString(node->doc, prop->children, 1); |
| if (ret == NULL) return(xmlStrdup((xmlChar *)"")); |
| return(ret); |
| } |
| prop = prop->next; |
| } |
| |
| /* |
| * Check if there is a default declaration in the internal |
| * or external subsets |
| */ |
| doc = node->doc; |
| if (doc != NULL) { |
| if (doc->intSubset != NULL) { |
| xmlAttributePtr attrDecl; |
| |
| attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name); |
| if ((attrDecl == NULL) && (doc->extSubset != NULL)) |
| attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name); |
| |
| if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) { |
| /* |
| * The DTD declaration only allows a prefix search |
| */ |
| ns = xmlSearchNs(doc, node, attrDecl->prefix); |
| if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace))) |
| return(xmlStrdup(attrDecl->defaultValue)); |
| } |
| } |
| } |
| return(NULL); |
| } |
| |
| /** |
| * xsltGetUTF8Char: |
| * @utf: a sequence of UTF-8 encoded bytes |
| * @len: a pointer to @bytes len |
| * |
| * Read one UTF8 Char from @utf |
| * Function copied from libxml2 xmlGetUTF8Char() ... to discard ultimately |
| * and use the original API |
| * |
| * Returns the char value or -1 in case of error and update @len with the |
| * number of bytes used |
| */ |
| int |
| xsltGetUTF8Char(const unsigned char *utf, int *len) { |
| unsigned int c; |
| |
| if (utf == NULL) |
| goto error; |
| if (len == NULL) |
| goto error; |
| if (*len < 1) |
| goto error; |
| |
| c = utf[0]; |
| if (c & 0x80) { |
| if (*len < 2) |
| goto error; |
| if ((utf[1] & 0xc0) != 0x80) |
| goto error; |
| if ((c & 0xe0) == 0xe0) { |
| if (*len < 3) |
| goto error; |
| if ((utf[2] & 0xc0) != 0x80) |
| goto error; |
| if ((c & 0xf0) == 0xf0) { |
| if (*len < 4) |
| goto error; |
| if ((c & 0xf8) != 0xf0 || (utf[3] & 0xc0) != 0x80) |
| goto error; |
| *len = 4; |
| /* 4-byte code */ |
| c = (utf[0] & 0x7) << 18; |
| c |= (utf[1] & 0x3f) << 12; |
| c |= (utf[2] & 0x3f) << 6; |
| c |= utf[3] & 0x3f; |
| } else { |
| /* 3-byte code */ |
| *len = 3; |
| c = (utf[0] & 0xf) << 12; |
| c |= (utf[1] & 0x3f) << 6; |
| c |= utf[2] & 0x3f; |
| } |
| } else { |
| /* 2-byte code */ |
| *len = 2; |
| c = (utf[0] & 0x1f) << 6; |
| c |= utf[1] & 0x3f; |
| } |
| } else { |
| /* 1-byte code */ |
| *len = 1; |
| } |
| return(c); |
| |
| error: |
| if (len != NULL) |
| *len = 0; |
| return(-1); |
| } |
| |
| #ifdef XSLT_REFACTORED |
| |
| /** |
| * xsltPointerListAddSize: |
| * @list: the pointer list structure |
| * @item: the item to be stored |
| * @initialSize: the initial size of the list |
| * |
| * Adds an item to the list. |
| * |
| * Returns the position of the added item in the list or |
| * -1 in case of an error. |
| */ |
| int |
| xsltPointerListAddSize(xsltPointerListPtr list, |
| void *item, |
| int initialSize) |
| { |
| if (list->items == NULL) { |
| if (initialSize <= 0) |
| initialSize = 1; |
| list->items = (void **) xmlMalloc( |
| initialSize * sizeof(void *)); |
| if (list->items == NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "xsltPointerListAddSize: memory allocation failure.\n"); |
| return(-1); |
| } |
| list->number = 0; |
| list->size = initialSize; |
| } else if (list->size <= list->number) { |
| list->size *= 2; |
| list->items = (void **) xmlRealloc(list->items, |
| list->size * sizeof(void *)); |
| if (list->items == NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "xsltPointerListAddSize: memory re-allocation failure.\n"); |
| list->size = 0; |
| return(-1); |
| } |
| } |
| list->items[list->number++] = item; |
| return(0); |
| } |
| |
| /** |
| * xsltPointerListCreate: |
| * @initialSize: the initial size for the list |
| * |
| * Creates an xsltPointerList structure. |
| * |
| * Returns a xsltPointerList structure or NULL in case of an error. |
| */ |
| xsltPointerListPtr |
| xsltPointerListCreate(int initialSize) |
| { |
| xsltPointerListPtr ret; |
| |
| ret = xmlMalloc(sizeof(xsltPointerList)); |
| if (ret == NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "xsltPointerListCreate: memory allocation failure.\n"); |
| return (NULL); |
| } |
| memset(ret, 0, sizeof(xsltPointerList)); |
| if (initialSize > 0) { |
| xsltPointerListAddSize(ret, NULL, initialSize); |
| ret->number = 0; |
| } |
| return (ret); |
| } |
| |
| /** |
| * xsltPointerListFree: |
| * @list: pointer to the list to be freed |
| * |
| * Frees the xsltPointerList structure. This does not free |
| * the content of the list. |
| */ |
| void |
| xsltPointerListFree(xsltPointerListPtr list) |
| { |
| if (list == NULL) |
| return; |
| if (list->items != NULL) |
| xmlFree(list->items); |
| xmlFree(list); |
| } |
| |
| /** |
| * xsltPointerListClear: |
| * @list: pointer to the list to be cleared |
| * |
| * Resets the list, but does not free the allocated array |
| * and does not free the content of the list. |
| */ |
| void |
| xsltPointerListClear(xsltPointerListPtr list) |
| { |
| if (list->items != NULL) { |
| xmlFree(list->items); |
| list->items = NULL; |
| } |
| list->number = 0; |
| list->size = 0; |
| } |
| |
| #endif /* XSLT_REFACTORED */ |
| |
| /************************************************************************ |
| * * |
| * Handling of XSLT stylesheets messages * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xsltMessage: |
| * @ctxt: an XSLT processing context |
| * @node: The current node |
| * @inst: The node containing the message instruction |
| * |
| * Process and xsl:message construct |
| */ |
| void |
| xsltMessage(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst) { |
| xmlGenericErrorFunc error = xsltGenericError; |
| void *errctx = xsltGenericErrorContext; |
| xmlChar *prop, *message; |
| int terminate = 0; |
| |
| if ((ctxt == NULL) || (inst == NULL)) |
| return; |
| |
| if (ctxt->error != NULL) { |
| error = ctxt->error; |
| errctx = ctxt->errctx; |
| } |
| |
| prop = xmlGetNsProp(inst, (const xmlChar *)"terminate", NULL); |
| if (prop != NULL) { |
| if (xmlStrEqual(prop, (const xmlChar *)"yes")) { |
| terminate = 1; |
| } else if (xmlStrEqual(prop, (const xmlChar *)"no")) { |
| terminate = 0; |
| } else { |
| error(errctx, |
| "xsl:message : terminate expecting 'yes' or 'no'\n"); |
| ctxt->state = XSLT_STATE_ERROR; |
| } |
| xmlFree(prop); |
| } |
| message = xsltEvalTemplateString(ctxt, node, inst); |
| if (message != NULL) { |
| int len = xmlStrlen(message); |
| |
| error(errctx, "%s", (const char *)message); |
| if ((len > 0) && (message[len - 1] != '\n')) |
| error(errctx, "\n"); |
| xmlFree(message); |
| } |
| if (terminate) |
| ctxt->state = XSLT_STATE_STOPPED; |
| } |
| |
| /************************************************************************ |
| * * |
| * Handling of out of context errors * |
| * * |
| ************************************************************************/ |
| |
| #define XSLT_GET_VAR_STR(msg, str) { \ |
| int size; \ |
| int chars; \ |
| char *larger; \ |
| va_list ap; \ |
| \ |
| str = (char *) xmlMalloc(150); \ |
| if (str == NULL) \ |
| return; \ |
| \ |
| size = 150; \ |
| \ |
| while (size < 64000) { \ |
| va_start(ap, msg); \ |
| chars = vsnprintf(str, size, msg, ap); \ |
| va_end(ap); \ |
| if ((chars > -1) && (chars < size)) \ |
| break; \ |
| if (chars > -1) \ |
| size += chars + 1; \ |
| else \ |
| size += 100; \ |
| if ((larger = (char *) xmlRealloc(str, size)) == NULL) {\ |
| xmlFree(str); \ |
| return; \ |
| } \ |
| str = larger; \ |
| } \ |
| } |
| /** |
| * xsltGenericErrorDefaultFunc: |
| * @ctx: an error context |
| * @msg: the message to display/transmit |
| * @...: extra parameters for the message display |
| * |
| * Default handler for out of context error messages. |
| */ |
| static void |
| xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { |
| va_list args; |
| |
| if (xsltGenericErrorContext == NULL) |
| xsltGenericErrorContext = (void *) stderr; |
| |
| va_start(args, msg); |
| vfprintf((FILE *)xsltGenericErrorContext, msg, args); |
| va_end(args); |
| } |
| |
| xmlGenericErrorFunc xsltGenericError = xsltGenericErrorDefaultFunc; |
| void *xsltGenericErrorContext = NULL; |
| |
| |
| /** |
| * xsltSetGenericErrorFunc: |
| * @ctx: the new error handling context |
| * @handler: the new handler function |
| * |
| * Function to reset the handler and the error context for out of |
| * context error messages. |
| * This simply means that @handler will be called for subsequent |
| * error messages while not parsing nor validating. And @ctx will |
| * be passed as first argument to @handler |
| * One can simply force messages to be emitted to another FILE * than |
| * stderr by setting @ctx to this file handle and @handler to NULL. |
| */ |
| void |
| xsltSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) { |
| xsltGenericErrorContext = ctx; |
| if (handler != NULL) |
| xsltGenericError = handler; |
| else |
| xsltGenericError = xsltGenericErrorDefaultFunc; |
| } |
| |
| /** |
| * xsltGenericDebugDefaultFunc: |
| * @ctx: an error context |
| * @msg: the message to display/transmit |
| * @...: extra parameters for the message display |
| * |
| * Default handler for out of context error messages. |
| */ |
| static void |
| xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { |
| va_list args; |
| |
| if (xsltGenericDebugContext == NULL) |
| return; |
| |
| va_start(args, msg); |
| vfprintf((FILE *)xsltGenericDebugContext, msg, args); |
| va_end(args); |
| } |
| |
| xmlGenericErrorFunc xsltGenericDebug = xsltGenericDebugDefaultFunc; |
| void *xsltGenericDebugContext = NULL; |
| |
| |
| /** |
| * xsltSetGenericDebugFunc: |
| * @ctx: the new error handling context |
| * @handler: the new handler function |
| * |
| * Function to reset the handler and the error context for out of |
| * context error messages. |
| * This simply means that @handler will be called for subsequent |
| * error messages while not parsing or validating. And @ctx will |
| * be passed as first argument to @handler |
| * One can simply force messages to be emitted to another FILE * than |
| * stderr by setting @ctx to this file handle and @handler to NULL. |
| */ |
| void |
| xsltSetGenericDebugFunc(void *ctx, xmlGenericErrorFunc handler) { |
| xsltGenericDebugContext = ctx; |
| if (handler != NULL) |
| xsltGenericDebug = handler; |
| else |
| xsltGenericDebug = xsltGenericDebugDefaultFunc; |
| } |
| |
| /** |
| * xsltPrintErrorContext: |
| * @ctxt: the transformation context |
| * @style: the stylesheet |
| * @node: the current node being processed |
| * |
| * Display the context of an error. |
| */ |
| void |
| xsltPrintErrorContext(xsltTransformContextPtr ctxt, |
| xsltStylesheetPtr style, xmlNodePtr node) { |
| int line = 0; |
| const xmlChar *file = NULL; |
| const xmlChar *name = NULL; |
| const char *type = "error"; |
| xmlGenericErrorFunc error = xsltGenericError; |
| void *errctx = xsltGenericErrorContext; |
| |
| if (ctxt != NULL) { |
| ctxt->state = XSLT_STATE_ERROR; |
| if (ctxt->error != NULL) { |
| error = ctxt->error; |
| errctx = ctxt->errctx; |
| } |
| } |
| if ((node == NULL) && (ctxt != NULL)) |
| node = ctxt->inst; |
| |
| if (node != NULL) { |
| if ((node->type == XML_DOCUMENT_NODE) || |
| (node->type == XML_HTML_DOCUMENT_NODE)) { |
| xmlDocPtr doc = (xmlDocPtr) node; |
| |
| file = doc->URL; |
| } else { |
| line = xmlGetLineNo(node); |
| if ((node->doc != NULL) && (node->doc->URL != NULL)) |
| file = node->doc->URL; |
| if (node->name != NULL) |
| name = node->name; |
| } |
| } |
| |
| if (ctxt != NULL) |
| type = "runtime error"; |
| else if (style != NULL) { |
| #ifdef XSLT_REFACTORED |
| if (XSLT_CCTXT(style)->errSeverity == XSLT_ERROR_SEVERITY_WARNING) |
| type = "compilation warning"; |
| else |
| type = "compilation error"; |
| #else |
| type = "compilation error"; |
| #endif |
| } |
| |
| if ((file != NULL) && (line != 0) && (name != NULL)) |
| error(errctx, "%s: file %s line %d element %s\n", |
| type, file, line, name); |
| else if ((file != NULL) && (name != NULL)) |
| error(errctx, "%s: file %s element %s\n", type, file, name); |
| else if ((file != NULL) && (line != 0)) |
| error(errctx, "%s: file %s line %d\n", type, file, line); |
| else if (file != NULL) |
| error(errctx, "%s: file %s\n", type, file); |
| else if (name != NULL) |
| error(errctx, "%s: element %s\n", type, name); |
| else |
| error(errctx, "%s\n", type); |
| } |
| |
| /** |
| * xsltSetTransformErrorFunc: |
| * @ctxt: the XSLT transformation context |
| * @ctx: the new error handling context |
| * @handler: the new handler function |
| * |
| * Function to reset the handler and the error context for out of |
| * context error messages specific to a given XSLT transromation. |
| * |
| * This simply means that @handler will be called for subsequent |
| * error messages while running the transformation. |
| */ |
| void |
| xsltSetTransformErrorFunc(xsltTransformContextPtr ctxt, |
| void *ctx, xmlGenericErrorFunc handler) |
| { |
| ctxt->error = handler; |
| ctxt->errctx = ctx; |
| } |
| |
| /** |
| * xsltTransformError: |
| * @ctxt: an XSLT transformation context |
| * @style: the XSLT stylesheet used |
| * @node: the current node in the stylesheet |
| * @msg: the message to display/transmit |
| * @...: extra parameters for the message display |
| * |
| * Display and format an error messages, gives file, line, position and |
| * extra parameters, will use the specific transformation context if available |
| */ |
| void |
| xsltTransformError(xsltTransformContextPtr ctxt, |
| xsltStylesheetPtr style, |
| xmlNodePtr node, |
| const char *msg, ...) { |
| xmlGenericErrorFunc error = xsltGenericError; |
| void *errctx = xsltGenericErrorContext; |
| char * str; |
| |
| if (ctxt != NULL) { |
| ctxt->state = XSLT_STATE_ERROR; |
| if (ctxt->error != NULL) { |
| error = ctxt->error; |
| errctx = ctxt->errctx; |
| } |
| } |
| if ((node == NULL) && (ctxt != NULL)) |
| node = ctxt->inst; |
| xsltPrintErrorContext(ctxt, style, node); |
| XSLT_GET_VAR_STR(msg, str); |
| error(errctx, "%s", str); |
| if (str != NULL) |
| xmlFree(str); |
| } |
| |
| /************************************************************************ |
| * * |
| * QNames * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xsltSplitQName: |
| * @dict: a dictionary |
| * @name: the full QName |
| * @prefix: the return value |
| * |
| * Split QNames into prefix and local names, both allocated from a dictionary. |
| * |
| * Returns: the localname or NULL in case of error. |
| */ |
| const xmlChar * |
| xsltSplitQName(xmlDictPtr dict, const xmlChar *name, const xmlChar **prefix) { |
| int len = 0; |
| const xmlChar *ret = NULL; |
| |
| *prefix = NULL; |
| if ((name == NULL) || (dict == NULL)) return(NULL); |
| if (name[0] == ':') |
| return(xmlDictLookup(dict, name, -1)); |
| while ((name[len] != 0) && (name[len] != ':')) len++; |
| if (name[len] == 0) return(xmlDictLookup(dict, name, -1)); |
| *prefix = xmlDictLookup(dict, name, len); |
| ret = xmlDictLookup(dict, &name[len + 1], -1); |
| return(ret); |
| } |
| |
| /** |
| * xsltGetQNameURI: |
| * @node: the node holding the QName |
| * @name: pointer to the initial QName value |
| * |
| * This function analyzes @name, if the name contains a prefix, |
| * the function seaches the associated namespace in scope for it. |
| * It will also replace @name value with the NCName, the old value being |
| * freed. |
| * Errors in the prefix lookup are signalled by setting @name to NULL. |
| * |
| * NOTE: the namespace returned is a pointer to the place where it is |
| * defined and hence has the same lifespan as the document holding it. |
| * |
| * Returns the namespace URI if there is a prefix, or NULL if @name is |
| * not prefixed. |
| */ |
| const xmlChar * |
| xsltGetQNameURI(xmlNodePtr node, xmlChar ** name) |
| { |
| int len = 0; |
| xmlChar *qname; |
| xmlNsPtr ns; |
| |
| if (name == NULL) |
| return(NULL); |
| qname = *name; |
| if ((qname == NULL) || (*qname == 0)) |
| return(NULL); |
| if (node == NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "QName: no element for namespace lookup %s\n", |
| qname); |
| xmlFree(qname); |
| *name = NULL; |
| return(NULL); |
| } |
| |
| /* nasty but valid */ |
| if (qname[0] == ':') |
| return(NULL); |
| |
| /* |
| * we are not trying to validate but just to cut, and yes it will |
| * work even if this is a set of UTF-8 encoded chars |
| */ |
| while ((qname[len] != 0) && (qname[len] != ':')) |
| len++; |
| |
| if (qname[len] == 0) |
| return(NULL); |
| |
| /* |
| * handle xml: separately, this one is magical |
| */ |
| if ((qname[0] == 'x') && (qname[1] == 'm') && |
| (qname[2] == 'l') && (qname[3] == ':')) { |
| if (qname[4] == 0) |
| return(NULL); |
| *name = xmlStrdup(&qname[4]); |
| xmlFree(qname); |
| return(XML_XML_NAMESPACE); |
| } |
| |
| qname[len] = 0; |
| ns = xmlSearchNs(node->doc, node, qname); |
| if (ns == NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "%s:%s : no namespace bound to prefix %s\n", |
| qname, &qname[len + 1], qname); |
| *name = NULL; |
| xmlFree(qname); |
| return(NULL); |
| } |
| *name = xmlStrdup(&qname[len + 1]); |
| xmlFree(qname); |
| return(ns->href); |
| } |
| |
| /** |
| * xsltGetQNameURI2: |
| * @style: stylesheet pointer |
| * @node: the node holding the QName |
| * @name: pointer to the initial QName value |
| * |
| * This function is similar to xsltGetQNameURI, but is used when |
| * @name is a dictionary entry. |
| * |
| * Returns the namespace URI if there is a prefix, or NULL if @name is |
| * not prefixed. |
| */ |
| const xmlChar * |
| xsltGetQNameURI2(xsltStylesheetPtr style, xmlNodePtr node, |
| const xmlChar **name) { |
| int len = 0; |
| xmlChar *qname; |
| xmlNsPtr ns; |
| |
| if (name == NULL) |
| return(NULL); |
| qname = (xmlChar *)*name; |
| if ((qname == NULL) || (*qname == 0)) |
| return(NULL); |
| if (node == NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "QName: no element for namespace lookup %s\n", |
| qname); |
| *name = NULL; |
| return(NULL); |
| } |
| |
| /* |
| * we are not trying to validate but just to cut, and yes it will |
| * work even if this is a set of UTF-8 encoded chars |
| */ |
| while ((qname[len] != 0) && (qname[len] != ':')) |
| len++; |
| |
| if (qname[len] == 0) |
| return(NULL); |
| |
| /* |
| * handle xml: separately, this one is magical |
| */ |
| if ((qname[0] == 'x') && (qname[1] == 'm') && |
| (qname[2] == 'l') && (qname[3] == ':')) { |
| if (qname[4] == 0) |
| return(NULL); |
| *name = xmlDictLookup(style->dict, &qname[4], -1); |
| return(XML_XML_NAMESPACE); |
| } |
| |
| qname = xmlStrndup(*name, len); |
| ns = xmlSearchNs(node->doc, node, qname); |
| if (ns == NULL) { |
| if (style) { |
| xsltTransformError(NULL, style, node, |
| "No namespace bound to prefix '%s'.\n", |
| qname); |
| style->errors++; |
| } else { |
| xsltGenericError(xsltGenericErrorContext, |
| "%s : no namespace bound to prefix %s\n", |
| *name, qname); |
| } |
| *name = NULL; |
| xmlFree(qname); |
| return(NULL); |
| } |
| *name = xmlDictLookup(style->dict, (*name)+len+1, -1); |
| xmlFree(qname); |
| return(ns->href); |
| } |
| |
| /************************************************************************ |
| * * |
| * Sorting * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xsltDocumentSortFunction: |
| * @list: the node set |
| * |
| * reorder the current node list @list accordingly to the document order |
| * This function is slow, obsolete and should not be used anymore. |
| */ |
| void |
| xsltDocumentSortFunction(xmlNodeSetPtr list) { |
| int i, j; |
| int len, tst; |
| xmlNodePtr node; |
| |
| if (list == NULL) |
| return; |
| len = list->nodeNr; |
| if (len <= 1) |
| return; |
| /* TODO: sort is really not optimized, does it needs to ? */ |
| for (i = 0;i < len -1;i++) { |
| for (j = i + 1; j < len; j++) { |
| tst = xmlXPathCmpNodes(list->nodeTab[i], list->nodeTab[j]); |
| if (tst == -1) { |
| node = list->nodeTab[i]; |
| list->nodeTab[i] = list->nodeTab[j]; |
| list->nodeTab[j] = node; |
| } |
| } |
| } |
| } |
| |
| /** |
| * xsltComputeSortResult: |
| * @ctxt: a XSLT process context |
| * @sort: node list |
| * |
| * reorder the current node list accordingly to the set of sorting |
| * requirement provided by the array of nodes. |
| * |
| * Returns a ordered XPath nodeset or NULL in case of error. |
| */ |
| xmlXPathObjectPtr * |
| xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) { |
| #ifdef XSLT_REFACTORED |
| xsltStyleItemSortPtr comp; |
| #else |
| xsltStylePreCompPtr comp; |
| #endif |
| xmlXPathObjectPtr *results = NULL; |
| xmlNodeSetPtr list = NULL; |
| xmlXPathObjectPtr res; |
| int len = 0; |
| int i; |
| xmlNodePtr oldNode; |
| xmlNodePtr oldInst; |
| int oldPos, oldSize ; |
| int oldNsNr; |
| xmlNsPtr *oldNamespaces; |
| |
| comp = sort->psvi; |
| if (comp == NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "xsl:sort : compilation failed\n"); |
| return(NULL); |
| } |
| |
| if ((comp->select == NULL) || (comp->comp == NULL)) |
| return(NULL); |
| |
| list = ctxt->nodeList; |
| if ((list == NULL) || (list->nodeNr <= 1)) |
| return(NULL); |
| |
| len = list->nodeNr; |
| |
| /* TODO: xsl:sort lang attribute */ |
| /* TODO: xsl:sort case-order attribute */ |
| |
| |
| results = xmlMalloc(len * sizeof(xmlXPathObjectPtr)); |
| if (results == NULL) { |
| xsltGenericError(xsltGenericErrorContext, |
| "xsltComputeSortResult: memory allocation failure\n"); |
| return(NULL); |
| } |
| |
| oldNode = ctxt->node; |
| oldInst = ctxt->inst; |
| oldPos = ctxt->xpathCtxt->proximityPosition; |
| oldSize = ctxt->xpathCtxt->contextSize; |
| oldNsNr = ctxt->xpathCtxt->nsNr; |
| oldNamespaces = ctxt->xpathCtxt->namespaces; |
| for (i = 0;i < len;i++) { |
| ctxt->inst = sort; |
| ctxt->xpathCtxt->contextSize = len; |
| ctxt->xpathCtxt->proximityPosition = i + 1; |
| ctxt->node = list->nodeTab[i]; |
| ctxt->xpathCtxt->node = ctxt->node; |
| #ifdef XSLT_REFACTORED |
| if (comp->inScopeNs != NULL) { |
| ctxt->xpathCtxt->namespaces = comp->inScopeNs->list; |
| ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber; |
| } else { |
| ctxt->xpathCtxt->namespaces = NULL; |
| ctxt->xpathCtxt->nsNr = 0; |
| } |
| #else |
| ctxt->xpathCtxt->namespaces = comp->nsList; |
| ctxt->xpathCtxt->nsNr = comp->nsNr; |
| #endif |
| res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt); |
| if (res != NULL) { |
| if (res->type != XPATH_STRING) |
| res = xmlXPathConvertString(res); |
| if (comp->number) |
| res = xmlXPathConvertNumber(res); |
| res->index = i; /* Save original pos for dupl resolv */ |
| if (comp->number) { |
| if (res->type == XPATH_NUMBER) { |
| results[i] = res; |
| } else { |
| #ifdef WITH_XSLT_DEBUG_PROCESS |
| xsltGenericDebug(xsltGenericDebugContext, |
| "xsltComputeSortResult: select didn't evaluate to a number\n"); |
| #endif |
| results[i] = NULL; |
| } |
| } else { |
| if (res->type == XPATH_STRING) { |
| if (comp->locale != (xsltLocale)0) { |
| xmlChar *str = res->stringval; |
| res->stringval = (xmlChar *) xsltStrxfrm(comp->locale, str); |
| xmlFree(str); |
| } |
| |
| results[i] = res; |
| } else { |
| #ifdef WITH_XSLT_DEBUG_PROCESS |
| xsltGenericDebug(xsltGenericDebugContext, |
| "xsltComputeSortResult: select didn't evaluate to a string\n"); |
| #endif |
| results[i] = NULL; |
| } |
| } |
| } else { |
| ctxt->state = XSLT_STATE_STOPPED; |
| results[i] = NULL; |
| } |
| } |
| ctxt->node = oldNode; |
| ctxt->inst = oldInst; |
| ctxt->xpathCtxt->contextSize = oldSize; |
| ctxt->xpathCtxt->proximityPosition = oldPos; |
| ctxt->xpathCtxt->nsNr = oldNsNr; |
| ctxt->xpathCtxt->namespaces = oldNamespaces; |
| |
| return(results); |
| } |
| |
| /** |
| * xsltDefaultSortFunction: |
| * @ctxt: a XSLT process context |
| * @sorts: array of sort nodes |
| * @nbsorts: the number of sorts in the array |
| * |
| * reorder the current node list accordingly to the set of sorting |
| * requirement provided by the arry of nodes. |
| */ |
| void |
| xsltDefaultSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts, |
| int nbsorts) { |
| #ifdef XSLT_REFACTORED |
| xsltStyleItemSortPtr comp; |
| #else |
| xsltStylePreCompPtr comp; |
| #endif |
| xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT]; |
| xmlXPathObjectPtr *results = NULL, *res; |
| xmlNodeSetPtr list = NULL; |
| int descending, number, desc, numb; |
| int len = 0; |
| int i, j, incr; |
| int tst; |
| int depth; |
| xmlNodePtr node; |
| xmlXPathObjectPtr tmp; |
| int tempstype[XSLT_MAX_SORT], temporder[XSLT_MAX_SORT]; |
| |
| if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) || |
| (nbsorts >= XSLT_MAX_SORT)) |
| return; |
| if (sorts[0] == NULL) |
| return; |
| comp = sorts[0]->psvi; |
| if (comp == NULL) |
| return; |
| |
| list = ctxt->nodeList; |
| if ((list == NULL) || (list->nodeNr <= 1)) |
| return; /* nothing to do */ |
| |
| for (j = 0; j < nbsorts; j++) { |
| comp = sorts[j]->psvi; |
| tempstype[j] = 0; |
| if ((comp->stype == NULL) && (comp->has_stype != 0)) { |
| comp->stype = |
| xsltEvalAttrValueTemplate(ctxt, sorts[j], |
| (const xmlChar *) "data-type", |
| XSLT_NAMESPACE); |
| if (comp->stype != NULL) { |
| tempstype[j] = 1; |
| if (xmlStrEqual(comp->stype, (const xmlChar *) "text")) |
| comp->number = 0; |
| else if (xmlStrEqual(comp->stype, (const xmlChar *) "number")) |
| comp->number = 1; |
| else { |
| xsltTransformError(ctxt, NULL, sorts[j], |
| "xsltDoSortFunction: no support for data-type = %s\n", |
| comp->stype); |
| comp->number = 0; /* use default */ |
| } |
| } |
| } |
| temporder[j] = 0; |
| if ((comp->order == NULL) && (comp->has_order != 0)) { |
| comp->order = xsltEvalAttrValueTemplate(ctxt, sorts[j], |
| (const xmlChar *) "order", |
| XSLT_NAMESPACE); |
| if (comp->order != NULL) { |
| temporder[j] = 1; |
| if (xmlStrEqual(comp->order, (const xmlChar *) "ascending")) |
| comp->descending = 0; |
| else if (xmlStrEqual(comp->order, |
| (const xmlChar *) "descending")) |
| comp->descending = 1; |
| else { |
| xsltTransformError(ctxt, NULL, sorts[j], |
| "xsltDoSortFunction: invalid value %s for order\n", |
| comp->order); |
| comp->descending = 0; /* use default */ |
| } |
| } |
| } |
| } |
| |
| len = list->nodeNr; |
| |
| resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]); |
| for (i = 1;i < XSLT_MAX_SORT;i++) |
| resultsTab[i] = NULL; |
| |
| results = resultsTab[0]; |
| |
| comp = sorts[0]->psvi; |
| descending = comp->descending; |
| number = comp->number; |
| if (results == NULL) |
| return; |
| |
| /* Shell's sort of node-set */ |
| for (incr = len / 2; incr > 0; incr /= 2) { |
| for (i = incr; i < len; i++) { |
| j = i - incr; |
| if (results[i] == NULL) |
| continue; |
| |
| while (j >= 0) { |
| if (results[j] == NULL) |
| tst = 1; |
| else { |
| if (number) { |
| /* We make NaN smaller than number in accordance |
| with XSLT spec */ |
| if (xmlXPathIsNaN(results[j]->floatval)) { |
| if (xmlXPathIsNaN(results[j + incr]->floatval)) |
| tst = 0; |
| else |
| tst = -1; |
| } else if (xmlXPathIsNaN(results[j + incr]->floatval)) |
| tst = 1; |
| else if (results[j]->floatval == |
| results[j + incr]->floatval) |
| tst = 0; |
| else if (results[j]->floatval > |
| results[j + incr]->floatval) |
| tst = 1; |
| else tst = -1; |
| } else if(comp->locale != (xsltLocale)0) { |
| tst = xsltLocaleStrcmp( |
| comp->locale, |
| (xsltLocaleChar *) results[j]->stringval, |
| (xsltLocaleChar *) results[j + incr]->stringval); |
| } else { |
| tst = xmlStrcmp(results[j]->stringval, |
| results[j + incr]->stringval); |
| } |
| if (descending) |
| tst = -tst; |
| } |
| if (tst == 0) { |
| /* |
| * Okay we need to use multi level sorts |
| */ |
| depth = 1; |
| while (depth < nbsorts) { |
| if (sorts[depth] == NULL) |
| break; |
| comp = sorts[depth]->psvi; |
| if (comp == NULL) |
| break; |
| desc = comp->descending; |
| numb = comp->number; |
| |
| /* |
| * Compute the result of the next level for the |
| * full set, this might be optimized ... or not |
| */ |
| if (resultsTab[depth] == NULL) |
| resultsTab[depth] = xsltComputeSortResult(ctxt, |
| sorts[depth]); |
| res = resultsTab[depth]; |
| if (res == NULL) |
| break; |
| if (res[j] == NULL) { |
| if (res[j+incr] != NULL) |
| tst = 1; |
| } else { |
| if (numb) { |
| /* We make NaN smaller than number in |
| accordance with XSLT spec */ |
| if (xmlXPathIsNaN(res[j]->floatval)) { |
| if (xmlXPathIsNaN(res[j + |
| incr]->floatval)) |
| tst = 0; |
| else |
| tst = -1; |
| } else if (xmlXPathIsNaN(res[j + incr]-> |
| floatval)) |
| tst = 1; |
| else if (res[j]->floatval == res[j + incr]-> |
| floatval) |
| tst = 0; |
| else if (res[j]->floatval > |
| res[j + incr]->floatval) |
| tst = 1; |
| else tst = -1; |
| } else if(comp->locale != (xsltLocale)0) { |
| tst = xsltLocaleStrcmp( |
| comp->locale, |
| (xsltLocaleChar *) res[j]->stringval, |
| (xsltLocaleChar *) res[j + incr]->stringval); |
| } else { |
| tst = xmlStrcmp(res[j]->stringval, |
| res[j + incr]->stringval); |
| } |
| if (desc) |
| tst = -tst; |
| } |
| |
| /* |
| * if we still can't differenciate at this level |
| * try one level deeper. |
| */ |
| if (tst != 0) |
| break; |
| depth++; |
| } |
| } |
| if (tst == 0) { |
| tst = results[j]->index > results[j + incr]->index; |
| } |
| if (tst > 0) { |
| tmp = results[j]; |
| results[j] = results[j + incr]; |
| results[j + incr] = tmp; |
| node = list->nodeTab[j]; |
| list->nodeTab[j] = list->nodeTab[j + incr]; |
| list->nodeTab[j + incr] = node; |
| depth = 1; |
| while (depth < nbsorts) { |
| if (sorts[depth] == NULL) |
| break; |
| if (resultsTab[depth] == NULL) |
| break; |
| res = resultsTab[depth]; |
| tmp = res[j]; |
| res[j] = res[j + incr]; |
| res[j + incr] = tmp; |
| depth++; |
| } |
| j -= incr; |
| } else |
| break; |
| } |
| } |
| } |
| |
| for (j = 0; j < nbsorts; j++) { |
| comp = sorts[j]->psvi; |
| if (tempstype[j] == 1) { |
| /* The data-type needs to be recomputed each time */ |
| xmlFree((void *)(comp->stype)); |
| comp->stype = NULL; |
| } |
| if (temporder[j] == 1) { |
| /* The order needs to be recomputed each time */ |
| xmlFree((void *)(comp->order)); |
| comp->order = NULL; |
| } |
| if (resultsTab[j] != NULL) { |
| for (i = 0;i < len;i++) |
| xmlXPathFreeObject(resultsTab[j][i]); |
| xmlFree(resultsTab[j]); |
| } |
| } |
| } |
| |
| |
| static xsltSortFunc xsltSortFunction = xsltDefaultSortFunction; |
| |
| /** |
| * xsltDoSortFunction: |
| * @ctxt: a XSLT process context |
| * @sorts: array of sort nodes |
| * @nbsorts: the number of sorts in the array |
| * |
| * reorder the current node list accordingly to the set of sorting |
| * requirement provided by the arry of nodes. |
| * This is a wrapper function, the actual function used is specified |
| * using xsltSetCtxtSortFunc() to set the context specific sort function, |
| * or xsltSetSortFunc() to set the global sort function. |
| * If a sort function is set on the context, this will get called. |
| * Otherwise the global sort function is called. |
| */ |
| void |
| xsltDoSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr * sorts, |
| int nbsorts) |
| { |
| if (ctxt->sortfunc != NULL) |
| (ctxt->sortfunc)(ctxt, sorts, nbsorts); |
| else if (xsltSortFunction != NULL) |
| xsltSortFunction(ctxt, sorts, nbsorts); |
| } |
| |
| /** |
| * xsltSetSortFunc: |
| * @handler: the new handler function |
| * |
| * Function to reset the global handler for XSLT sorting. |
| * If the handler is NULL, the default sort function will be used. |
| */ |
| void |
| xsltSetSortFunc(xsltSortFunc handler) { |
| if (handler != NULL) |
| xsltSortFunction = handler; |
| else |
| xsltSortFunction = xsltDefaultSortFunction; |
| } |
| |
| /** |
| * xsltSetCtxtSortFunc: |
| * @ctxt: a XSLT process context |
| * @handler: the new handler function |
| * |
| * Function to set the handler for XSLT sorting |
| * for the specified context. |
| * If the handler is NULL, then the global |
| * sort function will be called |
| */ |
| void |
| xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt, xsltSortFunc handler) { |
| ctxt->sortfunc = handler; |
| } |
| |
| /************************************************************************ |
| * * |
| * Parsing options * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xsltSetCtxtParseOptions: |
| * @ctxt: a XSLT process context |
| * @options: a combination of libxml2 xmlParserOption |
| * |
| * Change the default parser option passed by the XSLT engine to the |
| * parser when using document() loading. |
| * |
| * Returns the previous options or -1 in case of error |
| */ |
| int |
| xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt, int options) |
| { |
| int oldopts; |
| |
| if (ctxt == NULL) |
| return(-1); |
| oldopts = ctxt->parserOptions; |
| if (ctxt->xinclude) |
| oldopts |= XML_PARSE_XINCLUDE; |
| ctxt->parserOptions = options; |
| if (options & XML_PARSE_XINCLUDE) |
| ctxt->xinclude = 1; |
| else |
| ctxt->xinclude = 0; |
| return(oldopts); |
| } |
| |
| /************************************************************************ |
| * * |
| * Output * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xsltSaveResultTo: |
| * @buf: an output buffer |
| * @result: the result xmlDocPtr |
| * @style: the stylesheet |
| * |
| * Save the result @result obtained by applying the @style stylesheet |
| * to an I/O output channel @buf |
| * |
| * Returns the number of byte written or -1 in case of failure. |
| */ |
| int |
| xsltSaveResultTo(xmlOutputBufferPtr buf, xmlDocPtr result, |
| xsltStylesheetPtr style) { |
| const xmlChar *encoding; |
| int base; |
| const xmlChar *method; |
| int indent; |
| |
| if ((buf == NULL) || (result == NULL) || (style == NULL)) |
| return(-1); |
| if ((result->children == NULL) || |
| ((result->children->type == XML_DTD_NODE) && |
| (result->children->next == NULL))) |
| return(0); |
| |
| if ((style->methodURI != NULL) && |
| ((style->method == NULL) || |
| (!xmlStrEqual(style->method, (const xmlChar *) "xhtml")))) { |
| xsltGenericError(xsltGenericErrorContext, |
| "xsltSaveResultTo : unknown ouput method\n"); |
| return(-1); |
| } |
| |
| base = buf->written; |
| |
| XSLT_GET_IMPORT_PTR(method, style, method) |
| XSLT_GET_IMPORT_PTR(encoding, style, encoding) |
| XSLT_GET_IMPORT_INT(indent, style, indent); |
| |
| if ((method == NULL) && (result->type == XML_HTML_DOCUMENT_NODE)) |
| method = (const xmlChar *) "html"; |
| |
| if ((method != NULL) && |
| (xmlStrEqual(method, (const xmlChar *) "html"))) { |
| if (encoding != NULL) { |
| htmlSetMetaEncoding(result, (const xmlChar *) encoding); |
| } else { |
| htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8"); |
| } |
| if (indent == -1) |
| indent = 1; |
| htmlDocContentDumpFormatOutput(buf, result, (const char *) encoding, |
| indent); |
| xmlOutputBufferFlush(buf); |
| } else if ((method != NULL) && |
| (xmlStrEqual(method, (const xmlChar *) "xhtml"))) { |
| if (encoding != NULL) { |
| htmlSetMetaEncoding(result, (const xmlChar *) encoding); |
| } else { |
| htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8"); |
| } |
| htmlDocContentDumpOutput(buf, result, (const char *) encoding); |
| xmlOutputBufferFlush(buf); |
| } else if ((method != NULL) && |
| (xmlStrEqual(method, (const xmlChar *) "text"))) { |
| xmlNodePtr cur; |
| |
| cur = result->children; |
| while (cur != NULL) { |
| if (cur->type == XML_TEXT_NODE) |
| xmlOutputBufferWriteString(buf, (const char *) cur->content); |
| |
| /* |
| * Skip to next node |
| */ |
| if (cur->children != NULL) { |
| if ((cur->children->type != XML_ENTITY_DECL) && |
| (cur->children->type != XML_ENTITY_REF_NODE) && |
| (cur->children->type != XML_ENTITY_NODE)) { |
| cur = cur->children; |
| continue; |
| } |
| } |
| if (cur->next != NULL) { |
| cur = cur->next; |
| continue; |
| } |
| |
| do { |
| cur = cur->parent; |
| if (cur == NULL) |
| break; |
| if (cur == (xmlNodePtr) style->doc) { |
| cur = NULL; |
| break; |
| } |
| if (cur->next != NULL) { |
| cur = cur->next; |
| break; |
| } |
| } while (cur != NULL); |
| } |
| xmlOutputBufferFlush(buf); |
| } else { |
| int omitXmlDecl; |
| int standalone; |
| |
| XSLT_GET_IMPORT_INT(omitXmlDecl, style, omitXmlDeclaration); |
| XSLT_GET_IMPORT_INT(standalone, style, standalone); |
| |
| if (omitXmlDecl != 1) { |
| xmlOutputBufferWriteString(buf, "<?xml version="); |
| if (result->version != NULL) |
| xmlBufferWriteQuotedString(buf->buffer, result->version); |
| else |
| xmlOutputBufferWriteString(buf, "\"1.0\""); |
| if (encoding == NULL) { |
| if (result->encoding != NULL) |
| encoding = result->encoding; |
| else if (result->charset != XML_CHAR_ENCODING_UTF8) |
| encoding = (const xmlChar *) |
| xmlGetCharEncodingName((xmlCharEncoding) |
| result->charset); |
| } |
| if (encoding != NULL) { |
| xmlOutputBufferWriteString(buf, " encoding="); |
| xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding); |
| } |
| switch (standalone) { |
| case 0: |
| xmlOutputBufferWriteString(buf, " standalone=\"no\""); |
| break; |
| case 1: |
| xmlOutputBufferWriteString(buf, " standalone=\"yes\""); |
| break; |
| default: |
| break; |
| } |
| xmlOutputBufferWriteString(buf, "?>\n"); |
| } |
| if (result->children != NULL) { |
| xmlNodePtr child = result->children; |
| |
| while (child != NULL) { |
| xmlNodeDumpOutput(buf, result, child, 0, (indent == 1), |
| (const char *) encoding); |
| if ((child->type == XML_DTD_NODE) || |
| ((child->type == XML_COMMENT_NODE) && |
| (child->next != NULL))) |
| xmlOutputBufferWriteString(buf, "\n"); |
| child = child->next; |
| } |
| xmlOutputBufferWriteString(buf, "\n"); |
| } |
| xmlOutputBufferFlush(buf); |
| } |
| return(buf->written - base); |
| } |
| |
| /** |
| * xsltSaveResultToFilename: |
| * @URL: a filename or URL |
| * @result: the result xmlDocPtr |
| * @style: the stylesheet |
| * @compression: the compression factor (0 - 9 included) |
| * |
| * Save the result @result obtained by applying the @style stylesheet |
| * to a file or @URL |
| * |
| * Returns the number of byte written or -1 in case of failure. |
| */ |
| int |
| xsltSaveResultToFilename(const char *URL, xmlDocPtr result, |
| xsltStylesheetPtr style, int compression) { |
| xmlOutputBufferPtr buf; |
| const xmlChar *encoding; |
| int ret; |
| |
| if ((URL == NULL) || (result == NULL) || (style == NULL)) |
| return(-1); |
| if (result->children == NULL) |
| return(0); |
| |
| XSLT_GET_IMPORT_PTR(encoding, style, encoding) |
| if (encoding != NULL) { |
| xmlCharEncodingHandlerPtr encoder; |
| |
| encoder = xmlFindCharEncodingHandler((char *)encoding); |
| if ((encoder != NULL) && |
| (xmlStrEqual((const xmlChar *)encoder->name, |
| (const xmlChar *) "UTF-8"))) |
| encoder = NULL; |
| buf = xmlOutputBufferCreateFilename(URL, encoder, compression); |
| } else { |
| buf = xmlOutputBufferCreateFilename(URL, NULL, compression); |
| } |
| if (buf == NULL) |
| return(-1); |
| xsltSaveResultTo(buf, result, style); |
| ret = xmlOutputBufferClose(buf); |
| return(ret); |
| } |
| |
| /** |
| * xsltSaveResultToFile: |
| * @file: a FILE * I/O |
| * @result: the result xmlDocPtr |
| * @style: the stylesheet |
| * |
| * Save the result @result obtained by applying the @style stylesheet |
| * to an open FILE * I/O. |
| * This does not close the FILE @file |
| * |
| * Returns the number of bytes written or -1 in case of failure. |
| */ |
| int |
| xsltSaveResultToFile(FILE *file, xmlDocPtr result, xsltStylesheetPtr style) { |
| xmlOutputBufferPtr buf; |
| const xmlChar *encoding; |
| int ret; |
| |
| if ((file == NULL) || (result == NULL) || (style == NULL)) |
| return(-1); |
| if (result->children == NULL) |
| return(0); |
| |
| XSLT_GET_IMPORT_PTR(encoding, style, encoding) |
| if (encoding != NULL) { |
| xmlCharEncodingHandlerPtr encoder; |
| |
| encoder = xmlFindCharEncodingHandler((char *)encoding); |
| if ((encoder != NULL) && |
| (xmlStrEqual((const xmlChar *)encoder->name, |
| (const xmlChar *) "UTF-8"))) |
| encoder = NULL; |
| buf = xmlOutputBufferCreateFile(file, encoder); |
| } else { |
| buf = xmlOutputBufferCreateFile(file, NULL); |
| } |
| |
| if (buf == NULL) |
| return(-1); |
| xsltSaveResultTo(buf, result, style); |
| ret = xmlOutputBufferClose(buf); |
| return(ret); |
| } |
| |
| /** |
| * xsltSaveResultToFd: |
| * @fd: a file descriptor |
| * @result: the result xmlDocPtr |
| * @style: the stylesheet |
| * |
| * Save the result @result obtained by applying the @style stylesheet |
| * to an open file descriptor |
| * This does not close the descriptor. |
| * |
| * Returns the number of bytes written or -1 in case of failure. |
| */ |
| int |
| xsltSaveResultToFd(int fd, xmlDocPtr result, xsltStylesheetPtr style) { |
| xmlOutputBufferPtr buf; |
| const xmlChar *encoding; |
| int ret; |
| |
| if ((fd < 0) || (result == NULL) || (style == NULL)) |
| return(-1); |
| if (result->children == NULL) |
| return(0); |
| |
| XSLT_GET_IMPORT_PTR(encoding, style, encoding) |
| if (encoding != NULL) { |
| xmlCharEncodingHandlerPtr encoder; |
| |
| encoder = xmlFindCharEncodingHandler((char *)encoding); |
| if ((encoder != NULL) && |
| (xmlStrEqual((const xmlChar *)encoder->name, |
| (const xmlChar *) "UTF-8"))) |
| encoder = NULL; |
| buf = xmlOutputBufferCreateFd(fd, encoder); |
| } else { |
| buf = xmlOutputBufferCreateFd(fd, NULL); |
| } |
| if (buf == NULL) |
| return(-1); |
| xsltSaveResultTo(buf, result, style); |
| ret = xmlOutputBufferClose(buf); |
| return(ret); |
| } |
| |
| /** |
| * xsltSaveResultToString: |
| * @doc_txt_ptr: Memory pointer for allocated XML text |
| * @doc_txt_len: Length of the generated XML text |
| * @result: the result xmlDocPtr |
| * @style: the stylesheet |
| * |
| * Save the result @result obtained by applying the @style stylesheet |
| * to a new allocated string. |
| * |
| * Returns 0 in case of success and -1 in case of error |
| */ |
| int |
| xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len, |
| xmlDocPtr result, xsltStylesheetPtr style) { |
| xmlOutputBufferPtr buf; |
| const xmlChar *encoding; |
| |
| *doc_txt_ptr = NULL; |
| *doc_txt_len = 0; |
| if (result->children == NULL) |
| return(0); |
| |
| XSLT_GET_IMPORT_PTR(encoding, style, encoding) |
| if (encoding != NULL) { |
| xmlCharEncodingHandlerPtr encoder; |
| |
| encoder = xmlFindCharEncodingHandler((char *)encoding); |
| if ((encoder != NULL) && |
| (xmlStrEqual((const xmlChar *)encoder->name, |
| (const xmlChar *) "UTF-8"))) |
| encoder = NULL; |
| buf = xmlAllocOutputBuffer(encoder); |
| } else { |
| buf = xmlAllocOutputBuffer(NULL); |
| } |
| if (buf == NULL) |
| return(-1); |
| xsltSaveResultTo(buf, result, style); |
| if (buf->conv != NULL) { |
| *doc_txt_len = buf->conv->use; |
| *doc_txt_ptr = xmlStrndup(buf->conv->content, *doc_txt_len); |
| } else { |
| *doc_txt_len = buf->buffer->use; |
| *doc_txt_ptr = xmlStrndup(buf->buffer->content, *doc_txt_len); |
| } |
| (void)xmlOutputBufferClose(buf); |
| return 0; |
| } |
| |
| /************************************************************************ |
| * * |
| * Generating profiling informations * |
| * * |
| ************************************************************************/ |
| |
| static long calibration = -1; |
| |
| /** |
| * xsltCalibrateTimestamps: |
| * |
| * Used for to calibrate the xsltTimestamp() function |
| * Should work if launched at startup and we don't loose our quantum :-) |
| * |
| * Returns the number of milliseconds used by xsltTimestamp() |
| */ |
| static long |
| xsltCalibrateTimestamps(void) { |
| register int i; |
| |
| for (i = 0;i < 999;i++) |
| xsltTimestamp(); |
| return(xsltTimestamp() / 1000); |
| } |
| |
| /** |
| * xsltCalibrateAdjust: |
| * @delta: a negative dealy value found |
| * |
| * Used for to correct the calibration for xsltTimestamp() |
| */ |
| void |
| xsltCalibrateAdjust(long delta) { |
| calibration += delta; |
| } |
| |
| /** |
| * xsltTimestamp: |
| * |
| * Used for gathering profiling data |
| * |
| * Returns the number of tenth of milliseconds since the beginning of the |
| * profiling |
| */ |
| long |
| xsltTimestamp(void) |
| { |
| #ifdef XSLT_WIN32_PERFORMANCE_COUNTER |
| BOOL ok; |
| LARGE_INTEGER performanceCount; |
| LARGE_INTEGER performanceFrequency; |
| LONGLONG quadCount; |
| double seconds; |
| static LONGLONG startupQuadCount = 0; |
| static LONGLONG startupQuadFreq = 0; |
| |
| ok = QueryPerformanceCounter(&performanceCount); |
| if (!ok) |
| return 0; |
| quadCount = performanceCount.QuadPart; |
| if (calibration < 0) { |
| calibration = 0; |
| ok = QueryPerformanceFrequency(&performanceFrequency); |
| if (!ok) |
| return 0; |
| startupQuadFreq = performanceFrequency.QuadPart; |
| startupQuadCount = quadCount; |
| return (0); |
| } |
| if (startupQuadFreq == 0) |
| return 0; |
| seconds = (quadCount - startupQuadCount) / (double) startupQuadFreq; |
| return (long) (seconds * XSLT_TIMESTAMP_TICS_PER_SEC); |
| |
| #else /* XSLT_WIN32_PERFORMANCE_COUNTER */ |
| #ifdef HAVE_GETTIMEOFDAY |
| static struct timeval startup; |
| struct timeval cur; |
| long tics; |
| |
| if (calibration < 0) { |
| gettimeofday(&startup, NULL); |
| calibration = 0; |
| calibration = xsltCalibrateTimestamps(); |
| gettimeofday(&startup, NULL); |
| return (0); |
| } |
| |
| gettimeofday(&cur, NULL); |
| tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC; |
| tics += (cur.tv_usec - startup.tv_usec) / |
| (1000000l / XSLT_TIMESTAMP_TICS_PER_SEC); |
| |
| tics -= calibration; |
| return(tics); |
| #else |
| |
| /* Neither gettimeofday() nor Win32 performance counter available */ |
| |
| return (0); |
| |
| #endif /* HAVE_GETTIMEOFDAY */ |
| #endif /* XSLT_WIN32_PERFORMANCE_COUNTER */ |
| } |
| |
| #define MAX_TEMPLATES 10000 |
| |
| /** |
| * xsltSaveProfiling: |
| * @ctxt: an XSLT context |
| * @output: a FILE * for saving the informations |
| * |
| * Save the profiling informations on @output |
| */ |
| void |
| xsltSaveProfiling(xsltTransformContextPtr ctxt, FILE *output) { |
| int nb, i,j; |
| int max; |
| int total; |
| long totalt; |
| xsltTemplatePtr *templates; |
| xsltStylesheetPtr style; |
| xsltTemplatePtr template; |
| |
| if ((output == NULL) || (ctxt == NULL)) |
| return; |
| if (ctxt->profile == 0) |
| return; |
| |
| nb = 0; |
| max = MAX_TEMPLATES; |
| templates = xmlMalloc(max * sizeof(xsltTemplatePtr)); |
| if (templates == NULL) |
| return; |
| |
| style = ctxt->style; |
| while (style != NULL) { |
| template = style->templates; |
| while (template != NULL) { |
| if (nb >= max) |
| break; |
| |
| if (template->nbCalls > 0) |
| templates[nb++] = template; |
| template = template->next; |
| } |
| |
| style = xsltNextImport(style); |
| } |
| |
| for (i = 0;i < nb -1;i++) { |
| for (j = i + 1; j < nb; j++) { |
| if ((templates[i]->time <= templates[j]->time) || |
| ((templates[i]->time == templates[j]->time) && |
| (templates[i]->nbCalls <= templates[j]->nbCalls))) { |
| template = templates[j]; |
| templates[j] = templates[i]; |
| templates[i] = template; |
| } |
| } |
| } |
| |
| fprintf(output, "%6s%20s%20s%10s Calls Tot 100us Avg\n\n", |
| "number", "match", "name", "mode"); |
| total = 0; |
| totalt = 0; |
| for (i = 0;i < nb;i++) { |
| fprintf(output, "%5d ", i); |
| if (templates[i]->match != NULL) { |
| if (xmlStrlen(templates[i]->match) > 20) |
| fprintf(output, "%s\n%26s", templates[i]->match, ""); |
| else |
| fprintf(output, "%20s", templates[i]->match); |
| } else { |
| fprintf(output, "%20s", ""); |
| } |
| if (templates[i]->name != NULL) { |
| if (xmlStrlen(templates[i]->name) > 20) |
| fprintf(output, "%s\n%46s", templates[i]->name, ""); |
| else |
| fprintf(output, "%20s", templates[i]->name); |
| } else { |
| fprintf(output, "%20s", ""); |
| } |
| if (templates[i]->mode != NULL) { |
| if (xmlStrlen(templates[i]->mode) > 10) |
| fprintf(output, "%s\n%56s", templates[i]->mode, ""); |
| else |
| fprintf(output, "%10s", templates[i]->mode); |
| } else { |
| fprintf(output, "%10s", ""); |
| } |
| fprintf(output, " %6d", templates[i]->nbCalls); |
| fprintf(output, " %6ld %6ld\n", templates[i]->time, |
| templates[i]->time / templates[i]->nbCalls); |
| total += templates[i]->nbCalls; |
| totalt += templates[i]->time; |
| } |
| fprintf(output, "\n%30s%26s %6d %6ld\n", "Total", "", total, totalt); |
| |
| xmlFree(templates); |
| } |
| |
| /************************************************************************ |
| * * |
| * Fetching profiling informations * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xsltGetProfileInformation: |
| * @ctxt: a transformation context |
| * |
| * This function should be called after the transformation completed |
| * to extract template processing profiling informations if availble. |
| * The informations are returned as an XML document tree like |
| * <?xml version="1.0"?> |
| * <profile> |
| * <template rank="1" match="*" name="" |
| * mode="" calls="6" time="48" average="8"/> |
| * <template rank="2" match="item2|item3" name="" |
| * mode="" calls="10" time="30" average="3"/> |
| * <template rank="3" match="item1" name="" |
| * mode="" calls="5" time="17" average="3"/> |
| * </profile> |
| * The caller will need to free up the returned tree with xmlFreeDoc() |
| * |
| * Returns the xmlDocPtr corresponding to the result or NULL if not available. |
| */ |
| |
| xmlDocPtr |
| xsltGetProfileInformation(xsltTransformContextPtr ctxt) |
| { |
| xmlDocPtr ret = NULL; |
| xmlNodePtr root, child; |
| char buf[100]; |
| |
| xsltStylesheetPtr style; |
| xsltTemplatePtr *templates; |
| xsltTemplatePtr templ; |
| int nb = 0, max = 0, i, j; |
| |
| if (!ctxt) |
| return NULL; |
| |
| if (!ctxt->profile) |
| return NULL; |
| |
| nb = 0; |
| max = 10000; |
| templates = |
| (xsltTemplatePtr *) xmlMalloc(max * sizeof(xsltTemplatePtr)); |
| if (templates == NULL) |
| return NULL; |
| |
| /* |
| * collect all the templates in an array |
| */ |
| style = ctxt->style; |
| while (style != NULL) { |
| templ = style->templates; |
| while (templ != NULL) { |
| if (nb >= max) |
| break; |
| |
| if (templ->nbCalls > 0) |
| templates[nb++] = templ; |
| templ = templ->next; |
| } |
| |
| style = (xsltStylesheetPtr) xsltNextImport(style); |
| } |
| |
| /* |
| * Sort the array by time spent |
| */ |
| for (i = 0; i < nb - 1; i++) { |
| for (j = i + 1; j < nb; j++) { |
| if ((templates[i]->time <= templates[j]->time) || |
| ((templates[i]->time == templates[j]->time) && |
| (templates[i]->nbCalls <= templates[j]->nbCalls))) { |
| templ = templates[j]; |
| templates[j] = templates[i]; |
| templates[i] = templ; |
| } |
| } |
| } |
| |
| /* |
| * Generate a document corresponding to the results. |
| */ |
| ret = xmlNewDoc(BAD_CAST "1.0"); |
| root = xmlNewDocNode(ret, NULL, BAD_CAST "profile", NULL); |
| xmlDocSetRootElement(ret, root); |
| |
| for (i = 0; i < nb; i++) { |
| child = xmlNewChild(root, NULL, BAD_CAST "template", NULL); |
| sprintf(buf, "%d", i + 1); |
| xmlSetProp(child, BAD_CAST "rank", BAD_CAST buf); |
| xmlSetProp(child, BAD_CAST "match", BAD_CAST templates[i]->match); |
| xmlSetProp(child, BAD_CAST "name", BAD_CAST templates[i]->name); |
| xmlSetProp(child, BAD_CAST "mode", BAD_CAST templates[i]->mode); |
| |
| sprintf(buf, "%d", templates[i]->nbCalls); |
| xmlSetProp(child, BAD_CAST "calls", BAD_CAST buf); |
| |
| sprintf(buf, "%ld", templates[i]->time); |
| xmlSetProp(child, BAD_CAST "time", BAD_CAST buf); |
| |
| sprintf(buf, "%ld", templates[i]->time / templates[i]->nbCalls); |
| xmlSetProp(child, BAD_CAST "average", BAD_CAST buf); |
| }; |
| |
| xmlFree(templates); |
| |
| return ret; |
| } |
| |
| /************************************************************************ |
| * * |
| * Hooks for libxml2 XPath * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xsltXPathCompile: |
| * @style: the stylesheet |
| * @str: the XPath expression |
| * |
| * Compile an XPath expression |
| * |
| * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL. |
| * the caller has to free the object. |
| */ |
| xmlXPathCompExprPtr |
| xsltXPathCompile(xsltStylesheetPtr style, const xmlChar *str) { |
| xmlXPathContextPtr xpathCtxt; |
| xmlXPathCompExprPtr ret; |
| |
| if (style != NULL) { |
| #ifdef XSLT_REFACTORED_XPATHCOMP |
| if (XSLT_CCTXT(style)) { |
| /* |
| * Proposed by Jerome Pesenti |
| * -------------------------- |
| * For better efficiency we'll reuse the compilation |
| * context's XPath context. For the common stylesheet using |
| * XPath expressions this will reduce compilation time to |
| * about 50%. |
| * |
| * See http://mail.gnome.org/archives/xslt/2006-April/msg00037.html |
| */ |
| xpathCtxt = XSLT_CCTXT(style)->xpathCtxt; |
| xpathCtxt->doc = style->doc; |
| } else |
| xpathCtxt = xmlXPathNewContext(style->doc); |
| #else |
| xpathCtxt = xmlXPathNewContext(style->doc); |
| #endif |
| if (xpathCtxt == NULL) |
| return NULL; |
| xpathCtxt->dict = style->dict; |
| } else { |
| xpathCtxt = xmlXPathNewContext(NULL); |
| if (xpathCtxt == NULL) |
| return NULL; |
| } |
| /* |
| * Compile the expression. |
| */ |
| ret = xmlXPathCtxtCompile(xpathCtxt, str); |
| |
| #ifdef XSLT_REFACTORED_XPATHCOMP |
| if ((style == NULL) || (! XSLT_CCTXT(style))) { |
| xmlXPathFreeContext(xpathCtxt); |
| } |
| #else |
| xmlXPathFreeContext(xpathCtxt); |
| #endif |
| /* |
| * TODO: there is a lot of optimizations which should be possible |
| * like variable slot precomputations, function precomputations, etc. |
| */ |
| |
| return(ret); |
| } |
| |
| /************************************************************************ |
| * * |
| * Hooks for the debugger * |
| * * |
| ************************************************************************/ |
| |
| /* |
| * There is currently only 3 debugging callback defined |
| * Debugger callbacks are disabled by default |
| */ |
| #define XSLT_CALLBACK_NUMBER 3 |
| |
| typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks; |
| typedef xsltDebuggerCallbacks *xsltDebuggerCallbacksPtr; |
| struct _xsltDebuggerCallbacks { |
| xsltHandleDebuggerCallback handler; |
| xsltAddCallCallback add; |
| xsltDropCallCallback drop; |
| }; |
| |
| static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks = { |
| NULL, /* handler */ |
| NULL, /* add */ |
| NULL /* drop */ |
| }; |
| |
| int xslDebugStatus; |
| |
| /** |
| * xsltSetDebuggerStatus: |
| * @value : the value to be set |
| * |
| * This function sets the value of xslDebugStatus. |
| */ |
| void |
| xsltSetDebuggerStatus(int value) |
| { |
| xslDebugStatus = value; |
| } |
| |
| /** |
| * xsltGetDebuggerStatus: |
| * |
| * Get xslDebugStatus. |
| * |
| * Returns the value of xslDebugStatus. |
| */ |
| int |
| xsltGetDebuggerStatus(void) |
| { |
| return(xslDebugStatus); |
| } |
| |
| /** |
| * xsltSetDebuggerCallbacks: |
| * @no : number of callbacks |
| * @block : the block of callbacks |
| * |
| * This function allow to plug a debugger into the XSLT library |
| * @block points to a block of memory containing the address of @no |
| * callback routines. |
| * |
| * Returns 0 in case of success and -1 in case of error |
| */ |
| int |
| xsltSetDebuggerCallbacks(int no, void *block) |
| { |
| xsltDebuggerCallbacksPtr callbacks; |
| |
| if ((block == NULL) || (no != XSLT_CALLBACK_NUMBER)) |
| return(-1); |
| |
| callbacks = (xsltDebuggerCallbacksPtr) block; |
| xsltDebuggerCurrentCallbacks.handler = callbacks->handler; |
| xsltDebuggerCurrentCallbacks.add = callbacks->add; |
| xsltDebuggerCurrentCallbacks.drop = callbacks->drop; |
| return(0); |
| } |
| |
| /** |
| * xslHandleDebugger: |
| * @cur : source node being executed |
| * @node : data node being processed |
| * @templ : temlate that applies to node |
| * @ctxt : the xslt transform context |
| * |
| * If either cur or node are a breakpoint, or xslDebugStatus in state |
| * where debugging must occcur at this time then transfer control |
| * to the xslDebugBreak function |
| */ |
| void |
| xslHandleDebugger(xmlNodePtr cur, xmlNodePtr node, xsltTemplatePtr templ, |
| xsltTransformContextPtr ctxt) |
| { |
| if (xsltDebuggerCurrentCallbacks.handler != NULL) |
| xsltDebuggerCurrentCallbacks.handler(cur, node, templ, ctxt); |
| } |
| |
| /** |
| * xslAddCall: |
| * @templ : current template being applied |
| * @source : the source node being processed |
| * |
| * Add template "call" to call stack |
| * Returns : 1 on sucess 0 otherwise an error may be printed if |
| * WITH_XSLT_DEBUG_BREAKPOINTS is defined |
| */ |
| int |
| xslAddCall(xsltTemplatePtr templ, xmlNodePtr source) |
| { |
| if (xsltDebuggerCurrentCallbacks.add != NULL) |
| return(xsltDebuggerCurrentCallbacks.add(templ, source)); |
| return(0); |
| } |
| |
| /** |
| * xslDropCall: |
| * |
| * Drop the topmost item off the call stack |
| */ |
| void |
| xslDropCall(void) |
| { |
| if (xsltDebuggerCurrentCallbacks.drop != NULL) |
| xsltDebuggerCurrentCallbacks.drop(); |
| } |
| |