- libxslt/extension.[ch] libxslt/extra.[ch] libxslt/xsltInternals.h
authorDaniel Veillard <veillard@src.gnome.org>
Wed, 4 Jul 2001 13:22:40 +0000 (13:22 +0000)
committerDaniel Veillard <veillard@src.gnome.org>
Wed, 4 Jul 2001 13:22:40 +0000 (13:22 +0000)
  libxslt/transform.c libxslt/functions.c: Added the extension API
  suggested by Thomas Broyer, this should allow implementation of
  EXSLT for example.
- libxslt/extra.[ch]: added a fake xsltFunctionLocalTime() in Norm's
  CVS extension namespace to avoid complaints, some cleanup
- configure.in tests/Makefile.am tests/extensions/*: added a test
  for new modules testing both elements and functions registration
Daniel

15 files changed:
ChangeLog
configure.in
libxslt/extensions.c
libxslt/extensions.h
libxslt/extra.c
libxslt/extra.h
libxslt/functions.c
libxslt/transform.c
libxslt/xsltInternals.h
libxslt/xsltproc.c
tests/Makefile.am
tests/extensions/Makefile.am [new file with mode: 0644]
tests/extensions/module.out [new file with mode: 0644]
tests/extensions/module.xml [new file with mode: 0644]
tests/extensions/module.xsl [new file with mode: 0644]

index 7847d2a..79324c9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+Wed Jul  4 15:15:50 CEST 2001 Daniel Veillard <Daniel.Veillard@imag.fr>
+
+       * libxslt/extension.[ch] libxslt/extra.[ch] libxslt/xsltInternals.h
+         libxslt/transform.c libxslt/functions.c: Added the extension API
+         suggested by Thomas Broyer, this should allow implementation of
+         EXSLT for example.
+       * libxslt/extra.[ch]: added a fake xsltFunctionLocalTime() in Norm's
+         CVS extension namespace to avoid complaints, some cleanup
+       * configure.in tests/Makefile.am tests/extensions/*: added a test
+         for new modules testing both elements and functions registration
+
 Fri Jun 29 23:32:37 CEST 2001 Daniel Veillard <Daniel.Veillard@imag.fr>
 
        * libxslt/pattern.c libxslt/preproc.c libxslt/transform.c
index 5dfeaef..6250df2 100644 (file)
@@ -185,6 +185,7 @@ tests/REC1/Makefile
 tests/REC2/Makefile
 tests/REC/Makefile
 tests/general/Makefile
+tests/extensions/Makefile
 tests/namespaces/Makefile
 tests/numbers/Makefile
 tests/documents/Makefile
index 92a6c18..8894a18 100644 (file)
 #include <libxml/hash.h>
 #include <libxml/xmlerror.h>
 #include <libxml/parserInternals.h>
+#include <libxml/xpathInternals.h>
 #include "xslt.h"
 #include "xsltInternals.h"
 #include "xsltutils.h"
+#include "imports.h"
 #include "extensions.h"
 
 #ifdef WITH_XSLT_DEBUG
 #define WITH_XSLT_DEBUG_EXTENSIONS
 #endif
 
+/************************************************************************
+ *                                                                     *
+ *                     Private Types and Globals                       *
+ *                                                                     *
+ ************************************************************************/
+
 typedef struct _xsltExtDef xsltExtDef;
 typedef xsltExtDef *xsltExtDefPtr;
 struct _xsltExtDef {
@@ -36,6 +44,22 @@ struct _xsltExtDef {
     void    *data;
 };
 
+typedef struct _xsltExtModule xsltExtModule;
+typedef xsltExtModule *xsltExtModulePtr;
+struct _xsltExtModule {
+    xsltExtInitFunction initFunc;
+    xsltExtShutdownFunction shutdownFunc;
+};
+
+typedef struct _xsltExtData xsltExtData;
+typedef xsltExtData *xsltExtDataPtr;
+struct _xsltExtData {
+    xsltExtModulePtr extModule;
+    void *extData;
+};
+
+static xmlHashTablePtr xsltExtensionsHash = NULL;
+
 /************************************************************************
  *                                                                     *
  *                     Type functions                                  *
@@ -52,21 +76,22 @@ struct _xsltExtDef {
  * Returns the newly allocated xsltExtDefPtr or NULL in case of error
  */
 static xsltExtDefPtr
-xsltNewExtDef(const xmlChar *prefix, const xmlChar *URI) {
+xsltNewExtDef(const xmlChar * prefix, const xmlChar * URI)
+{
     xsltExtDefPtr cur;
 
     cur = (xsltExtDefPtr) xmlMalloc(sizeof(xsltExtDef));
     if (cur == NULL) {
         xsltGenericError(xsltGenericErrorContext,
-               "xsltNewExtDef : malloc failed\n");
-       return(NULL);
+                         "xsltNewExtDef : malloc failed\n");
+        return (NULL);
     }
     memset(cur, 0, sizeof(xsltExtDef));
     if (prefix != NULL)
-       cur->prefix = xmlStrdup(prefix);
+        cur->prefix = xmlStrdup(prefix);
     if (URI != NULL)
-       cur->URI = xmlStrdup(URI);
-    return(cur);
+        cur->URI = xmlStrdup(URI);
+    return (cur);
 }
 
 /**
@@ -83,7 +108,6 @@ xsltFreeExtDef(xsltExtDefPtr extensiond) {
        xmlFree(extensiond->prefix);
     if (extensiond->URI != NULL)
        xmlFree(extensiond->URI);
-    memset(extensiond, -1, sizeof(xsltExtDef));
     xmlFree(extensiond);
 }
 
@@ -104,10 +128,89 @@ xsltFreeExtDefList(xsltExtDefPtr extensiond) {
     }
 }
 
+/**
+ * xsltNewExtModule:
+ * @initFunc:  the module initialization function
+ * @shutdownFunc:  the module shutdown 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)
+{
+    xsltExtModulePtr cur;
+
+    cur = (xsltExtModulePtr) xmlMalloc(sizeof(xsltExtModule));
+    if (cur == NULL) {
+        xsltGenericError(xsltGenericErrorContext,
+                         "xsltNewExtModule : malloc failed\n");
+        return (NULL);
+    }
+    cur->initFunc = initFunc;
+    cur->shutdownFunc = shutdownFunc;
+    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) {
+        xsltGenericError(xsltGenericErrorContext,
+                         "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);
+}
+
 
 /************************************************************************
  *                                                                     *
- *             The interpreter for the precompiled patterns            *
+ *             The stylesheet extension prefixes handling              *
  *                                                                     *
  ************************************************************************/
 
@@ -156,6 +259,12 @@ xsltRegisterExtPrefix(xsltStylesheetPtr style,
     return(0);
 }
 
+/************************************************************************
+ *                                                                     *
+ *             The extensions modules interfaces                       *
+ *                                                                     *
+ ************************************************************************/
+
 /**
  * xsltRegisterExtFunction:
  * @ctxt: an XSLT transformation context
@@ -173,6 +282,9 @@ xsltRegisterExtFunction(xsltTransformContextPtr ctxt, const xmlChar *name,
     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);
     return(xmlHashAddEntry2(ctxt->extFunctions, name, URI, (void *) function));
@@ -215,6 +327,166 @@ xsltFreeCtxtExts(xsltTransformContextPtr ctxt) {
 }
 
 /**
+ * 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) || (ctxt->extInfos == NULL) || (URI == NULL))
+        return (NULL);
+    data = (xsltExtDataPtr) xmlHashLookup(ctxt->extInfos, URI);
+    if (data == NULL)
+        return (NULL);
+    return (data->extData);
+}
+
+/**
+ * xsltInitCtxtExts:
+ * @ctxt: an XSLT transformation context
+ *
+ * Initialize the set of modules associated to the extension prefixes
+ *
+ * Returns the number of modules initialized or -1 in case of error
+ */
+int
+xsltInitCtxtExts(xsltTransformContextPtr ctxt)
+{
+    int ret = 0;
+    xsltStylesheetPtr style;
+    xsltExtDefPtr def;
+    xsltExtModulePtr module;
+    xsltExtDataPtr data;
+    void *extData;
+
+    if (ctxt == NULL)
+        return (-1);
+
+    style = ctxt->style;
+    if (style == NULL)
+        return (-1);
+    while (style != NULL) {
+        def = (xsltExtDefPtr) style->nsDefs;
+        while (def != NULL) {
+            if (def->URI != NULL) {
+                if (ctxt->extInfos == NULL) {
+                    ctxt->extInfos = xmlHashCreate(10);
+                    if (ctxt->extInfos == NULL)
+                        return (-1);
+                    data = NULL;
+                } else {
+                    data =
+                        (xsltExtDataPtr) xmlHashLookup(ctxt->extInfos,
+                                                       def->URI);
+                }
+                if (data == NULL) {
+                    /*
+                     * Register this module
+                     */
+                    module = xmlHashLookup(xsltExtensionsHash, def->URI);
+                    if (module == NULL) {
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+                        xsltGenericDebug(xsltGenericDebugContext,
+                                    "Not registered extension module : %s\n",
+                                         def->URI);
+#endif
+                    } else {
+                        if (module->initFunc != NULL) {
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+                            xsltGenericDebug(xsltGenericDebugContext,
+                                             "Initializing module : %s\n",
+                                             def->URI);
+#endif
+                            extData = module->initFunc(ctxt, def->URI);
+                        } else {
+                            extData = NULL;
+                        }
+                        data = xsltNewExtData(module, extData);
+                        if (data == NULL)
+                            return (-1);
+                        if (xmlHashAddEntry(ctxt->extInfos, def->URI,
+                                            (void *) data) < 0) {
+                            xsltGenericError(xsltGenericErrorContext,
+                                             "Failed to register module : %s\n",
+                                             def->URI);
+                        } else {
+                           ret++;
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+                        xsltGenericDebug(xsltGenericDebugContext,
+                                         "Registered module : %s\n",
+                                         def->URI);
+#endif
+                       }
+
+                    }
+                }
+            }
+            def = def->next;
+        }
+        style = xsltNextImport(style);
+    }
+#ifdef WITH_XSLT_DEBUG_EXTENSIONS
+    xsltGenericDebug(xsltGenericDebugContext, "Registered %d modules\n", ret);
+#endif
+    return (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);
+    xmlHashRemoveEntry(ctxt->extInfos, URI,
+                       (xmlHashDeallocator) xsltFreeExtData);
+}
+
+/**
+ * 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;
+}
+
+/**
  * xsltCheckExtPrefix:
  * @style: the stylesheet
  * @prefix: the namespace prefix (possibly NULL)
@@ -239,3 +511,103 @@ xsltCheckExtPrefix(xsltStylesheetPtr style, const xmlChar *prefix) {
     return(0);
 }
 
+/**
+ * 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)
+{
+    int ret;
+    xsltExtModulePtr module;
+
+    if ((URI == NULL) || (initFunc == NULL))
+        return (-1);
+    if (xsltExtensionsHash == NULL)
+        xsltExtensionsHash = xmlHashCreate(10);
+
+    if (xsltExtensionsHash == NULL)
+        return (-1);
+
+    module = xmlHashLookup(xsltExtensionsHash, URI);
+    if (module != NULL) {
+        if ((module->initFunc == initFunc) &&
+            (module->shutdownFunc == shutdownFunc))
+            return (0);
+        return (-1);
+    }
+    module = xsltNewExtModule(initFunc, shutdownFunc);
+    if (module == NULL)
+        return (-1);
+    ret = xmlHashAddEntry(xsltExtensionsHash, URI, (void *) module);
+    return (ret);
+}
+
+/**
+ * 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);
+
+    ret =
+        xmlHashRemoveEntry(xsltExtensionsHash, URI,
+                           (xmlHashDeallocator) xsltFreeExtModule);
+    return (ret);
+}
+
+/**
+ * xsltUnregisterExtModule:
+ *
+ * Unregister all the XSLT extension module from the library.
+ */
+void
+xsltUnregisterAllExtModules(void)
+{
+    if (xsltExtensionsHash == NULL)
+       return;
+
+    xmlHashFree(xsltExtensionsHash, (xmlHashDeallocator) xsltFreeExtModule);
+    xsltExtensionsHash = NULL;
+}
+
+/**
+ * xsltXPathGetTransformContext:
+ * @ctxt:  an XPath transformation context
+ *
+ * Returns 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);
+}
+
+
index a4f053e..c3241ca 100644 (file)
 extern "C" {
 #endif
 
-int            xsltRegisterExtPrefix   (xsltStylesheetPtr style,
-                                        const xmlChar *prefix,
+/**
+ * Extension Modules API
+ */
+
+/**
+ * xsltExtInitFunction:
+ * @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
+ */
+typedef void * (*xsltExtInitFunction)  (xsltTransformContextPtr ctxt,
                                         const xmlChar *URI);
-int            xsltCheckExtPrefix      (xsltStylesheetPtr style,
-                                        const xmlChar *prefix);
+
+/**
+ * xsltExtShutdownFunction:
+ * @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
+ */
+typedef void (*xsltExtShutdownFunction) (xsltTransformContextPtr ctxt,
+                                        const xmlChar *URI,
+                                        void *data);
+
+int            xsltRegisterExtModule   (const xmlChar *URI,
+                                        xsltExtInitFunction initFunc,
+                                        xsltExtShutdownFunction shutdownFunc);
+
+int            xsltUnregisterExtModule (const xmlChar * URI);
+
+void           xsltUnregisterAllExtModules(void);
+
+void *         xsltGetExtData          (xsltTransformContextPtr ctxt,
+                                        const xmlChar *URI);
+
+void           xsltShutdownCtxtExts    (xsltTransformContextPtr ctxt);
+
+xsltTransformContextPtr
+               xsltXPathGetTransformContext
+                                       (xmlXPathParserContextPtr ctxt);
+
 int            xsltRegisterExtFunction (xsltTransformContextPtr ctxt,
                                         const xmlChar *name,
                                         const xmlChar *URI,
@@ -29,9 +69,22 @@ int          xsltRegisterExtElement  (xsltTransformContextPtr ctxt,
                                         const xmlChar *name,
                                         const xmlChar *URI,
                                         xsltTransformFunction function);
+
+/**
+ * Extension Prefix handling API
+ * Those are used by the XSLT (pre)processor.
+ */
+
+int            xsltRegisterExtPrefix   (xsltStylesheetPtr style,
+                                        const xmlChar *prefix,
+                                        const xmlChar *URI);
+int            xsltCheckExtPrefix      (xsltStylesheetPtr style,
+                                        const xmlChar *prefix);
+int            xsltInitCtxtExts        (xsltTransformContextPtr ctxt);
 void           xsltFreeCtxtExts        (xsltTransformContextPtr ctxt);
 void           xsltFreeExts            (xsltStylesheetPtr style);
 
+
 #ifdef __cplusplus
 }
 #endif
index 4f1786b..223e3d9 100644 (file)
  *
  * Process an debug node
  */
-void 
+void
 xsltDebug(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED,
-         xmlNodePtr inst ATTRIBUTE_UNUSED, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
+          xmlNodePtr inst ATTRIBUTE_UNUSED,
+          xsltStylePreCompPtr comp ATTRIBUTE_UNUSED)
+{
     int i, j;
 
     fprintf(stdout, "Templates:\n");
-    for (i = 0, j = ctxt->templNr - 1;((i < 15) && (j >= 0));i++,j--) {
-       fprintf(stdout, "#%d ", i);
-       if (ctxt->templTab[j]->name != NULL)
-           fprintf(stdout, "name %s ", ctxt->templTab[j]->name);
-       if (ctxt->templTab[j]->match != NULL)
-           fprintf(stdout, "name %s ", ctxt->templTab[j]->match);
-       if (ctxt->templTab[j]->mode != NULL)
-           fprintf(stdout, "name %s ", ctxt->templTab[j]->mode);
-       fprintf(stdout, "\n");
+    for (i = 0, j = ctxt->templNr - 1; ((i < 15) && (j >= 0)); i++, j--) {
+        fprintf(stdout, "#%d ", i);
+        if (ctxt->templTab[j]->name != NULL)
+            fprintf(stdout, "name %s ", ctxt->templTab[j]->name);
+        if (ctxt->templTab[j]->match != NULL)
+            fprintf(stdout, "name %s ", ctxt->templTab[j]->match);
+        if (ctxt->templTab[j]->mode != NULL)
+            fprintf(stdout, "name %s ", ctxt->templTab[j]->mode);
+        fprintf(stdout, "\n");
     }
     fprintf(stdout, "Variables:\n");
-    for (i = 0, j = ctxt->varsNr - 1;((i < 15) && (j >= 0));i++,j--) {
-       xsltStackElemPtr cur;
+    for (i = 0, j = ctxt->varsNr - 1; ((i < 15) && (j >= 0)); i++, j--) {
+        xsltStackElemPtr cur;
 
-       if (ctxt->varsTab[j] == NULL)
-           continue;
-       fprintf(stdout, "#%d\n", i);
-       cur = ctxt->varsTab[j];
-       while (cur != NULL) {
-           if (cur->comp == NULL) {
-               fprintf(stdout, "corrupted !!!\n");
-           } else if (cur->comp->type == XSLT_FUNC_PARAM) {
-               fprintf(stdout, "param ");
-           } else if (cur->comp->type == XSLT_FUNC_VARIABLE) {
-               fprintf(stdout, "var ");
-           }
-           if (cur->name != NULL)
-               fprintf(stdout, "%s ", cur->name);
-           else
-               fprintf(stdout, "noname !!!!");
+        if (ctxt->varsTab[j] == NULL)
+            continue;
+        fprintf(stdout, "#%d\n", i);
+        cur = ctxt->varsTab[j];
+        while (cur != NULL) {
+            if (cur->comp == NULL) {
+                fprintf(stdout, "corrupted !!!\n");
+            } else if (cur->comp->type == XSLT_FUNC_PARAM) {
+                fprintf(stdout, "param ");
+            } else if (cur->comp->type == XSLT_FUNC_VARIABLE) {
+                fprintf(stdout, "var ");
+            }
+            if (cur->name != NULL)
+                fprintf(stdout, "%s ", cur->name);
+            else
+                fprintf(stdout, "noname !!!!");
 #ifdef LIBXML_DEBUG_ENABLED
-           if (cur->value != NULL) {
-               xmlXPathDebugDumpObject(stdout, cur->value, 1);
-           } else {
-               fprintf(stdout, "NULL !!!!");
-           }
+            if (cur->value != NULL) {
+                xmlXPathDebugDumpObject(stdout, cur->value, 1);
+            } else {
+                fprintf(stdout, "NULL !!!!");
+            }
 #endif
-           fprintf(stdout, "\n");
-           cur = cur->next;
-       }
-       
+            fprintf(stdout, "\n");
+            cur = cur->next;
+        }
+
     }
 }
 
@@ -131,6 +133,32 @@ xsltFunctionNodeSet(xmlXPathParserContextPtr ctxt, int nargs){
 }
 
 /**
+ * xsltFunctionLocalTime:
+ * @ctxt:  the XPath Parser context
+ * @nargs:  the number of arguments
+ *
+ * Implement the localTime XSLT function used by NORM
+ *   string localTime(???)
+ *
+ * This function is available in Norm's extension namespace
+ */
+static void
+xsltFunctionLocalTime(xmlXPathParserContextPtr ctxt, int nargs) {
+    if ((nargs < 0) || (nargs > 1)) {
+        xsltGenericError(xsltGenericErrorContext,
+               "localTime() : invalid number of args %d\n", nargs);
+       ctxt->error = XPATH_INVALID_ARITY;
+       return;
+    }
+    /* TODO : Norm's localTime() extension */
+    if (nargs == 1) {
+       xmlXPathStringFunction(ctxt, 1);
+    } else {
+       valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
+    }
+}
+
+/**
  * xsltRegisterExtras:
  * @ctxt:  a XSLT process context
  *
@@ -144,6 +172,8 @@ xsltRegisterExtras(xsltTransformContextPtr ctxt) {
                            XSLT_SAXON_NAMESPACE, xsltFunctionNodeSet);
     xsltRegisterExtFunction(ctxt, (const xmlChar *) "node-set",
                            XSLT_XT_NAMESPACE, xsltFunctionNodeSet);
+    xsltRegisterExtFunction(ctxt, (const xmlChar *) "localTime",
+                           XSLT_NORM_SAXON_NAMESPACE, xsltFunctionLocalTime);
     xsltRegisterExtElement(ctxt, (const xmlChar *) "debug",
                            XSLT_LIBXSLT_NAMESPACE, xsltDebug);
     xsltRegisterExtElement(ctxt, (const xmlChar *) "output",
index df24228..5bc84cd 100644 (file)
@@ -45,6 +45,15 @@ extern "C" {
 #define XSLT_XALAN_NAMESPACE ((xmlChar *)      \
                                "org.apache.xalan.xslt.extensions.Redirect")
 
+/**
+ * XSLT_NORM_SAXON_NAMESPACE:
+ *
+ * This is Nom's namespace for SAXON extensions
+ */
+#define XSLT_NORM_SAXON_NAMESPACE ((xmlChar *) \
+       "http://nwalsh.com/xslt/ext/com.nwalsh.saxon.CVS")
+
+
 void           xsltFunctionNodeSet     (xmlXPathParserContextPtr ctxt,
                                         int nargs);
 
index 4b43036..bba428f 100644 (file)
@@ -35,6 +35,7 @@
 #include "xsltInternals.h"
 #include "xsltutils.h"
 #include "functions.h"
+#include "extensions.h"
 #include "numbersInternals.h"
 #include "keys.h"
 #include "documents.h"
@@ -153,7 +154,9 @@ xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs){
            }
            base = xmlNodeGetBase(target->doc, target);
        } else {
-           xsltTransformContextPtr tctxt = ctxt->context->extra;
+           xsltTransformContextPtr tctxt;
+           
+           tctxt = xsltXPathGetTransformContext(ctxt);
            if ((tctxt != NULL) && (tctxt->inst != NULL)) {
                base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst);
            } else if ((tctxt != NULL) && (tctxt->style != NULL) &&
@@ -170,7 +173,7 @@ xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs){
        } else {
            xsltTransformContextPtr tctxt;
 
-           tctxt = (xsltTransformContextPtr) ctxt->context->extra;
+           tctxt = xsltXPathGetTransformContext(ctxt);
            if (tctxt == NULL) {
                xsltGenericError(xsltGenericErrorContext,
                        "document() : internal error tctxt == NULL\n");
@@ -209,8 +212,6 @@ xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
     const xmlChar *keyURI;
     xsltTransformContextPtr tctxt;
 
-    tctxt = ((xsltTransformContextPtr)ctxt->context->extra);
-
     if (nargs != 2) {
         xsltGenericError(xsltGenericErrorContext,
                "key() : expects two arguments\n");
@@ -292,6 +293,8 @@ xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
        obj2 = valuePop(ctxt);
        value = obj2->stringval;
 
+       tctxt = xsltXPathGetTransformContext(ctxt);
+
        nodelist = xsltGetKey(tctxt, key, keyURI, value);
        valuePush(ctxt, xmlXPathWrapNodeSet(
                        xmlXPathNodeSetMerge(NULL, nodelist)));
@@ -366,8 +369,14 @@ xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs)
     xsltStylesheetPtr sheet;
     xsltDecimalFormatPtr formatValues;
     xmlChar *result;
+    xsltTransformContextPtr tctxt;
 
-    sheet = ((xsltTransformContextPtr)ctxt->context->extra)->style;
+    tctxt = xsltXPathGetTransformContext(ctxt);
+    if (tctxt == NULL)
+       return;
+    sheet = tctxt->style;
+    if (sheet == NULL)
+       return;
     formatValues = sheet->decimalFormat;
     
     switch (nargs) {
@@ -503,7 +512,7 @@ xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){
                xsltStylesheetPtr sheet;
                xsltTransformContextPtr tctxt;
 
-               tctxt = (xsltTransformContextPtr)ctxt->context->extra;
+               tctxt = xsltXPathGetTransformContext(ctxt);
                if ((tctxt != NULL) && (tctxt->inst != NULL) &&
                    (xmlStrEqual(tctxt->inst->name, BAD_CAST "variable")) &&
                    (tctxt->inst->parent != NULL) &&
@@ -514,7 +523,8 @@ xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){
                    sheet = NULL;
                if ((sheet != NULL) && (sheet->doc != NULL) &&
                    (sheet->doc->URL != NULL) &&
-                   (xmlStrstr(sheet->doc->URL, "chunk") != NULL)) {
+                   (xmlStrstr(sheet->doc->URL,
+                              (const xmlChar *)"chunk") != NULL)) {
                    valuePush(ctxt, xmlXPathNewString(
                        (const xmlChar *)"libxslt (SAXON 6.2 compatible)"));
 
@@ -646,7 +656,7 @@ xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){
        ctxt->error = XPATH_INVALID_ARITY;
        return;
     }
-    tctxt = (xsltTransformContextPtr) ctxt->context->extra;
+    tctxt = xsltXPathGetTransformContext(ctxt);
     if (tctxt == NULL) {
        xsltGenericError(xsltGenericErrorContext,
                "current() : internal error tctxt == NULL\n");
@@ -656,6 +666,165 @@ xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){
     }
 }
 
+/************************************************************************
+ *                                                                     *
+ *             Test of the extension module API                        *
+ *                                                                     *
+ ************************************************************************/
+
+static xmlChar *testData = 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)
+{
+    xsltTransformContextPtr tctxt;
+    void *data;
+
+    if (testData == NULL) {
+        xsltGenericError(xsltGenericErrorContext,
+                         "xsltExtFunctionTest: not initialized\n");
+        return;
+    }
+    tctxt = xsltXPathGetTransformContext(ctxt);
+    if (tctxt == NULL) {
+        xsltGenericError(xsltGenericErrorContext,
+                         "xsltExtFunctionTest: failed to get the transformation context\n");
+        return;
+    }
+    data = xsltGetExtData(tctxt, (const xmlChar *) XSLT_DEFAULT_URL);
+    if (data == NULL) {
+        xsltGenericError(xsltGenericErrorContext,
+                         "xsltExtFunctionTest: failed to get module data\n");
+        return;
+    }
+    if (data != testData) {
+        xsltGenericError(xsltGenericErrorContext,
+                         "xsltExtFunctionTest: got wrong module data\n");
+        return;
+    }
+#ifdef WITH_XSLT_DEBUG_FUNCTION
+    xsltGenericDebug(xsltGenericDebugContext,
+                     "libxslt:test() called with %d args\n", nargs);
+#endif
+}
+
+/**
+ * xsltExtElementTest:
+ * @ctxt:  an XSLT processing context
+ * @node:  The current node
+ * @inst:  the instruction in the stylesheet
+ * @comp:  precomputed informations
+ *
+ * Process an debug node
+ */
+static void
+xsltExtElementTest(xsltTransformContextPtr ctxt, xmlNodePtr node,
+                   xmlNodePtr inst,
+                   xsltStylePreCompPtr comp ATTRIBUTE_UNUSED)
+{
+    xmlNodePtr comment;
+
+    if (testData == NULL) {
+        xsltGenericError(xsltGenericErrorContext,
+                         "xsltExtElementTest: not initialized\n");
+        return;
+    }
+    if (ctxt == NULL) {
+        xsltGenericError(xsltGenericErrorContext,
+                         "xsltExtElementTest: no transformation context\n");
+        return;
+    }
+    if (node == NULL) {
+        xsltGenericError(xsltGenericErrorContext,
+                         "xsltExtElementTest: no current node\n");
+        return;
+    }
+    if (inst == NULL) {
+        xsltGenericError(xsltGenericErrorContext,
+                         "xsltExtElementTest: no instruction\n");
+        return;
+    }
+    if (ctxt->insert == NULL) {
+        xsltGenericError(xsltGenericErrorContext,
+                         "xsltExtElementTest: no insertion point\n");
+        return;
+    }
+    comment =
+        xmlNewComment((const xmlChar *)
+                      "libxslt:test element test worked");
+    xmlAddChild(ctxt->insert, comment);
+}
+
+/**
+ * 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 (testData != NULL) {
+        xsltGenericError(xsltGenericErrorContext,
+                         "xsltExtInitTest: already initialized\n");
+        return (NULL);
+    }
+    testData = (void *) "test data";
+    xsltRegisterExtFunction(ctxt, (const xmlChar *) "test",
+                            (const xmlChar *) XSLT_DEFAULT_URL,
+                            xsltExtFunctionTest);
+    xsltRegisterExtElement(ctxt, (const xmlChar *) "test",
+                            (const xmlChar *) XSLT_DEFAULT_URL,
+                            xsltExtElementTest);
+
+    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 ATTRIBUTE_UNUSED,
+                    const xmlChar * URI, void *data)
+{
+    if (testData == NULL) {
+        xsltGenericError(xsltGenericErrorContext,
+                         "xsltExtShutdownTest: not initialized\n");
+        return;
+    }
+    if (data != testData) {
+        xsltGenericError(xsltGenericErrorContext,
+                         "xsltExtShutdownTest: wrong data\n");
+    }
+    testData = NULL;
+    xsltGenericDebug(xsltGenericDebugContext,
+                     "Unregistered test module : %s\n", URI);
+}
+
+/************************************************************************
+ *                                                                     *
+ *             Registration of XSLT and libxslt functions              *
+ *                                                                     *
+ ************************************************************************/
+
 /**
  * xsltRegisterAllFunctions:
  * @ctxt:  the XPath context
@@ -663,23 +832,27 @@ xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){
  * Registers all default XSLT functions in this context
  */
 void
-xsltRegisterAllFunctions(xmlXPathContextPtr ctxt) {
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"current",
+xsltRegisterAllFunctions(xmlXPathContextPtr ctxt)
+{
+    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "current",
                          xsltCurrentFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"document",
+    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "document",
                          xsltDocumentFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"key",
-                         xsltKeyFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"unparsed-entity-uri",
+    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "key", xsltKeyFunction);
+    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "unparsed-entity-uri",
                          xsltUnparsedEntityURIFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"format-number",
+    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "format-number",
                          xsltFormatNumberFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"generate-id",
+    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "generate-id",
                          xsltGenerateIdFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"system-property",
+    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "system-property",
                          xsltSystemPropertyFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"element-available",
+    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "element-available",
                          xsltElementAvailableFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"function-available",
+    xmlXPathRegisterFunc(ctxt, (const xmlChar *) "function-available",
                          xsltFunctionAvailableFunction);
+
+    xsltRegisterExtModule((const xmlChar *) XSLT_DEFAULT_URL,
+                         xsltExtInitTest,
+                          xsltExtShutdownTest);
 }
index 81cc891..c6b20b3 100644 (file)
@@ -2897,7 +2897,6 @@ xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc,
     xsltStackElemPtr variables;
     xsltStackElemPtr vptr;
 
-
     if ((style == NULL) || (doc == NULL))
        return(NULL);
     ctxt = xsltNewTransformContext(style, doc);
@@ -2971,9 +2970,11 @@ xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc,
        xsltEvalUserParams(ctxt, params);
     xsltEvalGlobalVariables(ctxt);
     ctxt->node = (xmlNodePtr) doc;
+    xsltInitCtxtExts(ctxt);
     varsPush(ctxt, NULL);
     xsltProcessOneNode(ctxt, ctxt->node);
     xsltFreeStackElemList(varsPop(ctxt));
+    xsltShutdownCtxtExts(ctxt);
 
     xsltCleanupTemplates(style); /* TODO: <- style should be read only */
 
index 5eddd7f..43a1a68 100644 (file)
@@ -344,6 +344,7 @@ struct _xsltTransformContext {
      */
     xmlHashTablePtr   extFunctions;    /* the extension functions */
     xmlHashTablePtr   extElements;     /* the extension elements */
+    xmlHashTablePtr   extInfos;                /* the extension data */
 
     const xmlChar *mode;               /* the current mode */
     const xmlChar *modeURI;            /* the current mode URI */
index e979f61..1d0e5e9 100644 (file)
@@ -36,6 +36,7 @@
 #include <libxslt/xsltInternals.h>
 #include <libxslt/transform.h>
 #include <libxslt/xsltutils.h>
+#include <libxslt/extensions.h>
 
 #ifdef WIN32
 #ifdef _MSC_VER
@@ -442,6 +443,7 @@ main(int argc, char **argv)
         }
         xsltFreeStylesheet(cur);
     }
+    xsltUnregisterAllExtModules();
     xmlCleanupParser();
     xmlMemoryDump();
     return (0);
index e93769d..bb6ac23 100644 (file)
@@ -1,7 +1,7 @@
 ## Process this file with automake to produce Makefile.in
 
 SUBDIRS=docs REC1 REC2 REC general namespaces numbers documents \
-        xmlspec multiple XSLTMark docbook
+        extensions xmlspec multiple XSLTMark docbook 
 
 all:
 
diff --git a/tests/extensions/Makefile.am b/tests/extensions/Makefile.am
new file mode 100644 (file)
index 0000000..2bf71a9
--- /dev/null
@@ -0,0 +1,29 @@
+## Process this file with automake to produce Makefile.in
+
+$(top_builddir)/libxslt/xsltproc:
+       @(cd ../../libxslt ; make xsltproc)
+
+EXTRA_DIST = \
+    module.xml module.xsl module.out
+
+
+all: test
+
+test tests: $(top_builddir)/libxslt/xsltproc
+       @(echo > .memdump)
+       @(for i in $(srcdir)/*.xml ; do \
+         if [ -d $$i ] ; then continue ; fi ; \
+         doc=`basename $$i .xml` ; \
+         for j in $(srcdir)/$$doc*.xsl ; do \
+         if [ ! -f $$j ] ; then continue ; fi ; \
+         if [ -d $$j ] ; then continue ; fi ; \
+         name=`basename $$j .xsl`; \
+         out=$(srcdir)/"$$name".out; \
+         echo Running $$j on $$i ; \
+         $(top_builddir)/libxslt/xsltproc $$j $$i > result.$$name;\
+         if [ ! -f $$out ] ; then cp result.$$name $$out ; \
+         else diff $$out result.$$name; fi ; \
+         grep "MORY ALLO" .memdump  | grep -v "MEMORY ALLOCATED : 0" || true;\
+         rm -f result.$$name ; \
+         done ; done)
+
diff --git a/tests/extensions/module.out b/tests/extensions/module.out
new file mode 100644 (file)
index 0000000..fa2cdd7
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<!--libxslt:test element test worked-->
+SUCCESS
diff --git a/tests/extensions/module.xml b/tests/extensions/module.xml
new file mode 100644 (file)
index 0000000..69d62f2
--- /dev/null
@@ -0,0 +1 @@
+<doc/>
diff --git a/tests/extensions/module.xsl b/tests/extensions/module.xsl
new file mode 100644 (file)
index 0000000..7f69c18
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+               xmlns:libxslt="http://xmlsoft.org/XSLT/"
+               xmlns:test="http://xmlsoft.org/XSLT/"
+               xsl:extension-element-prefixes="libxslt test"
+                version='1.0'>
+<!-- the prefix is registered twice to check single initialization -->
+<xsl:template match="/">
+<libxslt:test/>
+<xsl:value-of select="libxslt:test('SUCCESS')"/>
+</xsl:template>
+</xsl:stylesheet>