Re: [xslt] plugin patch v3 plus exslt:regexp implementation



On Mon, Jan 10, 2005 at 09:53:56PM -0500, Joel Reed wrote:
> On Sun, Jan 09, 2005 at 09:14:18AM -0500, Daniel Veillard wrote:
> > On Sat, Jan 08, 2005 at 07:22:04PM -0500, Joel Reed wrote:

snip

> patch against CVS with the following changes:
> 
> *) plugin names now include full extension namespace minus protocol
>    e.g. /usr/local/lib/libxslt/1.1/exslt_org_regular_expressions.so
> *) init funcs are now based on extension namespace
>    e.g.  exslt_org_regular_expressions_init
> *) more comments for xsltExtModuleRegisterDynamic
> *) xsltExtModuleRegisterDynamic always a function
> *) if regfunc not found unload the module immediately

since this last patch was not applied yet, the attached patch below
is not incremental. it includes the above changes plus:

*) don't require a trailing slash on getenv("LIBXSLT_PLUGINS_PATH")
*) a test plugin, copied from extensions.c test code
   and placed in libxslt/libxslt/testplugin.c 
*) a test case: libxslt/tests/plugins/

jr

File changed/added:

libxslt/configure.in
libxslt/libxslt/Makefile.am
libxslt/libxslt/extensions.c
libxslt/libxslt/testplugin.c
libxslt/tests/Makefile.am
libxslt/tests/plugins/plugin.out
libxslt/tests/plugins/plugin.xml
libxslt/tests/plugins/plugin.xsl

diff -up -urNwbB -X /home/jreed/src/lm-4.0/lm/do-not-diff libxslt-orig/configure.in libxslt/configure.in
--- libxslt-orig/configure.in	2005-01-10 21:49:41.000000000 -0500
+++ libxslt/configure.in	2005-01-13 21:53:40.000000000 -0500
@@ -22,6 +22,7 @@ LIBXSLT_VERSION=$LIBXSLT_MAJOR_VERSION.$
 LIBXSLT_VERSION_INFO=`expr $LIBXSLT_MAJOR_VERSION + $LIBXSLT_MINOR_VERSION`:$LIBXSLT_MICRO_VERSION:$LIBXSLT_MINOR_VERSION
 
 LIBXSLT_VERSION_NUMBER=`expr $LIBXSLT_MAJOR_VERSION \* 10000 + $LIBXSLT_MINOR_VERSION \* 100 + $LIBXSLT_MICRO_VERSION`
+LIBXSLT_MAJOR_MINOR_VERSION=$LIBXSLT_MAJOR_VERSION.$LIBXSLT_MINOR_VERSION
 
 if test -f CVS/Entries; then
   extra=`grep ChangeLog CVS/Entries | grep -v LIBXSLT | sed -e s\%/ChangeLog/1\.%% -e s\%/.*$%%`
@@ -39,6 +40,7 @@ AC_SUBST(LIBXSLT_VERSION)
 AC_SUBST(LIBXSLT_VERSION_INFO)
 AC_SUBST(LIBXSLT_VERSION_NUMBER)
 AC_SUBST(LIBXSLT_VERSION_EXTRA)
+AC_SUBST(LIBXSLT_MAJOR_MINOR_VERSION)
 
 dnl
 dnl libexslt is an extension library
@@ -456,13 +458,14 @@ else
 fi
 
 AC_SUBST(WITH_MODULES)
+AM_CONDITIONAL(WITH_MODULES, test "$WITH_MODULES" == "1")
 
 dnl
 dnl setup default module path
 dnl
 module_prefix=$prefix
 test "x$module_prefix" = xNONE && module_prefix=$ac_default_prefix
