diff options
Diffstat (limited to 'libxslt/extensions.c')
-rw-r--r-- | libxslt/extensions.c | 2365 |
1 files changed, 2365 insertions, 0 deletions
diff --git a/libxslt/extensions.c b/libxslt/extensions.c new file mode 100644 index 0000000..ae6eef0 --- /dev/null +++ b/libxslt/extensions.c @@ -0,0 +1,2365 @@ +/* + * extensions.c: Implemetation of the extensions support + * + * 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" + +#include <string.h> +#include <limits.h> + +#include <libxml/xmlmemory.h> +#include <libxml/tree.h> +#include <libxml/hash.h> +#include <libxml/xmlerror.h> +#include <libxml/parserInternals.h> +#include <libxml/xpathInternals.h> +#ifdef WITH_MODULES +#include <libxml/xmlmodule.h> +#endif +#include <libxml/list.h> +#include <libxml/xmlIO.h> +#include "xslt.h" +#include "xsltInternals.h" +#include "xsltutils.h" +#include "imports.h" +#include "extensions.h" + +#ifdef _WIN32 +#include <stdlib.h> /* for _MAX_PATH */ +#ifndef PATH_MAX +#define PATH_MAX _MAX_PATH +#endif +#endif + +#ifdef WITH_XSLT_DEBUG +#define WITH_XSLT_DEBUG_EXTENSIONS +#endif + +/************************************************************************ + * * + * Private Types and Globals * + * * + ************************************************************************/ + +typedef struct _xsltExtDef xsltExtDef; +typedef xsltExtDef *xsltExtDefPtr; +struct _xsltExtDef { + struct _xsltExtDef *next; + xmlChar *prefix; + xmlChar *URI; + void *data; +}; + +typedef struct _xsltExtModule xsltExtModule; +typedef xsltExtModule *xsltExtModulePtr; +struct _xsltExtModule { + xsltExtInitFunction initFunc; + xsltExtShutdownFunction shutdownFunc; + xsltStyleExtInitFunction styleInitFunc; + xsltStyleExtShutdownFunction styleShutdownFunc; +}; + +typedef struct _xsltExtData xsltExtData; +typedef xsltExtData *xsltExtDataPtr; +struct _xsltExtData { + xsltExtModulePtr extModule; + void *extData; +}; + +typedef struct _xsltExtElement xsltExtElement; +typedef xsltExtElement *xsltExtElementPtr; +struct _xsltExtElement { + xsltPreComputeFunction precomp; + xsltTransformFunction transform; +}; + +static xmlHashTablePtr xsltExtensionsHash = NULL; +static xmlHashTablePtr xsltFunctionsHash = NULL; +static xmlHashTablePtr xsltElementsHash = NULL; +static xmlHashTablePtr xsltTopLevelsHash = NULL; +static xmlHashTablePtr xsltModuleHash = NULL; +static xmlMutexPtr xsltExtMutex = NULL; + +/************************************************************************ + * * + * Type functions * + * * + ************************************************************************/ + +/** + * xsltNewExtDef: + * @prefix: the extension prefix + * @URI: the namespace URI + * + * Create a new XSLT ExtDef + * + * Returns the newly allocated xsltExtDefPtr or NULL in case of error + */ +static xsltExtDefPtr +xsltNewExtDef(const xmlChar * prefix, const xmlChar * URI) +{ + xsltExtDefPtr cur; + + cur = (xsltExtDefPtr) xmlMalloc(sizeof(xsltExtDef)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewExtDef : malloc failed\n"); + return (NULL); + } + memset(cur, 0, sizeof(xsltExtDef)); + if (prefix != NULL) + cur->prefix = xmlStrdup(prefix); + if (URI != NULL) + cur->URI = xmlStrdup(URI); + return (cur); +} + +/** + * xsltFreeExtDef: + * @extensiond: an XSLT extension definition + * + * Free up the memory allocated by @extensiond + */ +static void +xsltFreeExtDef(xsltExtDefPtr extensiond) +{ + if (extensiond == NULL) + return; + if (extensiond->prefix != NULL) + xmlFree(extensiond->prefix); + if (extensiond->URI != NULL) + xmlFree(extensiond->URI); + xmlFree(extensiond); +} + +/** + * xsltFreeExtDefList: + * @extensiond: an XSLT extension definition list + * + * Free up the memory allocated by all the elements of @extensiond + */ +static void +xsltFreeExtDefList(xsltExtDefPtr extensiond) +{ + xsltExtDefPtr cur; + + while (extensiond != NULL) { + cur = extensiond; + extensiond = extensiond->next; + xsltFreeExtDef(cur); + } +} + +/** + * xsltNewExtModule: + * @initFunc: the module initialization function + * @shutdownFunc: the module shutdown function + * @styleInitFunc: the stylesheet module data allocator function + * @styleShutdownFunc: the stylesheet module data free function + * + * Create a new XSLT extension module + * + * Returns the newly allocated xsltExtModulePtr or NULL in case of error + */ +static xsltExtModulePtr +xsltNewExtModule(xsltExtInitFunction initFunc, + xsltExtShutdownFunction shutdownFunc, + xsltStyleExtInitFunction styleInitFunc, + xsltStyleExtShutdownFunction styleShutdownFunc) +{ + xsltExtModulePtr cur; + + cur = (xsltExtModulePtr) xmlMalloc(sizeof(xsltExtModule)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewExtModule : malloc failed\n"); + return (NULL); + } + cur->initFunc = initFunc; + cur->shutdownFunc = shutdownFunc; + cur->styleInitFunc = styleInitFunc; + cur->styleShutdownFunc = styleShutdownFunc; + return (cur); +} + +/** + * xsltFreeExtModule: + * @ext: an XSLT extension module + * + * Free up the memory allocated by @ext + */ +static void +xsltFreeExtModule(xsltExtModulePtr ext) +{ + if (ext == NULL) + return; + xmlFree(ext); +} + +/** + * xsltNewExtData: + * @extModule: the module + * @extData: the associated data + * + * Create a new XSLT extension module data wrapper + * + * Returns the newly allocated xsltExtDataPtr or NULL in case of error + */ +static xsltExtDataPtr +xsltNewExtData(xsltExtModulePtr extModule, void *extData) +{ + xsltExtDataPtr cur; + + if (extModule == NULL) + return (NULL); + cur = (xsltExtDataPtr) xmlMalloc(sizeof(xsltExtData)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewExtData : malloc failed\n"); + return (NULL); + } + cur->extModule = extModule; + cur->extData = extData; + return (cur); +} + +/** + * xsltFreeExtData: + * @ext: an XSLT extension module data wrapper + * + * Free up the memory allocated by @ext + */ +static void +xsltFreeExtData(xsltExtDataPtr ext) +{ + if (ext == NULL) + return; + xmlFree(ext); +} + +/** + * xsltNewExtElement: + * @precomp: the pre-computation function + * @transform: the transformation function + * + * Create a new XSLT extension element + * + * Returns the newly allocated xsltExtElementPtr or NULL in case of + * error + */ +static xsltExtElementPtr +xsltNewExtElement(xsltPreComputeFunction precomp, + xsltTransformFunction transform) +{ + xsltExtElementPtr cur; + + if (transform == NULL) + return (NULL); + + cur = (xsltExtElementPtr) xmlMalloc(sizeof(xsltExtElement)); + if (cur == NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltNewExtElement : malloc failed\n"); + return (NULL); + } + cur->precomp = precomp; + cur->transform = transform; + return (cur); +} + +/** + * xsltFreeExtElement: + * @ext: an XSLT extension element + * + * Frees up the memory allocated by @ext + */ +static void +xsltFreeExtElement(xsltExtElementPtr ext) +{ + if (ext == NULL) + return; + xmlFree(ext); +} + + +#ifdef WITH_MODULES +typedef void (*exsltRegisterFunction) (void); + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +/** + * xsltExtModuleRegisterDynamic: + * @URI: the function or element namespace URI + * + * Dynamically loads an extension plugin when available. + * + * The plugin name is derived from the URI by removing the + * initial protocol designation, e.g. "http://", then converting + * the characters ".", "-", "/", and "\" into "_", the removing + * any trailing "/", then concatenating LIBXML_MODULE_EXTENSION. + * + * Plugins are loaded from the directory specified by the + * environment variable LIBXSLT_PLUGINS_PATH, or if NULL, + * by LIBXSLT_DEFAULT_PLUGINS_PATH() which is determined at + * compile time. + * + * Returns 0 if successful, -1 in case of error. + */ + +static int +xsltExtModuleRegisterDynamic(const xmlChar * URI) +{ + + xmlModulePtr m; + exsltRegisterFunction regfunc; + xmlChar *ext_name; + char module_filename[PATH_MAX]; + const xmlChar *ext_directory = NULL; + const xmlChar *protocol = NULL; + xmlChar *i, *regfunc_name; + void *vregfunc; + int rc; + + /* check for bad inputs */ + if (URI == NULL) + return (-1); + + if (NULL == xsltModuleHash) { + xsltModuleHash = xmlHashCreate(5); + if (xsltModuleHash == NULL) + return (-1); + } + + xmlMutexLock(xsltExtMutex); + + /* have we attempted to register this module already? */ + if (xmlHashLookup(xsltModuleHash, URI) != NULL) { + xmlMutexUnlock(xsltExtMutex); + return (-1); + } + xmlMutexUnlock(xsltExtMutex); + + /* transform extension namespace into a module name */ + protocol = xmlStrstr(URI, BAD_CAST "://"); + if (protocol == NULL) { + ext_name = xmlStrdup(URI); + } else { + ext_name = xmlStrdup(protocol + 3); + } + if (ext_name == NULL) { + return (-1); + } + + i = ext_name; + while ('\0' != *i) { + if (('/' == *i) || ('\\' == *i) || ('.' == *i) || ('-' == *i)) + *i = '_'; + i++; + } + + /* Strip underscores from end of string. */ + while (i > ext_name && *(i - 1) == '_') { + i--; + *i = '\0'; + } + + /* determine module directory */ + ext_directory = (xmlChar *) getenv("LIBXSLT_PLUGINS_PATH"); + + if (NULL == ext_directory) { + ext_directory = BAD_CAST LIBXSLT_DEFAULT_PLUGINS_PATH(); + if (NULL == ext_directory) + return (-1); + } +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + else + xsltGenericDebug(xsltGenericDebugContext, + "LIBXSLT_PLUGINS_PATH is %s\n", ext_directory); +#endif + + /* build the module filename, and confirm the module exists */ + xmlStrPrintf((xmlChar *) module_filename, sizeof(module_filename), + BAD_CAST "%s/%s%s", + ext_directory, ext_name, LIBXML_MODULE_EXTENSION); + +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Attempting to load plugin: %s for URI: %s\n", + module_filename, URI); +#endif + + if (1 != xmlCheckFilename(module_filename)) { + +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "xmlCheckFilename failed for plugin: %s\n", module_filename); +#endif + + xmlFree(ext_name); + return (-1); + } + + /* attempt to open the module */ + m = xmlModuleOpen(module_filename, 0); + if (NULL == m) { + +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "xmlModuleOpen failed for plugin: %s\n", module_filename); +#endif + + xmlFree(ext_name); + return (-1); + } + + /* construct initialization func name */ + regfunc_name = xmlStrdup(ext_name); + regfunc_name = xmlStrcat(regfunc_name, BAD_CAST "_init"); + + vregfunc = NULL; + rc = xmlModuleSymbol(m, (const char *) regfunc_name, &vregfunc); + regfunc = vregfunc; + if (0 == rc) { + /* + * Call the module's init function. Note that this function + * calls xsltRegisterExtModuleFull which will add the module + * to xsltExtensionsHash (together with it's entry points). + */ + (*regfunc) (); + + /* register this module in our hash */ + xmlMutexLock(xsltExtMutex); + xmlHashAddEntry(xsltModuleHash, URI, (void *) m); + xmlMutexUnlock(xsltExtMutex); + } else { + +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "xmlModuleSymbol failed for plugin: %s, regfunc: %s\n", + module_filename, regfunc_name); +#endif + + /* if regfunc not found unload the module immediately */ + xmlModuleClose(m); + } + + xmlFree(ext_name); + xmlFree(regfunc_name); + return (NULL == regfunc) ? -1 : 0; +} +#else +static int +xsltExtModuleRegisterDynamic(const xmlChar * URI ATTRIBUTE_UNUSED) +{ + return -1; +} +#endif + +/************************************************************************ + * * + * The stylesheet extension prefixes handling * + * * + ************************************************************************/ + + +/** + * xsltFreeExts: + * @style: an XSLT stylesheet + * + * Free up the memory used by XSLT extensions in a stylesheet + */ +void +xsltFreeExts(xsltStylesheetPtr style) +{ + if (style->nsDefs != NULL) + xsltFreeExtDefList((xsltExtDefPtr) style->nsDefs); +} + +/** + * xsltRegisterExtPrefix: + * @style: an XSLT stylesheet + * @prefix: the prefix used (optional) + * @URI: the URI associated to the extension + * + * Registers an extension namespace + * This is called from xslt.c during compile-time. + * The given prefix is not needed. + * Called by: + * xsltParseExtElemPrefixes() (new function) + * xsltRegisterExtPrefix() (old function) + * + * Returns 0 in case of success, 1 if the @URI was already + * registered as an extension namespace and + * -1 in case of failure + */ +int +xsltRegisterExtPrefix(xsltStylesheetPtr style, + const xmlChar * prefix, const xmlChar * URI) +{ + xsltExtDefPtr def, ret; + + if ((style == NULL) || (URI == NULL)) + return (-1); + +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Registering extension namespace '%s'.\n", URI); +#endif + def = (xsltExtDefPtr) style->nsDefs; +#ifdef XSLT_REFACTORED + /* + * The extension is associated with a namespace name. + */ + while (def != NULL) { + if (xmlStrEqual(URI, def->URI)) + return (1); + def = def->next; + } +#else + while (def != NULL) { + if (xmlStrEqual(prefix, def->prefix)) + return (-1); + def = def->next; + } +#endif + ret = xsltNewExtDef(prefix, URI); + if (ret == NULL) + return (-1); + ret->next = (xsltExtDefPtr) style->nsDefs; + style->nsDefs = ret; + + /* + * check whether there is an extension module with a stylesheet + * initialization function. + */ +#ifdef XSLT_REFACTORED + /* + * Don't initialize modules based on specified namespaces via + * the attribute "[xsl:]extension-element-prefixes". + */ +#else + if (xsltExtensionsHash != NULL) { + xsltExtModulePtr module; + + xmlMutexLock(xsltExtMutex); + module = xmlHashLookup(xsltExtensionsHash, URI); + xmlMutexUnlock(xsltExtMutex); + if (NULL == module) { + if (!xsltExtModuleRegisterDynamic(URI)) { + xmlMutexLock(xsltExtMutex); + module = xmlHashLookup(xsltExtensionsHash, URI); + xmlMutexUnlock(xsltExtMutex); + } + } + if (module != NULL) { + xsltStyleGetExtData(style, URI); + } + } +#endif + return (0); +} + +/************************************************************************ + * * + * The extensions modules interfaces * + * * + ************************************************************************/ + +/** + * xsltRegisterExtFunction: + * @ctxt: an XSLT transformation context + * @name: the name of the element + * @URI: the URI associated to the element + * @function: the actual implementation which should be called + * + * Registers an extension function + * + * Returns 0 in case of success, -1 in case of failure + */ +int +xsltRegisterExtFunction(xsltTransformContextPtr ctxt, const xmlChar * name, + const xmlChar * URI, xmlXPathFunction function) +{ + int ret; + + if ((ctxt == NULL) || (name == NULL) || + (URI == NULL) || (function == NULL)) + return (-1); + if (ctxt->xpathCtxt != NULL) { + xmlXPathRegisterFuncNS(ctxt->xpathCtxt, name, URI, function); + } + if (ctxt->extFunctions == NULL) + ctxt->extFunctions = xmlHashCreate(10); + if (ctxt->extFunctions == NULL) + return (-1); + + ret = xmlHashAddEntry2(ctxt->extFunctions, name, URI, + XML_CAST_FPTR(function)); + + return(ret); +} + +/** + * xsltRegisterExtElement: + * @ctxt: an XSLT transformation context + * @name: the name of the element + * @URI: the URI associated to the element + * @function: the actual implementation which should be called + * + * Registers an extension element + * + * Returns 0 in case of success, -1 in case of failure + */ +int +xsltRegisterExtElement(xsltTransformContextPtr ctxt, const xmlChar * name, + const xmlChar * URI, xsltTransformFunction function) +{ + if ((ctxt == NULL) || (name == NULL) || + (URI == NULL) || (function == NULL)) + return (-1); + if (ctxt->extElements == NULL) + ctxt->extElements = xmlHashCreate(10); + if (ctxt->extElements == NULL) + return (-1); + return (xmlHashAddEntry2 + (ctxt->extElements, name, URI, XML_CAST_FPTR(function))); +} + +/** + * xsltFreeCtxtExts: + * @ctxt: an XSLT transformation context + * + * Free the XSLT extension data + */ +void +xsltFreeCtxtExts(xsltTransformContextPtr ctxt) +{ + if (ctxt->extElements != NULL) + xmlHashFree(ctxt->extElements, NULL); + if (ctxt->extFunctions != NULL) + xmlHashFree(ctxt->extFunctions, NULL); +} + +/** + * xsltStyleGetStylesheetExtData: + * @style: an XSLT stylesheet + * @URI: the URI associated to the exension module + * + * Fires the compile-time initialization callback + * of an extension module and returns a container + * holding the user-data (retrieved via the callback). + * + * Returns the create module-data container + * or NULL if such a module was not registered. + */ +static xsltExtDataPtr +xsltStyleInitializeStylesheetModule(xsltStylesheetPtr style, + const xmlChar * URI) +{ + xsltExtDataPtr dataContainer; + void *userData = NULL; + xsltExtModulePtr module; + + if ((style == NULL) || (URI == NULL)) + return(NULL); + + if (xsltExtensionsHash == NULL) { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Not registered extension module: %s\n", URI); +#endif + return(NULL); + } + + xmlMutexLock(xsltExtMutex); + + module = xmlHashLookup(xsltExtensionsHash, URI); + + xmlMutexUnlock(xsltExtMutex); + + if (module == NULL) { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Not registered extension module: %s\n", URI); +#endif + return (NULL); + } + /* + * The specified module was registered so initialize it. + */ + if (style->extInfos == NULL) { + style->extInfos = xmlHashCreate(10); + if (style->extInfos == NULL) + return (NULL); + } + /* + * Fire the initialization callback if available. + */ + if (module->styleInitFunc == NULL) { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Initializing module with *no* callback: %s\n", URI); +#endif + } else { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Initializing module with callback: %s\n", URI); +#endif + /* + * Fire the initialization callback. + */ + userData = module->styleInitFunc(style, URI); + } + /* + * Store the user-data in the context of the given stylesheet. + */ + dataContainer = xsltNewExtData(module, userData); + if (dataContainer == NULL) + return (NULL); + + if (xmlHashAddEntry(style->extInfos, URI, + (void *) dataContainer) < 0) + { + xsltTransformError(NULL, style, NULL, + "Failed to register module '%s'.\n", URI); + style->errors++; + if (module->styleShutdownFunc) + module->styleShutdownFunc(style, URI, userData); + xsltFreeExtData(dataContainer); + return (NULL); + } + + return(dataContainer); +} + +/** + * xsltStyleGetExtData: + * @style: an XSLT stylesheet + * @URI: the URI associated to the exension module + * + * Retrieve the data associated to the extension module + * in this given stylesheet. + * Called by: + * xsltRegisterExtPrefix(), + * ( xsltExtElementPreCompTest(), xsltExtInitTest ) + * + * Returns the pointer or NULL if not present + */ +void * +xsltStyleGetExtData(xsltStylesheetPtr style, const xmlChar * URI) +{ + xsltExtDataPtr dataContainer = NULL; + xsltStylesheetPtr tmpStyle; + + if ((style == NULL) || (URI == NULL) || + (xsltExtensionsHash == NULL)) + return (NULL); + + +#ifdef XSLT_REFACTORED + /* + * This is intended for global storage, so only the main + * stylesheet will hold the data. + */ + tmpStyle = style; + while (tmpStyle->parent != NULL) + tmpStyle = tmpStyle->parent; + if (tmpStyle->extInfos != NULL) { + dataContainer = + (xsltExtDataPtr) xmlHashLookup(tmpStyle->extInfos, URI); + if (dataContainer != NULL) { + /* + * The module was already initialized in the context + * of this stylesheet; just return the user-data that + * comes with it. + */ + return(dataContainer->extData); + } + } +#else + /* + * Old behaviour. + */ + tmpStyle = style; + while (tmpStyle != NULL) { + if (tmpStyle->extInfos != NULL) { + dataContainer = + (xsltExtDataPtr) xmlHashLookup(tmpStyle->extInfos, URI); + if (dataContainer != NULL) { + return(dataContainer->extData); + } + } + tmpStyle = xsltNextImport(tmpStyle); + } + tmpStyle = style; +#endif + + dataContainer = + xsltStyleInitializeStylesheetModule(tmpStyle, URI); + if (dataContainer != NULL) + return (dataContainer->extData); + return(NULL); +} + +#ifdef XSLT_REFACTORED +/** + * xsltStyleStylesheetLevelGetExtData: + * @style: an XSLT stylesheet + * @URI: the URI associated to the exension module + * + * Retrieve the data associated to the extension module in this given + * stylesheet. + * + * Returns the pointer or NULL if not present + */ +void * +xsltStyleStylesheetLevelGetExtData(xsltStylesheetPtr style, + const xmlChar * URI) +{ + xsltExtDataPtr dataContainer = NULL; + + if ((style == NULL) || (URI == NULL) || + (xsltExtensionsHash == NULL)) + return (NULL); + + if (style->extInfos != NULL) { + dataContainer = (xsltExtDataPtr) xmlHashLookup(style->extInfos, URI); + /* + * The module was already initialized in the context + * of this stylesheet; just return the user-data that + * comes with it. + */ + if (dataContainer) + return(dataContainer->extData); + } + + dataContainer = + xsltStyleInitializeStylesheetModule(style, URI); + if (dataContainer != NULL) + return (dataContainer->extData); + return(NULL); +} +#endif + +/** + * xsltGetExtData: + * @ctxt: an XSLT transformation context + * @URI: the URI associated to the exension module + * + * Retrieve the data associated to the extension module in this given + * transformation. + * + * Returns the pointer or NULL if not present + */ +void * +xsltGetExtData(xsltTransformContextPtr ctxt, const xmlChar * URI) +{ + xsltExtDataPtr data; + + if ((ctxt == NULL) || (URI == NULL)) + return (NULL); + if (ctxt->extInfos == NULL) { + ctxt->extInfos = xmlHashCreate(10); + if (ctxt->extInfos == NULL) + return (NULL); + data = NULL; + } else { + data = (xsltExtDataPtr) xmlHashLookup(ctxt->extInfos, URI); + } + if (data == NULL) { + void *extData; + xsltExtModulePtr module; + + xmlMutexLock(xsltExtMutex); + + module = xmlHashLookup(xsltExtensionsHash, URI); + + xmlMutexUnlock(xsltExtMutex); + + if (module == NULL) { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Not registered extension module: %s\n", URI); +#endif + return (NULL); + } else { + if (module->initFunc == NULL) + return (NULL); + +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Initializing module: %s\n", URI); +#endif + + extData = module->initFunc(ctxt, URI); + if (extData == NULL) + return (NULL); + + data = xsltNewExtData(module, extData); + if (data == NULL) + return (NULL); + if (xmlHashAddEntry(ctxt->extInfos, URI, (void *) data) < 0) { + xsltTransformError(ctxt, NULL, NULL, + "Failed to register module data: %s\n", + URI); + if (module->shutdownFunc) + module->shutdownFunc(ctxt, URI, extData); + xsltFreeExtData(data); + return (NULL); + } + } + } + return (data->extData); +} + +typedef struct _xsltInitExtCtxt xsltInitExtCtxt; +struct _xsltInitExtCtxt { + xsltTransformContextPtr ctxt; + int ret; +}; + +/** + * xsltInitCtxtExt: + * @styleData: the registered stylesheet data for the module + * @ctxt: the XSLT transformation context + the return value + * @URI: the extension URI + * + * Initializes an extension module + */ +static void +xsltInitCtxtExt(xsltExtDataPtr styleData, xsltInitExtCtxt * ctxt, + const xmlChar * URI) +{ + xsltExtModulePtr module; + xsltExtDataPtr ctxtData; + void *extData; + + if ((styleData == NULL) || (ctxt == NULL) || (URI == NULL) || + (ctxt->ret == -1)) { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "xsltInitCtxtExt: NULL param or error\n"); +#endif + return; + } + module = styleData->extModule; + if ((module == NULL) || (module->initFunc == NULL)) { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "xsltInitCtxtExt: no module or no initFunc\n"); +#endif + return; + } + + ctxtData = (xsltExtDataPtr) xmlHashLookup(ctxt->ctxt->extInfos, URI); + if (ctxtData != NULL) { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "xsltInitCtxtExt: already initialized\n"); +#endif + return; + } + + extData = module->initFunc(ctxt->ctxt, URI); + if (extData == NULL) { +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "xsltInitCtxtExt: no extData\n"); +#endif + } + ctxtData = xsltNewExtData(module, extData); + if (ctxtData == NULL) { + ctxt->ret = -1; + return; + } + + if (ctxt->ctxt->extInfos == NULL) + ctxt->ctxt->extInfos = xmlHashCreate(10); + if (ctxt->ctxt->extInfos == NULL) { + ctxt->ret = -1; + return; + } + + if (xmlHashAddEntry(ctxt->ctxt->extInfos, URI, ctxtData) < 0) { + xsltGenericError(xsltGenericErrorContext, + "Failed to register module data: %s\n", URI); + if (module->shutdownFunc) + module->shutdownFunc(ctxt->ctxt, URI, extData); + xsltFreeExtData(ctxtData); + ctxt->ret = -1; + return; + } +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, "Registered module %s\n", + URI); +#endif + ctxt->ret++; +} + +/** + * xsltInitCtxtExts: + * @ctxt: an XSLT transformation context + * + * Initialize the set of modules with registered stylesheet data + * + * Returns the number of modules initialized or -1 in case of error + */ +int +xsltInitCtxtExts(xsltTransformContextPtr ctxt) +{ + xsltStylesheetPtr style; + xsltInitExtCtxt ctx; + + if (ctxt == NULL) + return (-1); + + style = ctxt->style; + if (style == NULL) + return (-1); + + ctx.ctxt = ctxt; + ctx.ret = 0; + + while (style != NULL) { + if (style->extInfos != NULL) { + xmlHashScan(style->extInfos, + (xmlHashScanner) xsltInitCtxtExt, &ctx); + if (ctx.ret == -1) + return (-1); + } + style = xsltNextImport(style); + } +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, "Registered %d modules\n", + ctx.ret); +#endif + return (ctx.ret); +} + +/** + * xsltShutdownCtxtExt: + * @data: the registered data for the module + * @ctxt: the XSLT transformation context + * @URI: the extension URI + * + * Shutdown an extension module loaded + */ +static void +xsltShutdownCtxtExt(xsltExtDataPtr data, xsltTransformContextPtr ctxt, + const xmlChar * URI) +{ + xsltExtModulePtr module; + + if ((data == NULL) || (ctxt == NULL) || (URI == NULL)) + return; + module = data->extModule; + if ((module == NULL) || (module->shutdownFunc == NULL)) + return; + +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Shutting down module : %s\n", URI); +#endif + module->shutdownFunc(ctxt, URI, data->extData); +} + +/** + * xsltShutdownCtxtExts: + * @ctxt: an XSLT transformation context + * + * Shutdown the set of modules loaded + */ +void +xsltShutdownCtxtExts(xsltTransformContextPtr ctxt) +{ + if (ctxt == NULL) + return; + if (ctxt->extInfos == NULL) + return; + xmlHashScan(ctxt->extInfos, (xmlHashScanner) xsltShutdownCtxtExt, + ctxt); + xmlHashFree(ctxt->extInfos, (xmlHashDeallocator) xsltFreeExtData); + ctxt->extInfos = NULL; +} + +/** + * xsltShutdownExt: + * @data: the registered data for the module + * @ctxt: the XSLT stylesheet + * @URI: the extension URI + * + * Shutdown an extension module loaded + */ +static void +xsltShutdownExt(xsltExtDataPtr data, xsltStylesheetPtr style, + const xmlChar * URI) +{ + xsltExtModulePtr module; + + if ((data == NULL) || (style == NULL) || (URI == NULL)) + return; + module = data->extModule; + if ((module == NULL) || (module->styleShutdownFunc == NULL)) + return; + +#ifdef WITH_XSLT_DEBUG_EXTENSIONS + xsltGenericDebug(xsltGenericDebugContext, + "Shutting down module : %s\n", URI); +#endif + module->styleShutdownFunc(style, URI, data->extData); + /* + * Don't remove the entry from the hash table here, since + * this will produce segfaults - this fixes bug #340624. + * + * xmlHashRemoveEntry(style->extInfos, URI, + * (xmlHashDeallocator) xsltFreeExtData); + */ +} + +/** + * xsltShutdownExts: + * @style: an XSLT stylesheet + * + * Shutdown the set of modules loaded + */ +void +xsltShutdownExts(xsltStylesheetPtr style) +{ + if (style == NULL) + return; + if (style->extInfos == NULL) + return; + xmlHashScan(style->extInfos, (xmlHashScanner) xsltShutdownExt, style); + xmlHashFree(style->extInfos, (xmlHashDeallocator) xsltFreeExtData); + style->extInfos = NULL; +} + +/** + * xsltCheckExtPrefix: + * @style: the stylesheet + * @URI: the namespace prefix (possibly NULL) + * + * Check if the given prefix is one of the declared extensions. + * This is intended to be called only at compile-time. + * Called by: + * xsltGetInheritedNsList() (xslt.c) + * xsltParseTemplateContent (xslt.c) + * + * Returns 1 if this is an extension, 0 otherwise + */ +int +xsltCheckExtPrefix(xsltStylesheetPtr style, const xmlChar * URI) +{ +#ifdef XSLT_REFACTORED + if ((style == NULL) || (style->compCtxt == NULL) || + (XSLT_CCTXT(style)->inode == NULL) || + (XSLT_CCTXT(style)->inode->extElemNs == NULL)) + return (0); + /* + * Lookup the extension namespaces registered + * at the current node in the stylesheet's tree. + */ + if (XSLT_CCTXT(style)->inode->extElemNs != NULL) { + int i; + xsltPointerListPtr list = XSLT_CCTXT(style)->inode->extElemNs; + + for (i = 0; i < list->number; i++) { + if (xmlStrEqual((const xmlChar *) list->items[i], + URI)) + { + return(1); + } + } + } +#else + xsltExtDefPtr cur; + + if ((style == NULL) || (style->nsDefs == NULL)) + return (0); + if (URI == NULL) + URI = BAD_CAST "#default"; + cur = (xsltExtDefPtr) style->nsDefs; + while (cur != NULL) { + /* + * NOTE: This was change to work on namespace names rather + * than namespace prefixes. This fixes bug #339583. + * TODO: Consider renaming the field "prefix" of xsltExtDef + * to "href". + */ + if (xmlStrEqual(URI, cur->prefix)) + return (1); + cur = cur->next; + } +#endif + return (0); +} + +/** + * xsltCheckExtURI: + * @style: the stylesheet + * @URI: the namespace URI (possibly NULL) + * + * Check if the given prefix is one of the declared extensions. + * This is intended to be called only at compile-time. + * Called by: + * xsltPrecomputeStylesheet() (xslt.c) + * xsltParseTemplateContent (xslt.c) + * + * Returns 1 if this is an extension, 0 otherwise + */ +int +xsltCheckExtURI(xsltStylesheetPtr style, const xmlChar * URI) +{ + xsltExtDefPtr cur; + + if ((style == NULL) || (style->nsDefs == NULL)) + return (0); + if (URI == NULL) + return (0); + cur = (xsltExtDefPtr) style->nsDefs; + while (cur != NULL) { + if (xmlStrEqual(URI, cur->URI)) + return (1); + cur = cur->next; + } + return (0); +} + +/** + * xsltRegisterExtModuleFull: + * @URI: URI associated to this module + * @initFunc: the module initialization function + * @shutdownFunc: the module shutdown function + * @styleInitFunc: the module initialization function + * @styleShutdownFunc: the module shutdown function + * + * Register an XSLT extension module to the library. + * + * Returns 0 if sucessful, -1 in case of error + */ +int +xsltRegisterExtModuleFull(const xmlChar * URI, + xsltExtInitFunction initFunc, + xsltExtShutdownFunction shutdownFunc, + xsltStyleExtInitFunction styleInitFunc, + xsltStyleExtShutdownFunction styleShutdownFunc) +{ + int ret; + xsltExtModulePtr module; + + if ((URI == NULL) || (initFunc == NULL)) + return (-1); + if (xsltExtensionsHash == NULL) + xsltExtensionsHash = xmlHashCreate(10); + + if (xsltExtensionsHash == NULL) + return (-1); + + xmlMutexLock(xsltExtMutex); + + module = xmlHashLookup(xsltExtensionsHash, URI); + if (module != NULL) { + if ((module->initFunc == initFunc) && + (module->shutdownFunc == shutdownFunc)) + ret = 0; + else + ret = -1; + goto done; + } + module = xsltNewExtModule(initFunc, shutdownFunc, + styleInitFunc, styleShutdownFunc); + if (module == NULL) { + ret = -1; + goto done; + } + ret = xmlHashAddEntry(xsltExtensionsHash, URI, (void *) module); + +done: + xmlMutexUnlock(xsltExtMutex); + return (ret); +} + +/** + * xsltRegisterExtModule: + * @URI: URI associated to this module + * @initFunc: the module initialization function + * @shutdownFunc: the module shutdown function + * + * Register an XSLT extension module to the library. + * + * Returns 0 if sucessful, -1 in case of error + */ +int +xsltRegisterExtModule(const xmlChar * URI, + xsltExtInitFunction initFunc, + xsltExtShutdownFunction shutdownFunc) +{ + return xsltRegisterExtModuleFull(URI, initFunc, shutdownFunc, + NULL, NULL); +} + +/** + * xsltUnregisterExtModule: + * @URI: URI associated to this module + * + * Unregister an XSLT extension module from the library. + * + * Returns 0 if sucessful, -1 in case of error + */ +int +xsltUnregisterExtModule(const xmlChar * URI) +{ + int ret; + + if (URI == NULL) + return (-1); + if (xsltExtensionsHash == NULL) + return (-1); + + xmlMutexLock(xsltExtMutex); + + ret = xmlHashRemoveEntry(xsltExtensionsHash, URI, + (xmlHashDeallocator) xsltFreeExtModule); + + xmlMutexUnlock(xsltExtMutex); + + return (ret); +} + +/** + * xsltUnregisterAllExtModules: + * + * Unregister all the XSLT extension module from the library. + */ +static void +xsltUnregisterAllExtModules(void) +{ + if (xsltExtensionsHash == NULL) + return; + + xmlMutexLock(xsltExtMutex); + + xmlHashFree(xsltExtensionsHash, + (xmlHashDeallocator) xsltFreeExtModule); + xsltExtensionsHash = NULL; + + xmlMutexUnlock(xsltExtMutex); +} + +/** + * xsltXPathGetTransformContext: + * @ctxt: an XPath transformation context + * + * Provides the XSLT transformation context from the XPath transformation + * context. This is useful when an XPath function in the extension module + * is called by the XPath interpreter and that the XSLT context is needed + * for example to retrieve the associated data pertaining to this XSLT + * transformation. + * + * Returns the XSLT transformation context or NULL in case of error. + */ +xsltTransformContextPtr +xsltXPathGetTransformContext(xmlXPathParserContextPtr ctxt) +{ + if ((ctxt == NULL) || (ctxt->context == NULL)) + return (NULL); + return (ctxt->context->extra); +} + +/** + * xsltRegisterExtModuleFunction: + * @name: the function name + * @URI: the function namespace URI + * @function: the function callback + * + * Registers an extension module function. + * + * Returns 0 if successful, -1 in case of error. + */ +int +xsltRegisterExtModuleFunction(const xmlChar * name, const xmlChar * URI, + xmlXPathFunction function) +{ + if ((name == NULL) || (URI == NULL) || (function == NULL)) + return (-1); + + if (xsltFunctionsHash == NULL) + xsltFunctionsHash = xmlHashCreate(10); + if (xsltFunctionsHash == NULL) + return (-1); + + xmlMutexLock(xsltExtMutex); + + xmlHashUpdateEntry2(xsltFunctionsHash, name, URI, + XML_CAST_FPTR(function), NULL); + + xmlMutexUnlock(xsltExtMutex); + + return (0); +} + +/** + * xsltExtModuleFunctionLookup: + * @name: the function name + * @URI: the function namespace URI + * + * Looks up an extension module function + * + * Returns the function if found, NULL otherwise. + */ +xmlXPathFunction +xsltExtModuleFunctionLookup(const xmlChar * name, const xmlChar * URI) +{ + xmlXPathFunction ret; + + if ((xsltFunctionsHash == NULL) || (name == NULL) || (URI == NULL)) + return (NULL); + + xmlMutexLock(xsltExtMutex); + + XML_CAST_FPTR(ret) = xmlHashLookup2(xsltFunctionsHash, name, URI); + + xmlMutexUnlock(xsltExtMutex); + + /* if lookup fails, attempt a dynamic load on supported platforms */ + if (NULL == ret) { + if (!xsltExtModuleRegisterDynamic(URI)) { + xmlMutexLock(xsltExtMutex); + + XML_CAST_FPTR(ret) = + xmlHashLookup2(xsltFunctionsHash, name, URI); + + xmlMutexUnlock(xsltExtMutex); + } + } + + return ret; +} + +/** + * xsltUnregisterExtModuleFunction: + * @name: the function name + * @URI: the function namespace URI + * + * Unregisters an extension module function + * + * Returns 0 if successful, -1 in case of error. + */ +int +xsltUnregisterExtModuleFunction(const xmlChar * name, const xmlChar * URI) +{ + int ret; + + if ((xsltFunctionsHash == NULL) || (name == NULL) || (URI == NULL)) + return (-1); + + xmlMutexLock(xsltExtMutex); + + ret = xmlHashRemoveEntry2(xsltFunctionsHash, name, URI, NULL); + + xmlMutexUnlock(xsltExtMutex); + + return(ret); +} + +/** + * xsltUnregisterAllExtModuleFunction: + * + * Unregisters all extension module function + */ +static void +xsltUnregisterAllExtModuleFunction(void) +{ + xmlMutexLock(xsltExtMutex); + + xmlHashFree(xsltFunctionsHash, NULL); + xsltFunctionsHash = NULL; + + xmlMutexUnlock(xsltExtMutex); +} + + +/** + * xsltNewElemPreComp: + * @style: the XSLT stylesheet + * @inst: the element node + * @function: the transform function + * + * Creates and initializes an #xsltElemPreComp + * + * Returns the new and initialized #xsltElemPreComp + */ +xsltElemPreCompPtr +xsltNewElemPreComp(xsltStylesheetPtr style, xmlNodePtr inst, + xsltTransformFunction function) +{ + xsltElemPreCompPtr cur; + + cur = (xsltElemPreCompPtr) xmlMalloc(sizeof(xsltElemPreComp)); + if (cur == NULL) { + xsltTransformError(NULL, style, NULL, + "xsltNewExtElement : malloc failed\n"); + return (NULL); + } + memset(cur, 0, sizeof(xsltElemPreComp)); + + xsltInitElemPreComp(cur, style, inst, function, + (xsltElemPreCompDeallocator) xmlFree); + + return (cur); +} + +/** + * xsltInitElemPreComp: + * @comp: an #xsltElemPreComp (or generally a derived structure) + * @style: the XSLT stylesheet + * @inst: the element node + * @function: the transform function + * @freeFunc: the @comp deallocator + * + * Initializes an existing #xsltElemPreComp structure. This is usefull + * when extending an #xsltElemPreComp to store precomputed data. + * This function MUST be called on any extension element precomputed + * data struct. + */ +void +xsltInitElemPreComp(xsltElemPreCompPtr comp, xsltStylesheetPtr style, + xmlNodePtr inst, xsltTransformFunction function, + xsltElemPreCompDeallocator freeFunc) +{ + comp->type = XSLT_FUNC_EXTENSION; + comp->func = function; + comp->inst = inst; + comp->free = freeFunc; + + comp->next = style->preComps; + style->preComps = comp; +} + +/** + * xsltPreComputeExtModuleElement: + * @style: the stylesheet + * @inst: the element node + * + * Precomputes an extension module element + * + * Returns the precomputed data + */ +xsltElemPreCompPtr +xsltPreComputeExtModuleElement(xsltStylesheetPtr style, xmlNodePtr inst) +{ + xsltExtElementPtr ext; + xsltElemPreCompPtr comp = NULL; + + if ((style == NULL) || (inst == NULL) || + (inst->type != XML_ELEMENT_NODE) || (inst->ns == NULL)) + return (NULL); + + xmlMutexLock(xsltExtMutex); + + ext = (xsltExtElementPtr) + xmlHashLookup2(xsltElementsHash, inst->name, inst->ns->href); + + xmlMutexUnlock(xsltExtMutex); + + /* + * EXT TODO: Now what? + */ + if (ext == NULL) + return (NULL); + + if (ext->precomp != NULL) { + /* + * REVISIT TODO: Check if the text below is correct. + * This will return a xsltElemPreComp structure or NULL. + * 1) If the the author of the extension needs a + * custom structure to hold the specific values of + * this extension, he will derive a structure based on + * xsltElemPreComp; thus we obviously *cannot* refactor + * the xsltElemPreComp structure, since all already derived + * user-defined strucures will break. + * Example: For the extension xsl:document, + * in xsltDocumentComp() (preproc.c), the structure + * xsltStyleItemDocument is allocated, filled with + * specific values and returned. + * 2) If the author needs no values to be stored in + * this structure, then he'll return NULL; + */ + comp = ext->precomp(style, inst, ext->transform); + } + if (comp == NULL) { + /* + * Default creation of a xsltElemPreComp structure, if + * the author of this extension did not create a custom + * structure. + */ + comp = xsltNewElemPreComp(style, inst, ext->transform); + } + + return (comp); +} + +/** + * xsltRegisterExtModuleElement: + * @name: the element name + * @URI: the element namespace URI + * @precomp: the pre-computation callback + * @transform: the transformation callback + * + * Registers an extension module element. + * + * Returns 0 if successful, -1 in case of error. + */ +int +xsltRegisterExtModuleElement(const xmlChar * name, const xmlChar * URI, + xsltPreComputeFunction precomp, + xsltTransformFunction transform) +{ + int ret = 0; + + xsltExtElementPtr ext; + + if ((name == NULL) || (URI == NULL) || (transform == NULL)) + return (-1); + + if (xsltElementsHash == NULL) + xsltElementsHash = xmlHashCreate(10); + if (xsltElementsHash == NULL) + return (-1); + + xmlMutexLock(xsltExtMutex); + + ext = xsltNewExtElement(precomp, transform); + if (ext == NULL) { + ret = -1; + goto done; + } + + xmlHashUpdateEntry2(xsltElementsHash, name, URI, (void *) ext, + (xmlHashDeallocator) xsltFreeExtElement); + +done: + xmlMutexUnlock(xsltExtMutex); + + return (ret); +} + +/** + * xsltExtElementLookup: + * @ctxt: an XSLT process context + * @name: the element name + * @URI: the element namespace URI + * + * Looks up an extension element. @ctxt can be NULL to search only in + * module elements. + * + * Returns the element callback or NULL if not found + */ +xsltTransformFunction +xsltExtElementLookup(xsltTransformContextPtr ctxt, + const xmlChar * name, const xmlChar * URI) +{ + xsltTransformFunction ret; + + if ((name == NULL) || (URI == NULL)) + return (NULL); + + if ((ctxt != NULL) && (ctxt->extElements != NULL)) { + XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->extElements, name, URI); + if (ret != NULL) { + return(ret); + } + } + + ret = xsltExtModuleElementLookup(name, URI); + + return (ret); +} + +/** + * xsltExtModuleElementLookup: + * @name: the element name + * @URI: the element namespace URI + * + * Looks up an extension module element + * + * Returns the callback function if found, NULL otherwise. + */ +xsltTransformFunction +xsltExtModuleElementLookup(const xmlChar * name, const xmlChar * URI) +{ + xsltExtElementPtr ext; + + if ((xsltElementsHash == NULL) || (name == NULL) || (URI == NULL)) + return (NULL); + + xmlMutexLock(xsltExtMutex); + + ext = (xsltExtElementPtr) xmlHashLookup2(xsltElementsHash, name, URI); + + xmlMutexUnlock(xsltExtMutex); + + /* + * if function lookup fails, attempt a dynamic load on + * supported platforms + */ + if (NULL == ext) { + if (!xsltExtModuleRegisterDynamic(URI)) { + xmlMutexLock(xsltExtMutex); + + ext = (xsltExtElementPtr) + xmlHashLookup2(xsltElementsHash, name, URI); + + xmlMutexUnlock(xsltExtMutex); + } + } + + if (ext == NULL) + return (NULL); + return (ext->transform); +} + +/** + * xsltExtModuleElementPreComputeLookup: + * @name: the element name + * @URI: the element namespace URI + * + * Looks up an extension module element pre-computation function + * + * Returns the callback function if found, NULL otherwise. + */ +xsltPreComputeFunction +xsltExtModuleElementPreComputeLookup(const xmlChar * name, + const xmlChar * URI) +{ + xsltExtElementPtr ext; + + if ((xsltElementsHash == NULL) || (name == NULL) || (URI == NULL)) + return (NULL); + + xmlMutexLock(xsltExtMutex); + + ext = (xsltExtElementPtr) xmlHashLookup2(xsltElementsHash, name, URI); + + xmlMutexUnlock(xsltExtMutex); + + if (ext == NULL) { + if (!xsltExtModuleRegisterDynamic(URI)) { + xmlMutexLock(xsltExtMutex); + + ext = (xsltExtElementPtr) + xmlHashLookup2(xsltElementsHash, name, URI); + + xmlMutexUnlock(xsltExtMutex); + } + } + + if (ext == NULL) + return (NULL); + return (ext->precomp); +} + +/** + * xsltUnregisterExtModuleElement: + * @name: the element name + * @URI: the element namespace URI + * + * Unregisters an extension module element + * + * Returns 0 if successful, -1 in case of error. + */ +int +xsltUnregisterExtModuleElement(const xmlChar * name, const xmlChar * URI) +{ + int ret; + + if ((xsltElementsHash == NULL) || (name == NULL) || (URI == NULL)) + return (-1); + + xmlMutexLock(xsltExtMutex); + + ret = xmlHashRemoveEntry2(xsltElementsHash, name, URI, + (xmlHashDeallocator) xsltFreeExtElement); + + xmlMutexUnlock(xsltExtMutex); + + return(ret); +} + +/** + * xsltUnregisterAllExtModuleElement: + * + * Unregisters all extension module element + */ +static void +xsltUnregisterAllExtModuleElement(void) +{ + xmlMutexLock(xsltExtMutex); + + xmlHashFree(xsltElementsHash, (xmlHashDeallocator) xsltFreeExtElement); + xsltElementsHash = NULL; + + xmlMutexUnlock(xsltExtMutex); +} + +/** + * xsltRegisterExtModuleTopLevel: + * @name: the top-level element name + * @URI: the top-level element namespace URI + * @function: the top-level element callback + * + * Registers an extension module top-level element. + * + * Returns 0 if successful, -1 in case of error. + */ +int +xsltRegisterExtModuleTopLevel(const xmlChar * name, const xmlChar * URI, + xsltTopLevelFunction function) +{ + if ((name == NULL) || (URI == NULL) || (function == NULL)) + return (-1); + + if (xsltTopLevelsHash == NULL) + xsltTopLevelsHash = xmlHashCreate(10); + if (xsltTopLevelsHash == NULL) + return (-1); + + xmlMutexLock(xsltExtMutex); + + xmlHashUpdateEntry2(xsltTopLevelsHash, name, URI, + XML_CAST_FPTR(function), NULL); + + xmlMutexUnlock(xsltExtMutex); + + return (0); +} + +/** + * xsltExtModuleTopLevelLookup: + * @name: the top-level element name + * @URI: the top-level element namespace URI + * + * Looks up an extension module top-level element + * + * Returns the callback function if found, NULL otherwise. + */ +xsltTopLevelFunction +xsltExtModuleTopLevelLookup(const xmlChar * name, const xmlChar * URI) +{ + xsltTopLevelFunction ret; + + if ((xsltTopLevelsHash == NULL) || (name == NULL) || (URI == NULL)) + return (NULL); + + xmlMutexLock(xsltExtMutex); + + XML_CAST_FPTR(ret) = xmlHashLookup2(xsltTopLevelsHash, name, URI); + + xmlMutexUnlock(xsltExtMutex); + + /* if lookup fails, attempt a dynamic load on supported platforms */ + if (NULL == ret) { + if (!xsltExtModuleRegisterDynamic(URI)) { + xmlMutexLock(xsltExtMutex); + + XML_CAST_FPTR(ret) = xmlHashLookup2(xsltTopLevelsHash, name, URI); + + xmlMutexUnlock(xsltExtMutex); + } + } + + return (ret); +} + +/** + * xsltUnregisterExtModuleTopLevel: + * @name: the top-level element name + * @URI: the top-level element namespace URI + * + * Unregisters an extension module top-level element + * + * Returns 0 if successful, -1 in case of error. + */ +int +xsltUnregisterExtModuleTopLevel(const xmlChar * name, const xmlChar * URI) +{ + int ret; + + if ((xsltTopLevelsHash == NULL) || (name == NULL) || (URI == NULL)) + return (-1); + + xmlMutexLock(xsltExtMutex); + + ret = xmlHashRemoveEntry2(xsltTopLevelsHash, name, URI, NULL); + + xmlMutexUnlock(xsltExtMutex); + + return(ret); +} + +/** + * xsltUnregisterAllExtModuleTopLevel: + * + * Unregisters all extension module function + */ +static void +xsltUnregisterAllExtModuleTopLevel(void) +{ + xmlMutexLock(xsltExtMutex); + + xmlHashFree(xsltTopLevelsHash, NULL); + xsltTopLevelsHash = NULL; + + xmlMutexUnlock(xsltExtMutex); +} + +/** + * xsltGetExtInfo: + * @style: pointer to a stylesheet + * @URI: the namespace URI desired + * + * looks up URI in extInfos of the stylesheet + * + * returns a pointer to the hash table if found, else NULL + */ +xmlHashTablePtr +xsltGetExtInfo(xsltStylesheetPtr style, const xmlChar * URI) +{ + xsltExtDataPtr data; + + /* + * TODO: Why do we have a return type of xmlHashTablePtr? + * Is the user-allocated data for extension modules expected + * to be a xmlHashTablePtr only? Or is this intended for + * the EXSLT module only? + */ + + if (style != NULL && style->extInfos != NULL) { + data = xmlHashLookup(style->extInfos, URI); + if (data != NULL && data->extData != NULL) + return data->extData; + } + return NULL; +} + +/************************************************************************ + * * + * Test module http://xmlsoft.org/XSLT/ * + * * + ************************************************************************/ + +/************************************************************************ + * * + * Test of the extension module API * + * * + ************************************************************************/ + +static xmlChar *testData = NULL; +static xmlChar *testStyleData = NULL; + +/** + * xsltExtFunctionTest: + * @ctxt: the XPath Parser context + * @nargs: the number of arguments + * + * function libxslt:test() for testing the extensions support. + */ +static void +xsltExtFunctionTest(xmlXPathParserContextPtr ctxt, + int nargs ATTRIBUTE_UNUSED) +{ + xsltTransformContextPtr tctxt; + void *data = NULL; + + tctxt = xsltXPathGetTransformContext(ctxt); + + if (testData == NULL) { + xsltGenericDebug(xsltGenericDebugContext, + "xsltExtFunctionTest: not initialized," + " calling xsltGetExtData\n"); + data = xsltGetExtData(tctxt, (const xmlChar *) XSLT_DEFAULT_URL); + if (data == NULL) { + xsltTransformError(tctxt, NULL, NULL, + "xsltExtElementTest: not initialized\n"); + return; + } + } + if (tctxt == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "xsltExtFunctionTest: failed to get the transformation context\n"); + return; + } + if (data == NULL) + data = xsltGetExtData(tctxt, (const xmlChar *) XSLT_DEFAULT_URL); + if (data == NULL) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "xsltExtFunctionTest: failed to get module data\n"); + return; + } + if (data != testData) { + xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL, + "xsltExtFunctionTest: got wrong module data\n"); + return; + } +#ifdef WITH_XSLT_DEBUG_FUNCTION + xsltGenericDebug(xsltGenericDebugContext, + "libxslt:test() called with %d args\n", nargs); +#endif +} + +/** + * xsltExtElementPreCompTest: + * @style: the stylesheet + * @inst: the instruction in the stylesheet + * + * Process a libxslt:test node + */ +static xsltElemPreCompPtr +xsltExtElementPreCompTest(xsltStylesheetPtr style, xmlNodePtr inst, + xsltTransformFunction function) +{ + xsltElemPreCompPtr ret; + + if (style == NULL) { + xsltTransformError(NULL, NULL, inst, + "xsltExtElementTest: no transformation context\n"); + return (NULL); + } + if (testStyleData == NULL) { + xsltGenericDebug(xsltGenericDebugContext, + "xsltExtElementPreCompTest: not initialized," + " calling xsltStyleGetExtData\n"); + xsltStyleGetExtData(style, (const xmlChar *) XSLT_DEFAULT_URL); + if (testStyleData == NULL) { + xsltTransformError(NULL, style, inst, + "xsltExtElementPreCompTest: not initialized\n"); + if (style != NULL) + style->errors++; + return (NULL); + } + } + if (inst == NULL) { + xsltTransformError(NULL, style, inst, + "xsltExtElementPreCompTest: no instruction\n"); + if (style != NULL) + style->errors++; + return (NULL); + } + ret = xsltNewElemPreComp(style, inst, function); + return (ret); +} + +/** + * xsltExtElementTest: + * @ctxt: an XSLT processing context + * @node: The current node + * @inst: the instruction in the stylesheet + * @comp: precomputed informations + * + * Process a libxslt:test node + */ +static void +xsltExtElementTest(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst, + xsltElemPreCompPtr comp ATTRIBUTE_UNUSED) +{ + xmlNodePtr commentNode; + + if (testData == NULL) { + xsltGenericDebug(xsltGenericDebugContext, + "xsltExtElementTest: not initialized," + " calling xsltGetExtData\n"); + xsltGetExtData(ctxt, (const xmlChar *) XSLT_DEFAULT_URL); + if (testData == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsltExtElementTest: not initialized\n"); + return; + } + } + if (ctxt == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsltExtElementTest: no transformation context\n"); + return; + } + if (node == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsltExtElementTest: no current node\n"); + return; + } + if (inst == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsltExtElementTest: no instruction\n"); + return; + } + if (ctxt->insert == NULL) { + xsltTransformError(ctxt, NULL, inst, + "xsltExtElementTest: no insertion point\n"); + return; + } + commentNode = xmlNewComment((const xmlChar *) + "libxslt:test element test worked"); + xmlAddChild(ctxt->insert, commentNode); +} + +/** + * xsltExtInitTest: + * @ctxt: an XSLT transformation context + * @URI: the namespace URI for the extension + * + * A function called at initialization time of an XSLT extension module + * + * Returns a pointer to the module specific data for this transformation + */ +static void * +xsltExtInitTest(xsltTransformContextPtr ctxt, const xmlChar * URI) +{ + if (testStyleData == NULL) { + xsltGenericDebug(xsltGenericErrorContext, + "xsltExtInitTest: not initialized," + " calling xsltStyleGetExtData\n"); + testStyleData = xsltStyleGetExtData(ctxt->style, URI); + if (testStyleData == NULL) { + xsltTransformError(ctxt, NULL, NULL, + "xsltExtInitTest: not initialized\n"); + return (NULL); + } + } + if (testData != NULL) { + xsltTransformError(ctxt, NULL, NULL, + "xsltExtInitTest: already initialized\n"); + return (NULL); + } + testData = (void *) "test data"; + xsltGenericDebug(xsltGenericDebugContext, + "Registered test module : %s\n", URI); + return (testData); +} + + +/** + * xsltExtShutdownTest: + * @ctxt: an XSLT transformation context + * @URI: the namespace URI for the extension + * @data: the data associated to this module + * + * A function called at shutdown time of an XSLT extension module + */ +static void +xsltExtShutdownTest(xsltTransformContextPtr ctxt, + const xmlChar * URI, void *data) +{ + if (testData == NULL) { + xsltTransformError(ctxt, NULL, NULL, + "xsltExtShutdownTest: not initialized\n"); + return; + } + if (data != testData) { + xsltTransformError(ctxt, NULL, NULL, + "xsltExtShutdownTest: wrong data\n"); + } + testData = NULL; + xsltGenericDebug(xsltGenericDebugContext, + "Unregistered test module : %s\n", URI); +} + +/** + * xsltExtStyleInitTest: + * @style: an XSLT stylesheet + * @URI: the namespace URI for the extension + * + * A function called at initialization time of an XSLT extension module + * + * Returns a pointer to the module specific data for this transformation + */ +static void * +xsltExtStyleInitTest(xsltStylesheetPtr style ATTRIBUTE_UNUSED, + const xmlChar * URI) +{ + if (testStyleData != NULL) { + xsltTransformError(NULL, NULL, NULL, + "xsltExtInitTest: already initialized\n"); + return (NULL); + } + testStyleData = (void *) "test data"; + xsltGenericDebug(xsltGenericDebugContext, + "Registered test module : %s\n", URI); + return (testStyleData); +} + + +/** + * xsltExtStyleShutdownTest: + * @style: an XSLT stylesheet + * @URI: the namespace URI for the extension + * @data: the data associated to this module + * + * A function called at shutdown time of an XSLT extension module + */ +static void +xsltExtStyleShutdownTest(xsltStylesheetPtr style ATTRIBUTE_UNUSED, + const xmlChar * URI, void *data) +{ + if (testStyleData == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltExtShutdownTest: not initialized\n"); + return; + } + if (data != testStyleData) { + xsltTransformError(NULL, NULL, NULL, + "xsltExtShutdownTest: wrong data\n"); + } + testStyleData = NULL; + xsltGenericDebug(xsltGenericDebugContext, + "Unregistered test module : %s\n", URI); +} + +/** + * xsltRegisterTestModule: + * + * Registers the test module + */ +void +xsltRegisterTestModule(void) +{ + xsltInitGlobals(); + xsltRegisterExtModuleFull((const xmlChar *) XSLT_DEFAULT_URL, + xsltExtInitTest, xsltExtShutdownTest, + xsltExtStyleInitTest, + xsltExtStyleShutdownTest); + xsltRegisterExtModuleFunction((const xmlChar *) "test", + (const xmlChar *) XSLT_DEFAULT_URL, + xsltExtFunctionTest); + xsltRegisterExtModuleElement((const xmlChar *) "test", + (const xmlChar *) XSLT_DEFAULT_URL, + xsltExtElementPreCompTest, + xsltExtElementTest); +} + +static void +xsltHashScannerModuleFree(void *payload ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED, + xmlChar * name ATTRIBUTE_UNUSED) +{ +#ifdef WITH_MODULES + xmlModuleClose(payload); +#endif +} + +/** + * xsltInitGlobals: + * + * Initialize the global variables for extensions + */ +void +xsltInitGlobals(void) +{ + if (xsltExtMutex == NULL) { + xsltExtMutex = xmlNewMutex(); + } +} + +/** + * xsltCleanupGlobals: + * + * Unregister all global variables set up by the XSLT library + */ +void +xsltCleanupGlobals(void) +{ + xsltUnregisterAllExtModules(); + xsltUnregisterAllExtModuleFunction(); + xsltUnregisterAllExtModuleElement(); + xsltUnregisterAllExtModuleTopLevel(); + + xmlMutexLock(xsltExtMutex); + /* cleanup dynamic module hash */ + if (NULL != xsltModuleHash) { + xmlHashScan(xsltModuleHash, xsltHashScannerModuleFree, 0); + xmlHashFree(xsltModuleHash, NULL); + xsltModuleHash = NULL; + } + xmlMutexUnlock(xsltExtMutex); + + xmlFreeMutex(xsltExtMutex); + xsltExtMutex = NULL; + xsltFreeLocales(); + xsltUninit(); +} + +static void +xsltDebugDumpExtensionsCallback(void *function ATTRIBUTE_UNUSED, + FILE * output, const xmlChar * name, + const xmlChar * URI, + const xmlChar * not_used ATTRIBUTE_UNUSED) +{ + if (!name || !URI) + return; + fprintf(output, "{%s}%s\n", URI, name); +} + +static void +xsltDebugDumpExtModulesCallback(void *function ATTRIBUTE_UNUSED, + FILE * output, const xmlChar * URI, + const xmlChar * not_used ATTRIBUTE_UNUSED, + const xmlChar * not_used2 ATTRIBUTE_UNUSED) +{ + if (!URI) + return; + fprintf(output, "%s\n", URI); +} + +/** + * xsltDebugDumpExtensions: + * @output: the FILE * for the output, if NULL stdout is used + * + * Dumps a list of the registered XSLT extension functions and elements + */ +void +xsltDebugDumpExtensions(FILE * output) +{ + if (output == NULL) + output = stdout; + fprintf(output, + "Registered XSLT Extensions\n--------------------------\n"); + if (!xsltFunctionsHash) + fprintf(output, "No registered extension functions\n"); + else { + fprintf(output, "Registered Extension Functions:\n"); + xmlMutexLock(xsltExtMutex); + xmlHashScanFull(xsltFunctionsHash, + (xmlHashScannerFull) + xsltDebugDumpExtensionsCallback, output); + xmlMutexUnlock(xsltExtMutex); + } + if (!xsltElementsHash) + fprintf(output, "\nNo registered extension elements\n"); + else { + fprintf(output, "\nRegistered Extension Elements:\n"); + xmlMutexLock(xsltExtMutex); + xmlHashScanFull(xsltElementsHash, + (xmlHashScannerFull) + xsltDebugDumpExtensionsCallback, output); + xmlMutexUnlock(xsltExtMutex); + } + if (!xsltExtensionsHash) + fprintf(output, "\nNo registered extension modules\n"); + else { + fprintf(output, "\nRegistered Extension Modules:\n"); + xmlMutexLock(xsltExtMutex); + xmlHashScanFull(xsltExtensionsHash, + (xmlHashScannerFull) + xsltDebugDumpExtModulesCallback, output); + xmlMutexUnlock(xsltExtMutex); + } + +} |