Re: [xslt] plugin patch v3 plus exslt:regexp implementation
- From: Joel Reed <joelwreed comcast net>
- To: veillard redhat com, The Gnome XSLT library mailing-list <xslt gnome org>
- Cc:
- Subject: Re: [xslt] plugin patch v3 plus exslt:regexp implementation
- Date: Thu, 13 Jan 2005 23:02:32 -0500
On Thu, Jan 13, 2005 at 10:22:59PM -0500, Joel Reed wrote:
> > 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/
uggh. sorry. the last patch incorrectly referenced xsltproc
in the test case. this one should be better.
please ignore exslt-modules.5.patch and apply exslt-modules.6.patch instead.
jr
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 **) ®func);
+ rc = xmlModuleSymbol(m, regfunc_name, (void **) ®func);
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:55:28.000000000 -0500
@@ -12,6 +12,7 @@ all:
# and (if errors are expected) in *.err
test tests:
@(cur=`pwd` ; for dir in $(SUBDIRS) ; do cd $$dir ; $(MAKE) CHECKER='$(CHECKER)' tests ; cd $$cur ; done)
+ $(MAKE) plugin_tests
valgrind:
@echo '## Running the regression tests under Valgrind'
@@ -23,3 +24,15 @@ full: tests docbook_tests
docbook_tests:
@(cd docbook ; $(MAKE) full)
+if WITH_MODULES
+
+plugin_tests:
+ @echo Running the plugin tests...
+ @(cd plugins && LIBXSLT_PLUGINS_PATH=. $(top_builddir)/../xsltproc/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]