/* * 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. * * [email protected] */ #define IN_LIBXSLT #include "libxslt.h" #ifndef XSLT_NEED_TRIO #include <stdio.h> #else #include <trio.h> #endif #include <string.h> #include <stdlib.h> #include <stdarg.h> #include <time.h> #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #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" #if defined(_WIN32) #include <windows.h> #define XSLT_WIN32_PERFORMANCE_COUNTER #endif /************************************************************************ * * * 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) { … } /** * 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) { … } /** * 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) { … } /** * xsltGetUTF8CharZ: * @utf: a sequence of UTF-8 encoded bytes * @len: a pointer to @bytes len * * Read one UTF8 Char from a null-terminated string. * * Returns the char value or -1 in case of error and update @len with the * number of bytes used */ int xsltGetUTF8CharZ(const unsigned char *utf, int *len) { … } #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) { … } /************************************************************************ * * * Handling of out of context errors * * * ************************************************************************/ #define XSLT_GET_VAR_STR(msg, str) … /** * 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 LIBXSLT_ATTR_FORMAT(2,3) xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { … } xmlGenericErrorFunc xsltGenericError = …; void *xsltGenericErrorContext = …; /** * 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) { … } /** * 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 LIBXSLT_ATTR_FORMAT(2,3) xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { … } xmlGenericErrorFunc xsltGenericDebug = …; void *xsltGenericDebugContext = …; /** * 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) { … } /** * 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) { … } /** * 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) { … } /** * 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, ...) { … } /************************************************************************ * * * 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) { … } /** * 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) { … } /** * 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) { … } /************************************************************************ * * * 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) { … } /** * xsltComputeSortResultInternal: * @ctxt: a XSLT process context * @sort: xsl:sort node * @number: data-type is number * @locale: transform strings according to locale * * 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. */ static xmlXPathObjectPtr * xsltComputeSortResultInternal(xsltTransformContextPtr ctxt, xmlNodePtr sort, int number, void *locale) { … } /** * 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) { … } /** * 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) { … } static xsltSortFunc xsltSortFunction = …; /** * 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) { … } /** * xsltSetSortFunc: * @handler: the new handler function * * DEPRECATED: Use xsltSetCtxtLocaleHandlers. * * 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) { … } /** * xsltSetCtxtSortFunc: * @ctxt: a XSLT process context * @handler: the new handler function * * DEPRECATED: Use xsltSetCtxtLocaleHandlers. * * 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) { … } /** * xsltSetCtxtLocaleHandlers: * @ctxt: an XSLT transform context * @newLocale: locale constructor * @freeLocale: locale destructor * @genSortKey: sort key generator * * Set the locale handlers. */ void xsltSetCtxtLocaleHandlers(xsltTransformContextPtr ctxt, xsltNewLocaleFunc newLocale, xsltFreeLocaleFunc freeLocale, xsltGenSortKeyFunc genSortKey) { … } /************************************************************************ * * * 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) { … } /************************************************************************ * * * 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) { … } /** * 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) { … } /** * 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) { … } /** * 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) { … } /** * 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) { … } /** * xsltGetSourceNodeFlags: * @node: Node from source document * * Returns the flags for a source node. */ int xsltGetSourceNodeFlags(xmlNodePtr node) { … } /** * xsltSetSourceNodeFlags: * @node: Node from source document * @flags: Flags * * Sets the specified flags to 1. * * Returns 0 on success, -1 on error. */ int xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node, int flags) { … } /** * xsltClearSourceNodeFlags: * @node: Node from source document * @flags: Flags * * Sets the specified flags to 0. * * Returns 0 on success, -1 on error. */ int xsltClearSourceNodeFlags(xmlNodePtr node, int flags) { … } /** * xsltGetPSVIPtr: * @cur: Node * * Returns a pointer to the psvi member of a node or NULL on error. */ void ** xsltGetPSVIPtr(xmlNodePtr cur) { … } #ifdef WITH_PROFILER /************************************************************************ * * * Generating profiling information * * * ************************************************************************/ static long calibration = …; /** * 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() */ #if !defined(XSLT_WIN32_PERFORMANCE_COUNTER) && \ (defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY)) static long xsltCalibrateTimestamps(void) { … } #endif /** * xsltCalibrateAdjust: * @delta: a negative dealy value found * * Used for to correct the calibration for xsltTimestamp() */ void xsltCalibrateAdjust(long delta) { … } /** * xsltTimestamp: * * Used for gathering profiling data * * Returns the number of tenth of milliseconds since the beginning of the * profiling */ long xsltTimestamp(void) { … } static char * pretty_templ_match(xsltTemplatePtr templ) { … } #define MAX_TEMPLATES … /** * xsltSaveProfiling: * @ctxt: an XSLT context * @output: a FILE * for saving the information * * Save the profiling information on @output */ void xsltSaveProfiling(xsltTransformContextPtr ctxt, FILE *output) { … } /************************************************************************ * * * Fetching profiling information * * * ************************************************************************/ /** * xsltGetProfileInformation: * @ctxt: a transformation context * * This function should be called after the transformation completed * to extract template processing profiling information if available. * The information is 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) { … } #endif /* WITH_PROFILER */ /************************************************************************ * * * Hooks for libxml2 XPath * * * ************************************************************************/ /** * xsltXPathCompileFlags: * @style: the stylesheet * @str: the XPath expression * @flags: extra compilation flags to pass down to libxml2 XPath * * Compile an XPath expression * * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL. * the caller has to free the object. */ xmlXPathCompExprPtr xsltXPathCompileFlags(xsltStylesheetPtr style, const xmlChar *str, int flags) { … } /** * 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) { … } /************************************************************************ * * * Hooks for the debugger * * * ************************************************************************/ int xslDebugStatus; /** * xsltGetDebuggerStatus: * * Get xslDebugStatus. * * Returns the value of xslDebugStatus. */ int xsltGetDebuggerStatus(void) { … } #ifdef WITH_DEBUGGER /* * There is currently only 3 debugging callback defined * Debugger callbacks are disabled by default */ #define XSLT_CALLBACK_NUMBER … typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks; typedef xsltDebuggerCallbacks *xsltDebuggerCallbacksPtr; struct _xsltDebuggerCallbacks { xsltHandleDebuggerCallback handler; xsltAddCallCallback add; xsltDropCallCallback drop; }; static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks = { NULL, /* handler */ NULL, /* add */ NULL /* drop */ }; /** * xsltSetDebuggerStatus: * @value : the value to be set * * This function sets the value of xslDebugStatus. */ void xsltSetDebuggerStatus(int value) { xslDebugStatus = value; } /** * 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(); } #endif /* WITH_DEBUGGER */