root/third_party/libxslt/libxslt/extensions.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. xsltNewExtDef
  2. xsltFreeExtDef
  3. xsltFreeExtDefList
  4. xsltNewExtModule
  5. xsltFreeExtModule
  6. xsltNewExtData
  7. xsltFreeExtData
  8. xsltNewExtElement
  9. xsltFreeExtElement
  10. xsltExtModuleRegisterDynamic
  11. xsltExtModuleRegisterDynamic
  12. xsltFreeExts
  13. xsltRegisterExtPrefix
  14. xsltRegisterExtFunction
  15. xsltRegisterExtElement
  16. xsltFreeCtxtExts
  17. xsltStyleInitializeStylesheetModule
  18. xsltStyleGetExtData
  19. xsltStyleStylesheetLevelGetExtData
  20. xsltGetExtData
  21. xsltInitCtxtExt
  22. xsltInitCtxtExts
  23. xsltShutdownCtxtExt
  24. xsltShutdownCtxtExts
  25. xsltShutdownExt
  26. xsltShutdownExts
  27. xsltCheckExtPrefix
  28. xsltCheckExtURI
  29. xsltRegisterExtModuleFull
  30. xsltRegisterExtModule
  31. xsltUnregisterExtModule
  32. xsltUnregisterAllExtModules
  33. xsltXPathGetTransformContext
  34. xsltRegisterExtModuleFunction
  35. xsltExtModuleFunctionLookup
  36. xsltUnregisterExtModuleFunction
  37. xsltUnregisterAllExtModuleFunction
  38. xsltNewElemPreComp
  39. xsltInitElemPreComp
  40. xsltPreComputeExtModuleElement
  41. xsltRegisterExtModuleElement
  42. xsltExtElementLookup
  43. xsltExtModuleElementLookup
  44. xsltExtModuleElementPreComputeLookup
  45. xsltUnregisterExtModuleElement
  46. xsltUnregisterAllExtModuleElement
  47. xsltRegisterExtModuleTopLevel
  48. xsltExtModuleTopLevelLookup
  49. xsltUnregisterExtModuleTopLevel
  50. xsltUnregisterAllExtModuleTopLevel
  51. xsltGetExtInfo
  52. xsltExtFunctionTest
  53. xsltExtElementPreCompTest
  54. xsltExtElementTest
  55. xsltExtInitTest
  56. xsltExtShutdownTest
  57. xsltExtStyleInitTest
  58. xsltExtStyleShutdownTest
  59. xsltRegisterTestModule
  60. xsltHashScannerModuleFree
  61. xsltInitGlobals
  62. xsltCleanupGlobals
  63. xsltDebugDumpExtensionsCallback
  64. xsltDebugDumpExtModulesCallback
  65. xsltDebugDumpExtensions

/*
 * 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++;
    }

    if (*(i - 1) == '_')
        *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;

    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 (0);
}

/**
 * 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;
    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);
    }

}

/* [<][>][^][v][top][bottom][index][help] */