From ffc52efd1d107602e9b4ef8b75681ecf412becdb Mon Sep 17 00:00:00 2001 From: Daniel Veillard Date: Wed, 4 Jul 2001 13:22:40 +0000 Subject: [PATCH] - 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 Daniel --- ChangeLog | 11 + configure.in | 1 + libxslt/extensions.c | 388 ++++++++++++++++++++++++++++++++++- libxslt/extensions.h | 61 +++++- libxslt/extra.c | 106 ++++++---- libxslt/extra.h | 9 + libxslt/functions.c | 211 +++++++++++++++++-- libxslt/transform.c | 3 +- libxslt/xsltInternals.h | 1 + libxslt/xsltproc.c | 2 + tests/Makefile.am | 2 +- tests/extensions/Makefile.am | 29 +++ tests/extensions/module.out | 3 + tests/extensions/module.xml | 1 + tests/extensions/module.xsl | 12 ++ 15 files changed, 769 insertions(+), 71 deletions(-) create mode 100644 tests/extensions/Makefile.am create mode 100644 tests/extensions/module.out create mode 100644 tests/extensions/module.xml create mode 100644 tests/extensions/module.xsl diff --git a/ChangeLog b/ChangeLog index 7847d2a6..79324c9e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +Wed Jul 4 15:15:50 CEST 2001 Daniel Veillard + + * 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 * libxslt/pattern.c libxslt/preproc.c libxslt/transform.c diff --git a/configure.in b/configure.in index 5dfeaeff..6250df2b 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/libxslt/extensions.c b/libxslt/extensions.c index 92a6c18d..8894a180 100644 --- a/libxslt/extensions.c +++ b/libxslt/extensions.c @@ -18,15 +18,23 @@ #include #include #include +#include #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)); @@ -214,6 +326,166 @@ xsltFreeCtxtExts(xsltTransformContextPtr ctxt) { xmlHashFree(ctxt->extFunctions, NULL); } +/** + * 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 @@ -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); +} + + diff --git a/libxslt/extensions.h b/libxslt/extensions.h index a4f053e0..c3241ca9 100644 --- a/libxslt/extensions.h +++ b/libxslt/extensions.h @@ -16,11 +16,51 @@ 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 diff --git a/libxslt/extra.c b/libxslt/extra.c index 4f1786be..223e3d9f 100644 --- a/libxslt/extra.c +++ b/libxslt/extra.c @@ -46,53 +46,55 @@ * * 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; + } + } } @@ -130,6 +132,32 @@ xsltFunctionNodeSet(xmlXPathParserContextPtr ctxt, int nargs){ ctxt->value->boolval = 1; } +/** + * 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", diff --git a/libxslt/extra.h b/libxslt/extra.h index df242284..5bc84cdc 100644 --- a/libxslt/extra.h +++ b/libxslt/extra.h @@ -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); diff --git a/libxslt/functions.c b/libxslt/functions.c index 4b430369..bba428f5 100644 --- a/libxslt/functions.c +++ b/libxslt/functions.c @@ -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); } diff --git a/libxslt/transform.c b/libxslt/transform.c index 81cc8910..c6b20b3f 100644 --- a/libxslt/transform.c +++ b/libxslt/transform.c @@ -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 */ diff --git a/libxslt/xsltInternals.h b/libxslt/xsltInternals.h index 5eddd7f2..43a1a68a 100644 --- a/libxslt/xsltInternals.h +++ b/libxslt/xsltInternals.h @@ -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 */ diff --git a/libxslt/xsltproc.c b/libxslt/xsltproc.c index e979f61e..1d0e5e9a 100644 --- a/libxslt/xsltproc.c +++ b/libxslt/xsltproc.c @@ -36,6 +36,7 @@ #include #include #include +#include #ifdef WIN32 #ifdef _MSC_VER @@ -442,6 +443,7 @@ main(int argc, char **argv) } xsltFreeStylesheet(cur); } + xsltUnregisterAllExtModules(); xmlCleanupParser(); xmlMemoryDump(); return (0); diff --git a/tests/Makefile.am b/tests/Makefile.am index e93769de..bb6ac237 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 00000000..2bf71a93 --- /dev/null +++ b/tests/extensions/Makefile.am @@ -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 index 00000000..fa2cdd7f --- /dev/null +++ b/tests/extensions/module.out @@ -0,0 +1,3 @@ + + +SUCCESS diff --git a/tests/extensions/module.xml b/tests/extensions/module.xml new file mode 100644 index 00000000..69d62f2c --- /dev/null +++ b/tests/extensions/module.xml @@ -0,0 +1 @@ + diff --git a/tests/extensions/module.xsl b/tests/extensions/module.xsl new file mode 100644 index 00000000..7f69c18f --- /dev/null +++ b/tests/extensions/module.xsl @@ -0,0 +1,12 @@ + + + + + + + +