-LIBXSLT_DEFAULT_PLUGINS_PATH="\"$module_prefix/lib/libxslt/$LIBXSLT_MAJOR_VERSION.$LIBXSLT_MINOR_VERSION/\""
+LIBXSLT_DEFAULT_PLUGINS_PATH="\"$module_prefix/lib/libxslt/$LIBXSLT_MAJOR_VERSION.$LIBXSLT_MINOR_VERSION\""
 AC_SUBST(LIBXSLT_DEFAULT_PLUGINS_PATH)
 
 dnl
diff -up -urNwbB -X /home/jreed/src/lm-4.0/lm/do-not-diff libxslt-orig/libxslt/Makefile.am libxslt/libxslt/Makefile.am
--- libxslt-orig/libxslt/Makefile.am	2004-02-13 11:07:06.000000000 -0500
+++ libxslt/libxslt/Makefile.am	2005-01-13 20:26:45.000000000 -0500
@@ -57,5 +57,21 @@ man_MANS = libxslt.3
 
 EXTRA_DIST = $(man_MANS) trio.h triodef.h
 
+
+# somewhat unconventional pkglibdir, but noinst_LTLIBRARIES
+# never build DSOs (afaik). NOTE: must be defined outside the AM_CONDITIONAL
+pkglibdir=$(shell pwd)/../tests/plugins
+
+if WITH_MODULES
+pkglib_LTLIBRARIES = xmlsoft_org_xslt_testplugin.la
+
+xmlsoft_org_xslt_testplugin_la_CFLAGS = -DMODULE_COMPILE $(LIBXML_CFLAGS) $(LIBXSLT_CFLAGS)
+xmlsoft_org_xslt_testplugin_la_SOURCES = testplugin.c
+xmlsoft_org_xslt_testplugin_la_LDFLAGS = -module -avoid-version $(LIBXML_LIBS) $(LIBXSLT_LIBS)
+
+check-local: install-pkglibLTLIBRARIES
+
+endif
+
 xsltproc: all
 	@(cd ../xsltproc ; $(MAKE))
diff -up -urNwbB -X /home/jreed/src/lm-4.0/lm/do-not-diff libxslt-orig/libxslt/extensions.c libxslt/libxslt/extensions.c
--- libxslt-orig/libxslt/extensions.c	2005-01-09 11:05:10.000000000 -0500
+++ libxslt/libxslt/extensions.c	2005-01-13 21:52:45.000000000 -0500
@@ -298,8 +298,17 @@ typedef void (*exsltRegisterFunction) (v
  * xsltExtModuleRegisterDynamic:
  * @URI:  the function or element namespace URI
  *
- * Looks up an extension module to dynamically load
- * based on the 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. 
  */
@@ -310,10 +319,12 @@ xsltExtModuleRegisterDynamic(const xmlCh
 
     xmlModulePtr m;
     exsltRegisterFunction regfunc;
+    xmlChar *ext_name;
     xmlChar module_filename[PATH_MAX];
-    const xmlChar *extNameBegin = NULL;
-    const xmlChar *extDirectory = NULL;
-    int i, rc, seen_before;
+    const xmlChar *ext_directory = NULL;
+    const xmlChar *protocol = NULL;
+    xmlChar *i, *regfunc_name;
+    int rc, seen_before;
 
     /* check for bad inputs */
     if (URI == NULL)
@@ -331,43 +342,68 @@ xsltExtModuleRegisterDynamic(const xmlCh
         return (-1);
     }
 
-    for (i = xmlStrlen(URI); i != 0 && extNameBegin == NULL; --i) {
-        if (URI[i - 1] == '/')
-            extNameBegin = URI + i;
+    /* transform extension namespace into a module name */
+    protocol = xmlStrstr(URI, "://");
+    ext_name = xmlStrdup(protocol+3);
+
+    i = ext_name;
+    while ('\0' != *i) {
+      if (('/' == *i) || ('\\' == *i) || 
+          ('.' == *i) || ('-' == *i)) *i = '_';
+      i++;
     }
 
-    if (extNameBegin == NULL || *extNameBegin == '\0')
-        return (-1);
+    if (*(i-1) == '_') *i = '\0';
 
     /* determine module directory */
-    extDirectory = getenv(BAD_CAST "LIBXSLT_PLUGINS_PATH");
-    if (NULL == extDirectory)
-        extDirectory = LIBXSLT_DEFAULT_PLUGINS_PATH();
-    if (NULL == extDirectory)
+    ext_directory = getenv(BAD_CAST "LIBXSLT_PLUGINS_PATH");
+    if (NULL == ext_directory)
+        ext_directory = LIBXSLT_DEFAULT_PLUGINS_PATH();
+    if (NULL == ext_directory)
         return (-1);
 
     /* build the module filename, and confirm the module exists */
-    xmlStrPrintf(module_filename, sizeof(module_filename), "%s%s%s",
-                 extDirectory, extNameBegin, LIBXML_MODULE_EXTENSION);
-    if (1 != xmlCheckFilename(module_filename))
+    xmlStrPrintf(module_filename, sizeof(module_filename), "%s/%s%s",
+                 ext_directory, ext_name, LIBXML_MODULE_EXTENSION);
+    if (1 != xmlCheckFilename(module_filename)) {
+      xmlFree(ext_name);
         return (-1);
+    }
 
+    /* attempt to open the module*/
     m = xmlModuleOpen(module_filename, 0);
-    if (NULL == m)
+    if (NULL == m) {
+      xmlFree(ext_name);
         return (-1);
+    }
+
+    /* construct initialization func name */
+    regfunc_name = xmlStrdup(ext_name);
+    regfunc_name = xmlStrcat(regfunc_name, "_init");
 
-    rc = xmlModuleSymbol(m, "exsltRegisterModule", (void **) &regfunc);
+    rc = xmlModuleSymbol(m, regfunc_name, (void **) &regfunc);
     if (0 == rc) {
+      /* call the module's init function */
         (*regfunc) ();
-    }
 
     /* register this module in our hash */
     xmlHashAddEntry(xsltModuleHash, URI, (void *) m);
+    }
+    else {
+      /* if regfunc not found unload the module immediately */
+      xmlModuleClose(m);
+    }
 
+    xmlFree(ext_name);
+    xmlFree(regfunc_name);
     return (NULL == regfunc) ? -1 : 0;
 }
 #else
-#define xsltExtModuleRegisterDynamic(b) -1
+static int
+xsltExtModuleRegisterDynamic(const xmlChar * ATTRIBUTE_UNUSED URI)
+{
+  return -1;
+}
 #endif
 
 /************************************************************************
diff -up -urNwbB -X /home/jreed/src/lm-4.0/lm/do-not-diff libxslt-orig/libxslt/testplugin.c libxslt/libxslt/testplugin.c
--- libxslt-orig/libxslt/testplugin.c	1969-12-31 19:00:00.000000000 -0500
+++ libxslt/libxslt/testplugin.c	2005-01-13 22:06:47.000000000 -0500
@@ -0,0 +1,329 @@
+/*
+ * 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"
+
+#ifdef WITH_MODULES
+
+#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>
+#include <libxml/list.h>
+#include <libxml/xmlIO.h>
+#include "xslt.h"
+#include "xsltInternals.h"
+#include "xsltutils.h"
+#include "imports.h"
+#include "extensions.h"
+
+#define XSLT_TESTPLUGIN_URL "http://xmlsoft.org/xslt/testplugin";
+
+/************************************************************************
+ * 									*
+ * 		Test plugin module http://xmlsoft.org/xslt/testplugin			*
+ * 									*
+ ************************************************************************/
+
+/************************************************************************
+ * 									*
+ * 		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_TESTPLUGIN_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_TESTPLUGIN_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_TESTPLUGIN_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_TESTPLUGIN_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:testplugin 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");
+        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 plugin 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 plugin 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 plugin 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 plugin module : %s\n", URI);
+}
+
+/**
+ * xmlsoft_org_xslt_testplugin_init:
+ *
+ * Registers the test plugin module
+ */
+
+void
+xmlsoft_org_xslt_testplugin_init(void)
+{
+    xsltRegisterExtModuleFull((const xmlChar *) XSLT_TESTPLUGIN_URL,
+                              xsltExtInitTest, xsltExtShutdownTest,
+                              xsltExtStyleInitTest,
+                              xsltExtStyleShutdownTest);
+    xsltRegisterExtModuleFunction((const xmlChar *) "testplugin",
+                                  (const xmlChar *) XSLT_TESTPLUGIN_URL,
+                                  xsltExtFunctionTest);
+    xsltRegisterExtModuleElement((const xmlChar *) "testplugin",
+                                 (const xmlChar *) XSLT_TESTPLUGIN_URL,
+                                 xsltExtElementPreCompTest,
+                                 xsltExtElementTest);
+}
+
+#endif /*WITH_MODULES*/
diff -up -urNwbB -X /home/jreed/src/lm-4.0/lm/do-not-diff libxslt-orig/tests/Makefile.am libxslt/tests/Makefile.am
--- libxslt-orig/tests/Makefile.am	2004-03-06 10:07:42.000000000 -0500
+++ libxslt/tests/Makefile.am	2005-01-13 22:07:32.000000000 -0500
@@ -18,8 +18,20 @@ valgrind:
 	@echo '## Go get a cup of coffee it is gonna take a while ...'
 	$(MAKE) CHECKER='valgrind -q' tests
 
-full: tests docbook_tests
+full: tests docbook_tests plugin_tests
 
 docbook_tests:
 	@(cd docbook ; $(MAKE) full)
 
+if WITH_MODULES
+
+plugin_tests:
+	@echo Running the plugin tests...
+	@(cd plugins && LIBXSLT_PLUGINS_PATH=. xsltproc plugin.xsl plugin.xml)
+
+else
+
+plugin_tests:
+	@echo Skipping the plugin tests.
+
+endif
diff -up -urNwbB -X /home/jreed/src/lm-4.0/lm/do-not-diff libxslt-orig/tests/plugins/plugin.out libxslt/tests/plugins/plugin.out
--- libxslt-orig/tests/plugins/plugin.out	1969-12-31 19:00:00.000000000 -0500
+++ libxslt/tests/plugins/plugin.out	2005-01-13 22:07:04.000000000 -0500
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<!--libxslt:testplugin element test worked-->
+SUCCESS
diff -up -urNwbB -X /home/jreed/src/lm-4.0/lm/do-not-diff libxslt-orig/tests/plugins/plugin.xml libxslt/tests/plugins/plugin.xml
--- libxslt-orig/tests/plugins/plugin.xml	1969-12-31 19:00:00.000000000 -0500
+++ libxslt/tests/plugins/plugin.xml	2005-01-12 22:43:08.000000000 -0500
@@ -0,0 +1 @@
+<doc/>
diff -up -urNwbB -X /home/jreed/src/lm-4.0/lm/do-not-diff libxslt-orig/tests/plugins/plugin.xsl libxslt/tests/plugins/plugin.xsl
--- libxslt-orig/tests/plugins/plugin.xsl	1969-12-31 19:00:00.000000000 -0500
+++ libxslt/tests/plugins/plugin.xsl	2005-01-13 21:49:50.000000000 -0500
@@ -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/testplugin";
+  xmlns:test="http://xmlsoft.org/xslt/testplugin";
+  xsl:extension-element-prefixes="libxslt test"
+  version='1.0'>
+<!-- the prefix is registered twice to check single initialization -->
+<xsl:template match="/">
+<libxslt:testplugin/>
+<xsl:value-of select="libxslt:testplugin('SUCCESS')"/>
+</xsl:template>
+</xsl:stylesheet>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]