1/*
2 * extensions.c: Implemetation of the extensions support
3 *
4 * Reference:
5 *   http://www.w3.org/TR/1999/REC-xslt-19991116
6 *
7 * See Copyright for the status of this software.
8 *
9 * daniel@veillard.com
10 */
11
12#define IN_LIBXSLT
13#include "libxslt.h"
14
15#include <string.h>
16#include <limits.h>
17
18#include <libxml/xmlmemory.h>
19#include <libxml/tree.h>
20#include <libxml/hash.h>
21#include <libxml/xmlerror.h>
22#include <libxml/parserInternals.h>
23#include <libxml/xpathInternals.h>
24#ifdef WITH_MODULES
25#include <libxml/xmlmodule.h>
26#endif
27#include <libxml/list.h>
28#include <libxml/xmlIO.h>
29#include "xslt.h"
30#include "xsltInternals.h"
31#include "xsltutils.h"
32#include "imports.h"
33#include "extensions.h"
34
35#ifdef _WIN32
36#include <stdlib.h>             /* for _MAX_PATH */
37#define PATH_MAX _MAX_PATH
38#endif
39
40#ifdef WITH_XSLT_DEBUG
41#define WITH_XSLT_DEBUG_EXTENSIONS
42#endif
43
44/************************************************************************
45 * 									*
46 * 			Private Types and Globals			*
47 * 									*
48 ************************************************************************/
49
50typedef struct _xsltExtDef xsltExtDef;
51typedef xsltExtDef *xsltExtDefPtr;
52struct _xsltExtDef {
53    struct _xsltExtDef *next;
54    xmlChar *prefix;
55    xmlChar *URI;
56    void *data;
57};
58
59typedef struct _xsltExtModule xsltExtModule;
60typedef xsltExtModule *xsltExtModulePtr;
61struct _xsltExtModule {
62    xsltExtInitFunction initFunc;
63    xsltExtShutdownFunction shutdownFunc;
64    xsltStyleExtInitFunction styleInitFunc;
65    xsltStyleExtShutdownFunction styleShutdownFunc;
66};
67
68typedef struct _xsltExtData xsltExtData;
69typedef xsltExtData *xsltExtDataPtr;
70struct _xsltExtData {
71    xsltExtModulePtr extModule;
72    void *extData;
73};
74
75typedef struct _xsltExtElement xsltExtElement;
76typedef xsltExtElement *xsltExtElementPtr;
77struct _xsltExtElement {
78    xsltPreComputeFunction precomp;
79    xsltTransformFunction transform;
80};
81
82static xmlHashTablePtr xsltExtensionsHash = NULL;
83static xmlHashTablePtr xsltFunctionsHash = NULL;
84static xmlHashTablePtr xsltElementsHash = NULL;
85static xmlHashTablePtr xsltTopLevelsHash = NULL;
86static xmlHashTablePtr xsltModuleHash = NULL;
87
88/************************************************************************
89 * 									*
90 * 			Type functions 					*
91 * 									*
92 ************************************************************************/
93
94/**
95 * xsltNewExtDef:
96 * @prefix:  the extension prefix
97 * @URI:  the namespace URI
98 *
99 * Create a new XSLT ExtDef
100 *
101 * Returns the newly allocated xsltExtDefPtr or NULL in case of error
102 */
103static xsltExtDefPtr
104xsltNewExtDef(const xmlChar * prefix, const xmlChar * URI)
105{
106    xsltExtDefPtr cur;
107
108    cur = (xsltExtDefPtr) xmlMalloc(sizeof(xsltExtDef));
109    if (cur == NULL) {
110        xsltTransformError(NULL, NULL, NULL,
111                           "xsltNewExtDef : malloc failed\n");
112        return (NULL);
113    }
114    memset(cur, 0, sizeof(xsltExtDef));
115    if (prefix != NULL)
116        cur->prefix = xmlStrdup(prefix);
117    if (URI != NULL)
118        cur->URI = xmlStrdup(URI);
119    return (cur);
120}
121
122/**
123 * xsltFreeExtDef:
124 * @extensiond:  an XSLT extension definition
125 *
126 * Free up the memory allocated by @extensiond
127 */
128static void
129xsltFreeExtDef(xsltExtDefPtr extensiond)
130{
131    if (extensiond == NULL)
132        return;
133    if (extensiond->prefix != NULL)
134        xmlFree(extensiond->prefix);
135    if (extensiond->URI != NULL)
136        xmlFree(extensiond->URI);
137    xmlFree(extensiond);
138}
139
140/**
141 * xsltFreeExtDefList:
142 * @extensiond:  an XSLT extension definition list
143 *
144 * Free up the memory allocated by all the elements of @extensiond
145 */
146static void
147xsltFreeExtDefList(xsltExtDefPtr extensiond)
148{
149    xsltExtDefPtr cur;
150
151    while (extensiond != NULL) {
152        cur = extensiond;
153        extensiond = extensiond->next;
154        xsltFreeExtDef(cur);
155    }
156}
157
158/**
159 * xsltNewExtModule:
160 * @initFunc:  the module initialization function
161 * @shutdownFunc:  the module shutdown function
162 * @styleInitFunc:  the stylesheet module data allocator function
163 * @styleShutdownFunc:  the stylesheet module data free function
164 *
165 * Create a new XSLT extension module
166 *
167 * Returns the newly allocated xsltExtModulePtr or NULL in case of error
168 */
169static xsltExtModulePtr
170xsltNewExtModule(xsltExtInitFunction initFunc,
171                 xsltExtShutdownFunction shutdownFunc,
172                 xsltStyleExtInitFunction styleInitFunc,
173                 xsltStyleExtShutdownFunction styleShutdownFunc)
174{
175    xsltExtModulePtr cur;
176
177    cur = (xsltExtModulePtr) xmlMalloc(sizeof(xsltExtModule));
178    if (cur == NULL) {
179        xsltTransformError(NULL, NULL, NULL,
180                           "xsltNewExtModule : malloc failed\n");
181        return (NULL);
182    }
183    cur->initFunc = initFunc;
184    cur->shutdownFunc = shutdownFunc;
185    cur->styleInitFunc = styleInitFunc;
186    cur->styleShutdownFunc = styleShutdownFunc;
187    return (cur);
188}
189
190/**
191 * xsltFreeExtModule:
192 * @ext:  an XSLT extension module
193 *
194 * Free up the memory allocated by @ext
195 */
196static void
197xsltFreeExtModule(xsltExtModulePtr ext)
198{
199    if (ext == NULL)
200        return;
201    xmlFree(ext);
202}
203
204/**
205 * xsltNewExtData:
206 * @extModule:  the module
207 * @extData:  the associated data
208 *
209 * Create a new XSLT extension module data wrapper
210 *
211 * Returns the newly allocated xsltExtDataPtr or NULL in case of error
212 */
213static xsltExtDataPtr
214xsltNewExtData(xsltExtModulePtr extModule, void *extData)
215{
216    xsltExtDataPtr cur;
217
218    if (extModule == NULL)
219        return (NULL);
220    cur = (xsltExtDataPtr) xmlMalloc(sizeof(xsltExtData));
221    if (cur == NULL) {
222        xsltTransformError(NULL, NULL, NULL,
223                           "xsltNewExtData : malloc failed\n");
224        return (NULL);
225    }
226    cur->extModule = extModule;
227    cur->extData = extData;
228    return (cur);
229}
230
231/**
232 * xsltFreeExtData:
233 * @ext:  an XSLT extension module data wrapper
234 *
235 * Free up the memory allocated by @ext
236 */
237static void
238xsltFreeExtData(xsltExtDataPtr ext)
239{
240    if (ext == NULL)
241        return;
242    xmlFree(ext);
243}
244
245/**
246 * xsltNewExtElement:
247 * @precomp:  the pre-computation function
248 * @transform:  the transformation function
249 *
250 * Create a new XSLT extension element
251 *
252 * Returns the newly allocated xsltExtElementPtr or NULL in case of
253 * error
254 */
255static xsltExtElementPtr
256xsltNewExtElement(xsltPreComputeFunction precomp,
257                  xsltTransformFunction transform)
258{
259    xsltExtElementPtr cur;
260
261    if (transform == NULL)
262        return (NULL);
263
264    cur = (xsltExtElementPtr) xmlMalloc(sizeof(xsltExtElement));
265    if (cur == NULL) {
266        xsltTransformError(NULL, NULL, NULL,
267                           "xsltNewExtElement : malloc failed\n");
268        return (NULL);
269    }
270    cur->precomp = precomp;
271    cur->transform = transform;
272    return (cur);
273}
274
275/**
276 * xsltFreeExtElement:
277 * @ext: an XSLT extension element
278 *
279 * Frees up the memory allocated by @ext
280 */
281static void
282xsltFreeExtElement(xsltExtElementPtr ext)
283{
284    if (ext == NULL)
285        return;
286    xmlFree(ext);
287}
288
289
290#ifdef WITH_MODULES
291typedef void (*exsltRegisterFunction) (void);
292
293#ifndef PATH_MAX
294#define PATH_MAX 4096
295#endif
296
297/**
298 * xsltExtModuleRegisterDynamic:
299 * @URI:  the function or element namespace URI
300 *
301 * Dynamically loads an extension plugin when available.
302 *
303 * The plugin name is derived from the URI by removing the
304 * initial protocol designation, e.g. "http://", then converting
305 * the characters ".", "-", "/", and "\" into "_", the removing
306 * any trailing "/", then concatenating LIBXML_MODULE_EXTENSION.
307 *
308 * Plugins are loaded from the directory specified by the
309 * environment variable LIBXSLT_PLUGINS_PATH, or if NULL,
310 * by LIBXSLT_DEFAULT_PLUGINS_PATH() which is determined at
311 * compile time.
312 *
313 * Returns 0 if successful, -1 in case of error.
314 */
315
316static int
317xsltExtModuleRegisterDynamic(const xmlChar * URI)
318{
319
320    xmlModulePtr m;
321    exsltRegisterFunction regfunc;
322    xmlChar *ext_name;
323    char module_filename[PATH_MAX];
324    const xmlChar *ext_directory = NULL;
325    const xmlChar *protocol = NULL;
326    xmlChar *i, *regfunc_name;
327    int rc, seen_before;
328
329    /* check for bad inputs */
330    if (URI == NULL)
331        return (-1);
332
333    if (NULL == xsltModuleHash) {
334        xsltModuleHash = xmlHashCreate(5);
335        if (xsltModuleHash == NULL)
336            return (-1);
337    }
338
339    /* have we attempted to register this module already? */
340    seen_before = (int) xmlHashLookup(xsltModuleHash, URI);
341    if (0 != seen_before) {
342        return (-1);
343    }
344
345    /* transform extension namespace into a module name */
346    protocol = xmlStrstr(URI, BAD_CAST "://");
347    if (protocol == NULL) {
348        ext_name = xmlStrdup(URI);
349    } else {
350        ext_name = xmlStrdup(protocol + 3);
351    }
352    if (ext_name == NULL) {
353        return (-1);
354    }
355
356    i = ext_name;
357    while ('\0' != *i) {
358        if (('/' == *i) || ('\\' == *i) || ('.' == *i) || ('-' == *i))
359            *i = '_';
360        i++;
361    }
362
363    if (*(i - 1) == '_')
364        *i = '\0';
365
366    /* determine module directory */
367    ext_directory = (xmlChar *) getenv("LIBXSLT_PLUGINS_PATH");
368
369#ifdef WITH_XSLT_DEBUG_EXTENSIONS
370    xsltGenericDebug(xsltGenericDebugContext,
371                     "LIBXSLT_PLUGINS_PATH is %s\n", ext_directory);
372#endif
373
374    if (NULL == ext_directory)
375        ext_directory = BAD_CAST LIBXSLT_DEFAULT_PLUGINS_PATH();
376    if (NULL == ext_directory)
377        return (-1);
378
379    /* build the module filename, and confirm the module exists */
380    xmlStrPrintf((xmlChar *) module_filename, sizeof(module_filename),
381                 BAD_CAST "%s/%s%s",
382                 ext_directory, ext_name, LIBXML_MODULE_EXTENSION);
383
384#ifdef WITH_XSLT_DEBUG_EXTENSIONS
385    xsltGenericDebug(xsltGenericDebugContext,
386                     "Attempting to load plugin: %s for URI: %s\n",
387                     module_filename, URI);
388#endif
389
390    if (1 != xmlCheckFilename(module_filename)) {
391
392#ifdef WITH_XSLT_DEBUG_EXTENSIONS
393	xsltGenericDebug(xsltGenericDebugContext,
394                     "xmlCheckFilename failed for plugin: %s\n", module_filename);
395#endif
396
397        xmlFree(ext_name);
398        return (-1);
399    }
400
401    /* attempt to open the module */
402    m = xmlModuleOpen(module_filename, 0);
403    if (NULL == m) {
404
405#ifdef WITH_XSLT_DEBUG_EXTENSIONS
406	xsltGenericDebug(xsltGenericDebugContext,
407                     "xmlModuleOpen failed for plugin: %s\n", module_filename);
408#endif
409
410        xmlFree(ext_name);
411        return (-1);
412    }
413
414    /* construct initialization func name */
415    regfunc_name = xmlStrdup(ext_name);
416    regfunc_name = xmlStrcat(regfunc_name, BAD_CAST "_init");
417
418    rc = xmlModuleSymbol(m, (const char *) regfunc_name, (void **) &regfunc);
419    if (0 == rc) {
420        /* call the module's init function */
421        (*regfunc) ();
422
423        /* register this module in our hash */
424        xmlHashAddEntry(xsltModuleHash, URI, (void *) m);
425    } else {
426
427#ifdef WITH_XSLT_DEBUG_EXTENSIONS
428	xsltGenericDebug(xsltGenericDebugContext,
429                     "xmlModuleSymbol failed for plugin: %s, regfunc: %s\n",
430                     module_filename, regfunc_name);
431#endif
432
433        /* if regfunc not found unload the module immediately */
434        xmlModuleClose(m);
435    }
436
437    xmlFree(ext_name);
438    xmlFree(regfunc_name);
439    return (NULL == regfunc) ? -1 : 0;
440}
441#else
442static int
443xsltExtModuleRegisterDynamic(const xmlChar * ATTRIBUTE_UNUSED URI)
444{
445  return -1;
446}
447#endif
448
449/************************************************************************
450 * 									*
451 * 		The stylesheet extension prefixes handling		*
452 * 									*
453 ************************************************************************/
454
455
456/**
457 * xsltFreeExts:
458 * @style: an XSLT stylesheet
459 *
460 * Free up the memory used by XSLT extensions in a stylesheet
461 */
462void
463xsltFreeExts(xsltStylesheetPtr style)
464{
465    if (style->nsDefs != NULL)
466        xsltFreeExtDefList((xsltExtDefPtr) style->nsDefs);
467}
468
469/**
470 * xsltRegisterExtPrefix:
471 * @style: an XSLT stylesheet
472 * @prefix: the prefix used (optional)
473 * @URI: the URI associated to the extension
474 *
475 * Registers an extension namespace
476 * This is called from xslt.c during compile-time.
477 * The given prefix is not needed.
478 * Called by:
479 *   xsltParseExtElemPrefixes() (new function)
480 *   xsltRegisterExtPrefix() (old function)
481 *
482 * Returns 0 in case of success, 1 if the @URI was already
483 *         registered as an extension namespace and
484 *         -1 in case of failure
485 */
486int
487xsltRegisterExtPrefix(xsltStylesheetPtr style,
488                      const xmlChar * prefix, const xmlChar * URI)
489{
490    xsltExtDefPtr def, ret;
491
492    if ((style == NULL) || (URI == NULL))
493        return (-1);
494
495#ifdef WITH_XSLT_DEBUG_EXTENSIONS
496    xsltGenericDebug(xsltGenericDebugContext,
497	"Registering extension namespace '%s'.\n", URI);
498#endif
499    def = (xsltExtDefPtr) style->nsDefs;
500#ifdef XSLT_REFACTORED
501    /*
502    * The extension is associated with a namespace name.
503    */
504    while (def != NULL) {
505        if (xmlStrEqual(URI, def->URI))
506            return (1);
507        def = def->next;
508    }
509#else
510    while (def != NULL) {
511        if (xmlStrEqual(prefix, def->prefix))
512            return (-1);
513        def = def->next;
514    }
515#endif
516    ret = xsltNewExtDef(prefix, URI);
517    if (ret == NULL)
518        return (-1);
519    ret->next = (xsltExtDefPtr) style->nsDefs;
520    style->nsDefs = ret;
521
522    /*
523     * check wether there is an extension module with a stylesheet
524     * initialization function.
525     */
526#ifdef XSLT_REFACTORED
527    /*
528    * Don't initialize modules based on specified namespaced via
529    * the attribute "[xsl:]extension-element-prefixes".
530    */
531#else
532    if (xsltExtensionsHash != NULL) {
533        xsltExtModulePtr module;
534
535        module = xmlHashLookup(xsltExtensionsHash, URI);
536        if (NULL == module) {
537            if (!xsltExtModuleRegisterDynamic(URI)) {
538                module = xmlHashLookup(xsltExtensionsHash, URI);
539            }
540        }
541        if (module != NULL) {
542            xsltStyleGetExtData(style, URI);
543        }
544    }
545#endif
546    return (0);
547}
548
549/************************************************************************
550 * 									*
551 * 		The extensions modules interfaces			*
552 * 									*
553 ************************************************************************/
554
555/**
556 * xsltRegisterExtFunction:
557 * @ctxt: an XSLT transformation context
558 * @name: the name of the element
559 * @URI: the URI associated to the element
560 * @function: the actual implementation which should be called
561 *
562 * Registers an extension function
563 *
564 * Returns 0 in case of success, -1 in case of failure
565 */
566int
567xsltRegisterExtFunction(xsltTransformContextPtr ctxt, const xmlChar * name,
568                        const xmlChar * URI, xmlXPathFunction function)
569{
570    if ((ctxt == NULL) || (name == NULL) ||
571        (URI == NULL) || (function == NULL))
572        return (-1);
573    if (ctxt->xpathCtxt != NULL) {
574        xmlXPathRegisterFuncNS(ctxt->xpathCtxt, name, URI, function);
575    }
576    if (ctxt->extFunctions == NULL)
577        ctxt->extFunctions = xmlHashCreate(10);
578    if (ctxt->extFunctions == NULL)
579        return (-1);
580    return (xmlHashAddEntry2
581            (ctxt->extFunctions, name, URI, XML_CAST_FPTR(function)));
582}
583
584/**
585 * xsltRegisterExtElement:
586 * @ctxt: an XSLT transformation context
587 * @name: the name of the element
588 * @URI: the URI associated to the element
589 * @function: the actual implementation which should be called
590 *
591 * Registers an extension element
592 *
593 * Returns 0 in case of success, -1 in case of failure
594 */
595int
596xsltRegisterExtElement(xsltTransformContextPtr ctxt, const xmlChar * name,
597                       const xmlChar * URI, xsltTransformFunction function)
598{
599    if ((ctxt == NULL) || (name == NULL) ||
600        (URI == NULL) || (function == NULL))
601        return (-1);
602    if (ctxt->extElements == NULL)
603        ctxt->extElements = xmlHashCreate(10);
604    if (ctxt->extElements == NULL)
605        return (-1);
606    return (xmlHashAddEntry2
607            (ctxt->extElements, name, URI, XML_CAST_FPTR(function)));
608}
609
610/**
611 * xsltFreeCtxtExts:
612 * @ctxt: an XSLT transformation context
613 *
614 * Free the XSLT extension data
615 */
616void
617xsltFreeCtxtExts(xsltTransformContextPtr ctxt)
618{
619    if (ctxt->extElements != NULL)
620        xmlHashFree(ctxt->extElements, NULL);
621    if (ctxt->extFunctions != NULL)
622        xmlHashFree(ctxt->extFunctions, NULL);
623}
624
625/**
626 * xsltStyleGetStylesheetExtData:
627 * @style: an XSLT stylesheet
628 * @URI:  the URI associated to the exension module
629 *
630 * Fires the compile-time initialization callback
631 * of an extension module and returns a container
632 * holding the user-data (retrieved via the callback).
633 *
634 * Returns the create module-data container
635 *         or NULL if such a module was not registered.
636 */
637static xsltExtDataPtr
638xsltStyleInitializeStylesheetModule(xsltStylesheetPtr style,
639				     const xmlChar * URI)
640{
641    xsltExtDataPtr dataContainer;
642    void *userData = NULL;
643    xsltExtModulePtr module;
644
645    if ((style == NULL) || (URI == NULL))
646	return(NULL);
647
648    if (xsltExtensionsHash == NULL) {
649#ifdef WITH_XSLT_DEBUG_EXTENSIONS
650	xsltGenericDebug(xsltGenericDebugContext,
651	    "Not registered extension module: %s\n", URI);
652#endif
653	return(NULL);
654    }
655
656    module = xmlHashLookup(xsltExtensionsHash, URI);
657    if (module == NULL) {
658#ifdef WITH_XSLT_DEBUG_EXTENSIONS
659	xsltGenericDebug(xsltGenericDebugContext,
660	    "Not registered extension module: %s\n", URI);
661#endif
662	return (NULL);
663    }
664    /*
665    * The specified module was registered so initialize it.
666    */
667    if (style->extInfos == NULL) {
668	style->extInfos = xmlHashCreate(10);
669	if (style->extInfos == NULL)
670	    return (NULL);
671    }
672    /*
673    * Fire the initialization callback if available.
674    */
675    if (module->styleInitFunc == NULL) {
676#ifdef WITH_XSLT_DEBUG_EXTENSIONS
677	xsltGenericDebug(xsltGenericDebugContext,
678	    "Initializing module with *no* callback: %s\n", URI);
679#endif
680    } else {
681#ifdef WITH_XSLT_DEBUG_EXTENSIONS
682	xsltGenericDebug(xsltGenericDebugContext,
683	    "Initializing module with callback: %s\n", URI);
684#endif
685	/*
686	* Fire the initialization callback.
687	*/
688	userData = module->styleInitFunc(style, URI);
689    }
690    /*
691    * Store the user-data in the context of the given stylesheet.
692    */
693    dataContainer = xsltNewExtData(module, userData);
694    if (dataContainer == NULL)
695	return (NULL);
696
697    if (xmlHashAddEntry(style->extInfos, URI,
698	(void *) dataContainer) < 0)
699    {
700	xsltTransformError(NULL, style, NULL,
701	    "Failed to register module '%s'.\n", URI);
702	style->errors++;
703	if (module->styleShutdownFunc)
704	    module->styleShutdownFunc(style, URI, userData);
705	xsltFreeExtData(dataContainer);
706	return (NULL);
707    }
708
709    return(dataContainer);
710}
711
712/**
713 * xsltStyleGetExtData:
714 * @style: an XSLT stylesheet
715 * @URI:  the URI associated to the exension module
716 *
717 * Retrieve the data associated to the extension module
718 * in this given stylesheet.
719 * Called by:
720 *   xsltRegisterExtPrefix(),
721 *   ( xsltExtElementPreCompTest(), xsltExtInitTest )
722 *
723 * Returns the pointer or NULL if not present
724 */
725void *
726xsltStyleGetExtData(xsltStylesheetPtr style, const xmlChar * URI)
727{
728    xsltExtDataPtr dataContainer = NULL;
729    xsltStylesheetPtr tmpStyle;
730
731    if ((style == NULL) || (URI == NULL) ||
732	(xsltExtensionsHash == NULL))
733	return (NULL);
734
735
736#ifdef XSLT_REFACTORED
737    /*
738    * This is intended for global storage, so only the main
739    * stylesheet will hold the data.
740    */
741    tmpStyle = style;
742    while (tmpStyle->parent != NULL)
743	tmpStyle = tmpStyle->parent;
744    if (tmpStyle->extInfos != NULL) {
745	dataContainer =
746	    (xsltExtDataPtr) xmlHashLookup(tmpStyle->extInfos, URI);
747	if (dataContainer != NULL) {
748	    /*
749	    * The module was already initialized in the context
750	    * of this stylesheet; just return the user-data that
751	    * comes with it.
752	    */
753	    return(dataContainer->extData);
754	}
755    }
756#else
757    /*
758    * Old behaviour.
759    */
760    tmpStyle = style;
761    while (tmpStyle != NULL) {
762	if (tmpStyle->extInfos != NULL) {
763	    dataContainer =
764		(xsltExtDataPtr) xmlHashLookup(tmpStyle->extInfos, URI);
765	    if (dataContainer != NULL) {
766		return(dataContainer->extData);
767	    }
768	}
769	tmpStyle = xsltNextImport(tmpStyle);
770    }
771    tmpStyle = style;
772#endif
773
774    dataContainer =
775        xsltStyleInitializeStylesheetModule(tmpStyle, URI);
776    if (dataContainer != NULL)
777	return (dataContainer->extData);
778    return(NULL);
779}
780
781/**
782 * xsltStyleGetExtDataPerStylesheetLevel:
783 * @style: an XSLT stylesheet
784 * @URI:  the URI associated to the exension module
785 *
786 * Retrieve the data associated to the extension module in this given
787 * stylesheet.
788 *
789 * Returns the pointer or NULL if not present
790 */
791void *
792xsltStyleStylesheetLevelGetExtData(xsltStylesheetPtr style,
793				   const xmlChar * URI)
794{
795    xsltExtDataPtr dataContainer = NULL;
796
797    if ((style == NULL) || (URI == NULL) ||
798	(xsltExtensionsHash == NULL))
799	return (NULL);
800
801    if (style->extInfos != NULL) {
802	dataContainer = (xsltExtDataPtr) xmlHashLookup(style->extInfos, URI);
803	/*
804	* The module was already initialized in the context
805	* of this stylesheet; just return the user-data that
806	* comes with it.
807	*/
808	if (dataContainer)
809	    return(dataContainer->extData);
810    }
811
812    dataContainer =
813        xsltStyleInitializeStylesheetModule(style, URI);
814    if (dataContainer != NULL)
815	return (dataContainer->extData);
816    return(NULL);
817}
818
819/**
820 * xsltGetExtData:
821 * @ctxt: an XSLT transformation context
822 * @URI:  the URI associated to the exension module
823 *
824 * Retrieve the data associated to the extension module in this given
825 * transformation.
826 *
827 * Returns the pointer or NULL if not present
828 */
829void *
830xsltGetExtData(xsltTransformContextPtr ctxt, const xmlChar * URI)
831{
832    xsltExtDataPtr data;
833
834    if ((ctxt == NULL) || (URI == NULL))
835        return (NULL);
836    if (ctxt->extInfos == NULL) {
837        ctxt->extInfos = xmlHashCreate(10);
838        if (ctxt->extInfos == NULL)
839            return (NULL);
840        data = NULL;
841    } else {
842        data = (xsltExtDataPtr) xmlHashLookup(ctxt->extInfos, URI);
843    }
844    if (data == NULL) {
845        void *extData;
846        xsltExtModulePtr module;
847
848        module = xmlHashLookup(xsltExtensionsHash, URI);
849        if (module == NULL) {
850#ifdef WITH_XSLT_DEBUG_EXTENSIONS
851            xsltGenericDebug(xsltGenericDebugContext,
852                             "Not registered extension module: %s\n", URI);
853#endif
854            return (NULL);
855        } else {
856            if (module->initFunc == NULL)
857                return (NULL);
858
859#ifdef WITH_XSLT_DEBUG_EXTENSIONS
860            xsltGenericDebug(xsltGenericDebugContext,
861                             "Initializing module: %s\n", URI);
862#endif
863
864            extData = module->initFunc(ctxt, URI);
865            if (extData == NULL)
866                return (NULL);
867
868            data = xsltNewExtData(module, extData);
869            if (data == NULL)
870                return (NULL);
871            if (xmlHashAddEntry(ctxt->extInfos, URI, (void *) data) < 0) {
872                xsltTransformError(ctxt, NULL, NULL,
873                                   "Failed to register module data: %s\n",
874                                   URI);
875                if (module->shutdownFunc)
876                    module->shutdownFunc(ctxt, URI, extData);
877                xsltFreeExtData(data);
878                return (NULL);
879            }
880        }
881    }
882    return (data->extData);
883}
884
885typedef struct _xsltInitExtCtxt xsltInitExtCtxt;
886struct _xsltInitExtCtxt {
887    xsltTransformContextPtr ctxt;
888    int ret;
889};
890
891/**
892 * xsltInitCtxtExt:
893 * @styleData:  the registered stylesheet data for the module
894 * @ctxt:  the XSLT transformation context + the return value
895 * @URI:  the extension URI
896 *
897 * Initializes an extension module
898 */
899static void
900xsltInitCtxtExt(xsltExtDataPtr styleData, xsltInitExtCtxt * ctxt,
901                const xmlChar * URI)
902{
903    xsltExtModulePtr module;
904    xsltExtDataPtr ctxtData;
905    void *extData;
906
907    if ((styleData == NULL) || (ctxt == NULL) || (URI == NULL) ||
908        (ctxt->ret == -1)) {
909#ifdef WITH_XSLT_DEBUG_EXTENSIONS
910        xsltGenericDebug(xsltGenericDebugContext,
911                         "xsltInitCtxtExt: NULL param or error\n");
912#endif
913        return;
914    }
915    module = styleData->extModule;
916    if ((module == NULL) || (module->initFunc == NULL)) {
917#ifdef WITH_XSLT_DEBUG_EXTENSIONS
918        xsltGenericDebug(xsltGenericDebugContext,
919                         "xsltInitCtxtExt: no module or no initFunc\n");
920#endif
921        return;
922    }
923
924    ctxtData = (xsltExtDataPtr) xmlHashLookup(ctxt->ctxt->extInfos, URI);
925    if (ctxtData != NULL) {
926#ifdef WITH_XSLT_DEBUG_EXTENSIONS
927        xsltGenericDebug(xsltGenericDebugContext,
928                         "xsltInitCtxtExt: already initialized\n");
929#endif
930        return;
931    }
932
933    extData = module->initFunc(ctxt->ctxt, URI);
934    if (extData == NULL) {
935#ifdef WITH_XSLT_DEBUG_EXTENSIONS
936        xsltGenericDebug(xsltGenericDebugContext,
937                         "xsltInitCtxtExt: no extData\n");
938#endif
939    }
940    ctxtData = xsltNewExtData(module, extData);
941    if (ctxtData == NULL) {
942        ctxt->ret = -1;
943        return;
944    }
945
946    if (ctxt->ctxt->extInfos == NULL)
947        ctxt->ctxt->extInfos = xmlHashCreate(10);
948    if (ctxt->ctxt->extInfos == NULL) {
949        ctxt->ret = -1;
950        return;
951    }
952
953    if (xmlHashAddEntry(ctxt->ctxt->extInfos, URI, ctxtData) < 0) {
954        xsltGenericError(xsltGenericErrorContext,
955                         "Failed to register module data: %s\n", URI);
956        if (module->shutdownFunc)
957            module->shutdownFunc(ctxt->ctxt, URI, extData);
958        xsltFreeExtData(ctxtData);
959        ctxt->ret = -1;
960        return;
961    }
962#ifdef WITH_XSLT_DEBUG_EXTENSIONS
963    xsltGenericDebug(xsltGenericDebugContext, "Registered module %s\n",
964                     URI);
965#endif
966    ctxt->ret++;
967}
968
969/**
970 * xsltInitCtxtExts:
971 * @ctxt: an XSLT transformation context
972 *
973 * Initialize the set of modules with registered stylesheet data
974 *
975 * Returns the number of modules initialized or -1 in case of error
976 */
977int
978xsltInitCtxtExts(xsltTransformContextPtr ctxt)
979{
980    xsltStylesheetPtr style;
981    xsltInitExtCtxt ctx;
982
983    if (ctxt == NULL)
984        return (-1);
985
986    style = ctxt->style;
987    if (style == NULL)
988        return (-1);
989
990    ctx.ctxt = ctxt;
991    ctx.ret = 0;
992
993    while (style != NULL) {
994        if (style->extInfos != NULL) {
995            xmlHashScan(style->extInfos,
996                        (xmlHashScanner) xsltInitCtxtExt, &ctx);
997            if (ctx.ret == -1)
998                return (-1);
999        }
1000        style = xsltNextImport(style);
1001    }
1002#ifdef WITH_XSLT_DEBUG_EXTENSIONS
1003    xsltGenericDebug(xsltGenericDebugContext, "Registered %d modules\n",
1004                     ctx.ret);
1005#endif
1006    return (ctx.ret);
1007}
1008
1009/**
1010 * xsltShutdownCtxtExt:
1011 * @data:  the registered data for the module
1012 * @ctxt:  the XSLT transformation context
1013 * @URI:  the extension URI
1014 *
1015 * Shutdown an extension module loaded
1016 */
1017static void
1018xsltShutdownCtxtExt(xsltExtDataPtr data, xsltTransformContextPtr ctxt,
1019                    const xmlChar * URI)
1020{
1021    xsltExtModulePtr module;
1022
1023    if ((data == NULL) || (ctxt == NULL) || (URI == NULL))
1024        return;
1025    module = data->extModule;
1026    if ((module == NULL) || (module->shutdownFunc == NULL))
1027        return;
1028
1029#ifdef WITH_XSLT_DEBUG_EXTENSIONS
1030    xsltGenericDebug(xsltGenericDebugContext,
1031                     "Shutting down module : %s\n", URI);
1032#endif
1033    module->shutdownFunc(ctxt, URI, data->extData);
1034}
1035
1036/**
1037 * xsltShutdownCtxtExts:
1038 * @ctxt: an XSLT transformation context
1039 *
1040 * Shutdown the set of modules loaded
1041 */
1042void
1043xsltShutdownCtxtExts(xsltTransformContextPtr ctxt)
1044{
1045    if (ctxt == NULL)
1046        return;
1047    if (ctxt->extInfos == NULL)
1048        return;
1049    xmlHashScan(ctxt->extInfos, (xmlHashScanner) xsltShutdownCtxtExt,
1050                ctxt);
1051    xmlHashFree(ctxt->extInfos, (xmlHashDeallocator) xsltFreeExtData);
1052    ctxt->extInfos = NULL;
1053}
1054
1055/**
1056 * xsltShutdownExt:
1057 * @data:  the registered data for the module
1058 * @ctxt:  the XSLT stylesheet
1059 * @URI:  the extension URI
1060 *
1061 * Shutdown an extension module loaded
1062 */
1063static void
1064xsltShutdownExt(xsltExtDataPtr data, xsltStylesheetPtr style,
1065                const xmlChar * URI)
1066{
1067    xsltExtModulePtr module;
1068
1069    if ((data == NULL) || (style == NULL) || (URI == NULL))
1070        return;
1071    module = data->extModule;
1072    if ((module == NULL) || (module->styleShutdownFunc == NULL))
1073        return;
1074
1075#ifdef WITH_XSLT_DEBUG_EXTENSIONS
1076    xsltGenericDebug(xsltGenericDebugContext,
1077                     "Shutting down module : %s\n", URI);
1078#endif
1079    module->styleShutdownFunc(style, URI, data->extData);
1080    /*
1081    * Don't remove the entry from the hash table here, since
1082    * this will produce segfaults - this fixes bug #340624.
1083    *
1084    * xmlHashRemoveEntry(style->extInfos, URI,
1085    *   (xmlHashDeallocator) xsltFreeExtData);
1086    */
1087}
1088
1089/**
1090 * xsltShutdownExts:
1091 * @style: an XSLT stylesheet
1092 *
1093 * Shutdown the set of modules loaded
1094 */
1095void
1096xsltShutdownExts(xsltStylesheetPtr style)
1097{
1098    if (style == NULL)
1099        return;
1100    if (style->extInfos == NULL)
1101        return;
1102    xmlHashScan(style->extInfos, (xmlHashScanner) xsltShutdownExt, style);
1103    xmlHashFree(style->extInfos, (xmlHashDeallocator) xsltFreeExtData);
1104    style->extInfos = NULL;
1105}
1106
1107/**
1108 * xsltCheckExtPrefix:
1109 * @style: the stylesheet
1110 * @URI: the namespace URI (possibly NULL)
1111 *
1112 * Check if the given prefix is one of the declared extensions.
1113 * This is intended to be called only at compile-time.
1114 * Called by:
1115 *  xsltGetInheritedNsList() (xslt.c)
1116 *  xsltParseTemplateContent (xslt.c)
1117 *
1118 * Returns 1 if this is an extension, 0 otherwise
1119 */
1120int
1121xsltCheckExtPrefix(xsltStylesheetPtr style, const xmlChar * URI)
1122{
1123#ifdef XSLT_REFACTORED
1124    if ((style == NULL) || (style->compCtxt == NULL) ||
1125	(XSLT_CCTXT(style)->inode == NULL) ||
1126	(XSLT_CCTXT(style)->inode->extElemNs == NULL))
1127        return (0);
1128    /*
1129    * Lookup the extension namespaces registered
1130    * at the current node in the stylesheet's tree.
1131    */
1132    if (XSLT_CCTXT(style)->inode->extElemNs != NULL) {
1133	int i;
1134	xsltPointerListPtr list = XSLT_CCTXT(style)->inode->extElemNs;
1135
1136	for (i = 0; i < list->number; i++) {
1137	    if (xmlStrEqual((const xmlChar *) list->items[i],
1138		URI))
1139	    {
1140		return(1);
1141	    }
1142	}
1143    }
1144#else
1145    xsltExtDefPtr cur;
1146
1147    if ((style == NULL) || (style->nsDefs == NULL))
1148        return (0);
1149    if (URI == NULL)
1150        URI = BAD_CAST "#default";
1151    cur = (xsltExtDefPtr) style->nsDefs;
1152    while (cur != NULL) {
1153	/*
1154	* NOTE: This was change to work on namespace names rather
1155	* than namespace prefixes. This fixes bug #339583.
1156	* TODO: Consider renaming the field "prefix" of xsltExtDef
1157	*  to "href".
1158	*/
1159        if (xmlStrEqual(URI, cur->prefix))
1160            return (1);
1161        cur = cur->next;
1162    }
1163#endif
1164    return (0);
1165}
1166
1167/**
1168 * xsltRegisterExtModuleFull:
1169 * @URI:  URI associated to this module
1170 * @initFunc:  the module initialization function
1171 * @shutdownFunc:  the module shutdown function
1172 * @styleInitFunc:  the module initialization function
1173 * @styleShutdownFunc:  the module shutdown function
1174 *
1175 * Register an XSLT extension module to the library.
1176 *
1177 * Returns 0 if sucessful, -1 in case of error
1178 */
1179int
1180xsltRegisterExtModuleFull(const xmlChar * URI,
1181                          xsltExtInitFunction initFunc,
1182                          xsltExtShutdownFunction shutdownFunc,
1183                          xsltStyleExtInitFunction styleInitFunc,
1184                          xsltStyleExtShutdownFunction styleShutdownFunc)
1185{
1186    int ret;
1187    xsltExtModulePtr module;
1188
1189    if ((URI == NULL) || (initFunc == NULL))
1190        return (-1);
1191    if (xsltExtensionsHash == NULL)
1192        xsltExtensionsHash = xmlHashCreate(10);
1193
1194    if (xsltExtensionsHash == NULL)
1195        return (-1);
1196
1197    module = xmlHashLookup(xsltExtensionsHash, URI);
1198    if (module != NULL) {
1199        if ((module->initFunc == initFunc) &&
1200            (module->shutdownFunc == shutdownFunc))
1201            return (0);
1202        return (-1);
1203    }
1204    module = xsltNewExtModule(initFunc, shutdownFunc,
1205                              styleInitFunc, styleShutdownFunc);
1206    if (module == NULL)
1207        return (-1);
1208    ret = xmlHashAddEntry(xsltExtensionsHash, URI, (void *) module);
1209    return (ret);
1210}
1211
1212/**
1213 * xsltRegisterExtModule:
1214 * @URI:  URI associated to this module
1215 * @initFunc:  the module initialization function
1216 * @shutdownFunc:  the module shutdown function
1217 *
1218 * Register an XSLT extension module to the library.
1219 *
1220 * Returns 0 if sucessful, -1 in case of error
1221 */
1222int
1223xsltRegisterExtModule(const xmlChar * URI,
1224                      xsltExtInitFunction initFunc,
1225                      xsltExtShutdownFunction shutdownFunc)
1226{
1227    return xsltRegisterExtModuleFull(URI, initFunc, shutdownFunc,
1228                                     NULL, NULL);
1229}
1230
1231/**
1232 * xsltUnregisterExtModule:
1233 * @URI:  URI associated to this module
1234 *
1235 * Unregister an XSLT extension module from the library.
1236 *
1237 * Returns 0 if sucessful, -1 in case of error
1238 */
1239int
1240xsltUnregisterExtModule(const xmlChar * URI)
1241{
1242    int ret;
1243
1244    if (URI == NULL)
1245        return (-1);
1246    if (xsltExtensionsHash == NULL)
1247        return (-1);
1248
1249    ret =
1250        xmlHashRemoveEntry(xsltExtensionsHash, URI,
1251                           (xmlHashDeallocator) xsltFreeExtModule);
1252    return (ret);
1253}
1254
1255/**
1256 * xsltUnregisterAllExtModules:
1257 *
1258 * Unregister all the XSLT extension module from the library.
1259 */
1260static void
1261xsltUnregisterAllExtModules(void)
1262{
1263    if (xsltExtensionsHash == NULL)
1264        return;
1265
1266    xmlHashFree(xsltExtensionsHash,
1267                (xmlHashDeallocator) xsltFreeExtModule);
1268    xsltExtensionsHash = NULL;
1269}
1270
1271/**
1272 * xsltXPathGetTransformContext:
1273 * @ctxt:  an XPath transformation context
1274 *
1275 * Provides the XSLT transformation context from the XPath transformation
1276 * context. This is useful when an XPath function in the extension module
1277 * is called by the XPath interpreter and that the XSLT context is needed
1278 * for example to retrieve the associated data pertaining to this XSLT
1279 * transformation.
1280 *
1281 * Returns the XSLT transformation context or NULL in case of error.
1282 */
1283xsltTransformContextPtr
1284xsltXPathGetTransformContext(xmlXPathParserContextPtr ctxt)
1285{
1286    if ((ctxt == NULL) || (ctxt->context == NULL))
1287        return (NULL);
1288    return (ctxt->context->extra);
1289}
1290
1291/**
1292 * xsltRegisterExtModuleFunction:
1293 * @name:  the function name
1294 * @URI:  the function namespace URI
1295 * @function:  the function callback
1296 *
1297 * Registers an extension module function.
1298 *
1299 * Returns 0 if successful, -1 in case of error.
1300 */
1301int
1302xsltRegisterExtModuleFunction(const xmlChar * name, const xmlChar * URI,
1303                              xmlXPathFunction function)
1304{
1305    if ((name == NULL) || (URI == NULL) || (function == NULL))
1306        return (-1);
1307
1308    if (xsltFunctionsHash == NULL)
1309        xsltFunctionsHash = xmlHashCreate(10);
1310    if (xsltFunctionsHash == NULL)
1311        return (-1);
1312
1313    xmlHashUpdateEntry2(xsltFunctionsHash, name, URI,
1314                        XML_CAST_FPTR(function), NULL);
1315
1316    return (0);
1317}
1318
1319/**
1320 * xsltExtModuleFunctionLookup:
1321 * @name:  the function name
1322 * @URI:  the function namespace URI
1323 *
1324 * Looks up an extension module function
1325 *
1326 * Returns the function if found, NULL otherwise.
1327 */
1328xmlXPathFunction
1329xsltExtModuleFunctionLookup(const xmlChar * name, const xmlChar * URI)
1330{
1331    xmlXPathFunction ret;
1332
1333    if ((xsltFunctionsHash == NULL) || (name == NULL) || (URI == NULL))
1334        return (NULL);
1335
1336    XML_CAST_FPTR(ret) = xmlHashLookup2(xsltFunctionsHash, name, URI);
1337
1338    /* if lookup fails, attempt a dynamic load on supported platforms */
1339    if (NULL == ret) {
1340        if (!xsltExtModuleRegisterDynamic(URI)) {
1341            XML_CAST_FPTR(ret) =
1342                xmlHashLookup2(xsltFunctionsHash, name, URI);
1343        }
1344    }
1345
1346    return ret;
1347}
1348
1349/**
1350 * xsltUnregisterExtModuleFunction:
1351 * @name:  the function name
1352 * @URI:  the function namespace URI
1353 *
1354 * Unregisters an extension module function
1355 *
1356 * Returns 0 if successful, -1 in case of error.
1357 */
1358int
1359xsltUnregisterExtModuleFunction(const xmlChar * name, const xmlChar * URI)
1360{
1361    if ((xsltFunctionsHash == NULL) || (name == NULL) || (URI == NULL))
1362        return (-1);
1363
1364    return xmlHashRemoveEntry2(xsltFunctionsHash, name, URI, NULL);
1365}
1366
1367/**
1368 * xsltUnregisterAllExtModuleFunction:
1369 *
1370 * Unregisters all extension module function
1371 */
1372static void
1373xsltUnregisterAllExtModuleFunction(void)
1374{
1375    xmlHashFree(xsltFunctionsHash, NULL);
1376    xsltFunctionsHash = NULL;
1377}
1378
1379
1380/**
1381 * xsltNewElemPreComp:
1382 * @style:  the XSLT stylesheet
1383 * @inst:  the element node
1384 * @function: the transform function
1385 *
1386 * Creates and initializes an #xsltElemPreComp
1387 *
1388 * Returns the new and initialized #xsltElemPreComp
1389 */
1390xsltElemPreCompPtr
1391xsltNewElemPreComp(xsltStylesheetPtr style, xmlNodePtr inst,
1392                   xsltTransformFunction function)
1393{
1394    xsltElemPreCompPtr cur;
1395
1396    cur = (xsltElemPreCompPtr) xmlMalloc(sizeof(xsltElemPreComp));
1397    if (cur == NULL) {
1398        xsltTransformError(NULL, style, NULL,
1399                           "xsltNewExtElement : malloc failed\n");
1400        return (NULL);
1401    }
1402    memset(cur, 0, sizeof(xsltElemPreComp));
1403
1404    xsltInitElemPreComp(cur, style, inst, function,
1405                        (xsltElemPreCompDeallocator) xmlFree);
1406
1407    return (cur);
1408}
1409
1410/**
1411 * xsltInitElemPreComp:
1412 * @comp:  an #xsltElemPreComp (or generally a derived structure)
1413 * @style:  the XSLT stylesheet
1414 * @inst:  the element node
1415 * @function:  the transform function
1416 * @freeFunc:  the @comp deallocator
1417 *
1418 * Initializes an existing #xsltElemPreComp structure. This is usefull
1419 * when extending an #xsltElemPreComp to store precomputed data.
1420 * This function MUST be called on any extension element precomputed
1421 * data struct.
1422 */
1423void
1424xsltInitElemPreComp(xsltElemPreCompPtr comp, xsltStylesheetPtr style,
1425                    xmlNodePtr inst, xsltTransformFunction function,
1426                    xsltElemPreCompDeallocator freeFunc)
1427{
1428    comp->type = XSLT_FUNC_EXTENSION;
1429    comp->func = function;
1430    comp->inst = inst;
1431    comp->free = freeFunc;
1432
1433    comp->next = style->preComps;
1434    style->preComps = comp;
1435}
1436
1437/**
1438 * xsltPreComputeExtModuleElement:
1439 * @style:  the stylesheet
1440 * @inst:  the element node
1441 *
1442 * Precomputes an extension module element
1443 *
1444 * Returns the precomputed data
1445 */
1446xsltElemPreCompPtr
1447xsltPreComputeExtModuleElement(xsltStylesheetPtr style, xmlNodePtr inst)
1448{
1449    xsltExtElementPtr ext;
1450    xsltElemPreCompPtr comp = NULL;
1451
1452    if ((style == NULL) || (inst == NULL) ||
1453        (inst->type != XML_ELEMENT_NODE) || (inst->ns == NULL))
1454        return (NULL);
1455
1456    ext = (xsltExtElementPtr)
1457        xmlHashLookup2(xsltElementsHash, inst->name, inst->ns->href);
1458    /*
1459    * EXT TODO: Now what?
1460    */
1461    if (ext == NULL)
1462        return (NULL);
1463
1464    if (ext->precomp != NULL) {
1465	/*
1466	* REVISIT TODO: Check if the text below is correct.
1467	* This will return a xsltElemPreComp structure or NULL.
1468	* 1) If the the author of the extension needs a
1469	*  custom structure to hold the specific values of
1470	*  this extension, he will derive a structure based on
1471	*  xsltElemPreComp; thus we obviously *cannot* refactor
1472	*  the xsltElemPreComp structure, since all already derived
1473	*  user-defined strucures will break.
1474	*  Example: For the extension xsl:document,
1475	*   in xsltDocumentComp() (preproc.c), the structure
1476	*   xsltStyleItemDocument is allocated, filled with
1477	*   specific values and returned.
1478	* 2) If the author needs no values to be stored in
1479	*  this structure, then he'll return NULL;
1480	*/
1481        comp = ext->precomp(style, inst, ext->transform);
1482    }
1483    if (comp == NULL) {
1484	/*
1485	* Default creation of a xsltElemPreComp structure, if
1486	* the author of this extension did not create a custom
1487	* structure.
1488	*/
1489        comp = xsltNewElemPreComp(style, inst, ext->transform);
1490    }
1491
1492    return (comp);
1493}
1494
1495/**
1496 * xsltRegisterExtModuleElement:
1497 * @name:  the element name
1498 * @URI:  the element namespace URI
1499 * @precomp:  the pre-computation callback
1500 * @transform:  the transformation callback
1501 *
1502 * Registers an extension module element.
1503 *
1504 * Returns 0 if successful, -1 in case of error.
1505 */
1506int
1507xsltRegisterExtModuleElement(const xmlChar * name, const xmlChar * URI,
1508                             xsltPreComputeFunction precomp,
1509                             xsltTransformFunction transform)
1510{
1511    xsltExtElementPtr ext;
1512
1513    if ((name == NULL) || (URI == NULL) || (transform == NULL))
1514        return (-1);
1515
1516    if (xsltElementsHash == NULL)
1517        xsltElementsHash = xmlHashCreate(10);
1518    if (xsltElementsHash == NULL)
1519        return (-1);
1520
1521    ext = xsltNewExtElement(precomp, transform);
1522    if (ext == NULL)
1523        return (-1);
1524
1525    xmlHashUpdateEntry2(xsltElementsHash, name, URI, (void *) ext,
1526                        (xmlHashDeallocator) xsltFreeExtElement);
1527
1528    return (0);
1529}
1530
1531/**
1532 * xsltExtElementLookup:
1533 * @ctxt:  an XSLT process context
1534 * @name:  the element name
1535 * @URI:  the element namespace URI
1536 *
1537 * Looks up an extension element. @ctxt can be NULL to search only in
1538 * module elements.
1539 *
1540 * Returns the element callback or NULL if not found
1541 */
1542xsltTransformFunction
1543xsltExtElementLookup(xsltTransformContextPtr ctxt,
1544                     const xmlChar * name, const xmlChar * URI)
1545{
1546    xsltTransformFunction ret;
1547
1548    if ((name == NULL) || (URI == NULL))
1549        return (NULL);
1550
1551    if ((ctxt != NULL) && (ctxt->extElements != NULL)) {
1552        XML_CAST_FPTR(ret) = xmlHashLookup2(ctxt->extElements, name, URI);
1553        if (ret != NULL)
1554            return (ret);
1555    }
1556    return xsltExtModuleElementLookup(name, URI);
1557}
1558
1559/**
1560 * xsltExtModuleElementLookup:
1561 * @name:  the element name
1562 * @URI:  the element namespace URI
1563 *
1564 * Looks up an extension module element
1565 *
1566 * Returns the callback function if found, NULL otherwise.
1567 */
1568xsltTransformFunction
1569xsltExtModuleElementLookup(const xmlChar * name, const xmlChar * URI)
1570{
1571    xsltExtElementPtr ext;
1572
1573    if ((xsltElementsHash == NULL) || (name == NULL) || (URI == NULL))
1574        return (NULL);
1575
1576    ext = (xsltExtElementPtr) xmlHashLookup2(xsltElementsHash, name, URI);
1577
1578    /* if function lookup fails, attempt a dynamic load on supported platforms */
1579    ext = (xsltExtElementPtr) xmlHashLookup2(xsltElementsHash, name, URI);
1580    if (NULL == ext) {
1581        if (!xsltExtModuleRegisterDynamic(URI)) {
1582            ext = (xsltExtElementPtr)
1583	          xmlHashLookup2(xsltElementsHash, name, URI);
1584        }
1585    }
1586
1587    if (ext == NULL)
1588        return (NULL);
1589    return (ext->transform);
1590}
1591
1592/**
1593 * xsltExtModuleElementPreComputeLookup:
1594 * @name:  the element name
1595 * @URI:  the element namespace URI
1596 *
1597 * Looks up an extension module element pre-computation function
1598 *
1599 * Returns the callback function if found, NULL otherwise.
1600 */
1601xsltPreComputeFunction
1602xsltExtModuleElementPreComputeLookup(const xmlChar * name,
1603                                     const xmlChar * URI)
1604{
1605    xsltExtElementPtr ext;
1606
1607    if ((xsltElementsHash == NULL) || (name == NULL) || (URI == NULL))
1608        return (NULL);
1609
1610    ext = (xsltExtElementPtr) xmlHashLookup2(xsltElementsHash, name, URI);
1611
1612    if (ext == NULL) {
1613        if (!xsltExtModuleRegisterDynamic(URI)) {
1614            ext = (xsltExtElementPtr)
1615	          xmlHashLookup2(xsltElementsHash, name, URI);
1616        }
1617    }
1618
1619    if (ext == NULL)
1620        return (NULL);
1621    return (ext->precomp);
1622}
1623
1624/**
1625 * xsltUnregisterExtModuleElement:
1626 * @name:  the element name
1627 * @URI:  the element namespace URI
1628 *
1629 * Unregisters an extension module element
1630 *
1631 * Returns 0 if successful, -1 in case of error.
1632 */
1633int
1634xsltUnregisterExtModuleElement(const xmlChar * name, const xmlChar * URI)
1635{
1636    if ((xsltElementsHash == NULL) || (name == NULL) || (URI == NULL))
1637        return (-1);
1638
1639    return xmlHashRemoveEntry2(xsltElementsHash, name, URI,
1640                               (xmlHashDeallocator) xsltFreeExtElement);
1641}
1642
1643/**
1644 * xsltUnregisterAllExtModuleElement:
1645 *
1646 * Unregisters all extension module element
1647 */
1648static void
1649xsltUnregisterAllExtModuleElement(void)
1650{
1651    xmlHashFree(xsltElementsHash, (xmlHashDeallocator) xsltFreeExtElement);
1652    xsltElementsHash = NULL;
1653}
1654
1655/**
1656 * xsltRegisterExtModuleTopLevel:
1657 * @name:  the top-level element name
1658 * @URI:  the top-level element namespace URI
1659 * @function:  the top-level element callback
1660 *
1661 * Registers an extension module top-level element.
1662 *
1663 * Returns 0 if successful, -1 in case of error.
1664 */
1665int
1666xsltRegisterExtModuleTopLevel(const xmlChar * name, const xmlChar * URI,
1667                              xsltTopLevelFunction function)
1668{
1669    if ((name == NULL) || (URI == NULL) || (function == NULL))
1670        return (-1);
1671
1672    if (xsltTopLevelsHash == NULL)
1673        xsltTopLevelsHash = xmlHashCreate(10);
1674    if (xsltTopLevelsHash == NULL)
1675        return (-1);
1676
1677    xmlHashUpdateEntry2(xsltTopLevelsHash, name, URI,
1678                        XML_CAST_FPTR(function), NULL);
1679
1680    return (0);
1681}
1682
1683/**
1684 * xsltExtModuleTopLevelLookup:
1685 * @name:  the top-level element name
1686 * @URI:  the top-level element namespace URI
1687 *
1688 * Looks up an extension module top-level element
1689 *
1690 * Returns the callback function if found, NULL otherwise.
1691 */
1692xsltTopLevelFunction
1693xsltExtModuleTopLevelLookup(const xmlChar * name, const xmlChar * URI)
1694{
1695    xsltTopLevelFunction ret;
1696
1697    if ((xsltTopLevelsHash == NULL) || (name == NULL) || (URI == NULL))
1698        return (NULL);
1699
1700    XML_CAST_FPTR(ret) = xmlHashLookup2(xsltTopLevelsHash, name, URI);
1701
1702    /* if lookup fails, attempt a dynamic load on supported platforms */
1703    if (NULL == ret) {
1704        if (!xsltExtModuleRegisterDynamic(URI)) {
1705            XML_CAST_FPTR(ret) = xmlHashLookup2(xsltTopLevelsHash, name, URI);
1706        }
1707    }
1708
1709    return (ret);
1710}
1711
1712/**
1713 * xsltUnregisterExtModuleTopLevel:
1714 * @name:  the top-level element name
1715 * @URI:  the top-level element namespace URI
1716 *
1717 * Unregisters an extension module top-level element
1718 *
1719 * Returns 0 if successful, -1 in case of error.
1720 */
1721int
1722xsltUnregisterExtModuleTopLevel(const xmlChar * name, const xmlChar * URI)
1723{
1724    if ((xsltTopLevelsHash == NULL) || (name == NULL) || (URI == NULL))
1725        return (-1);
1726
1727    return xmlHashRemoveEntry2(xsltTopLevelsHash, name, URI, NULL);
1728}
1729
1730/**
1731 * xsltUnregisterAllExtModuleTopLevel:
1732 *
1733 * Unregisters all extension module function
1734 */
1735static void
1736xsltUnregisterAllExtModuleTopLevel(void)
1737{
1738    xmlHashFree(xsltTopLevelsHash, NULL);
1739    xsltTopLevelsHash = NULL;
1740}
1741
1742/**
1743 * xsltGetExtInfo:
1744 * @style:  pointer to a stylesheet
1745 * @URI:    the namespace URI desired
1746 *
1747 * looks up URI in extInfos of the stylesheet
1748 *
1749 * returns a pointer to the hash table if found, else NULL
1750 */
1751xmlHashTablePtr
1752xsltGetExtInfo(xsltStylesheetPtr style, const xmlChar * URI)
1753{
1754    xsltExtDataPtr data;
1755
1756    /*
1757    * TODO: Why do we have a return type of xmlHashTablePtr?
1758    *   Is the user-allocated data for extension modules expected
1759    *   to be a xmlHashTablePtr only? Or is this intended for
1760    *   the EXSLT module only?
1761    */
1762
1763    if (style != NULL && style->extInfos != NULL) {
1764        data = xmlHashLookup(style->extInfos, URI);
1765        if (data != NULL && data->extData != NULL)
1766            return data->extData;
1767    }
1768    return NULL;
1769}
1770
1771/************************************************************************
1772 * 									*
1773 * 		Test module http://xmlsoft.org/XSLT/			*
1774 * 									*
1775 ************************************************************************/
1776
1777/************************************************************************
1778 * 									*
1779 * 		Test of the extension module API			*
1780 * 									*
1781 ************************************************************************/
1782
1783static xmlChar *testData = NULL;
1784static xmlChar *testStyleData = NULL;
1785
1786/**
1787 * xsltExtFunctionTest:
1788 * @ctxt:  the XPath Parser context
1789 * @nargs:  the number of arguments
1790 *
1791 * function libxslt:test() for testing the extensions support.
1792 */
1793static void
1794xsltExtFunctionTest(xmlXPathParserContextPtr ctxt,
1795                    int nargs ATTRIBUTE_UNUSED)
1796{
1797    xsltTransformContextPtr tctxt;
1798    void *data = NULL;
1799
1800    tctxt = xsltXPathGetTransformContext(ctxt);
1801
1802    if (testData == NULL) {
1803        xsltGenericDebug(xsltGenericDebugContext,
1804                         "xsltExtFunctionTest: not initialized,"
1805                         " calling xsltGetExtData\n");
1806        data = xsltGetExtData(tctxt, (const xmlChar *) XSLT_DEFAULT_URL);
1807        if (data == NULL) {
1808            xsltTransformError(tctxt, NULL, NULL,
1809                               "xsltExtElementTest: not initialized\n");
1810            return;
1811        }
1812    }
1813    if (tctxt == NULL) {
1814        xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
1815                           "xsltExtFunctionTest: failed to get the transformation context\n");
1816        return;
1817    }
1818    if (data == NULL)
1819        data = xsltGetExtData(tctxt, (const xmlChar *) XSLT_DEFAULT_URL);
1820    if (data == NULL) {
1821        xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
1822                           "xsltExtFunctionTest: failed to get module data\n");
1823        return;
1824    }
1825    if (data != testData) {
1826        xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
1827                           "xsltExtFunctionTest: got wrong module data\n");
1828        return;
1829    }
1830#ifdef WITH_XSLT_DEBUG_FUNCTION
1831    xsltGenericDebug(xsltGenericDebugContext,
1832                     "libxslt:test() called with %d args\n", nargs);
1833#endif
1834}
1835
1836/**
1837 * xsltExtElementPreCompTest:
1838 * @style:  the stylesheet
1839 * @inst:  the instruction in the stylesheet
1840 *
1841 * Process a libxslt:test node
1842 */
1843static xsltElemPreCompPtr
1844xsltExtElementPreCompTest(xsltStylesheetPtr style, xmlNodePtr inst,
1845                          xsltTransformFunction function)
1846{
1847    xsltElemPreCompPtr ret;
1848
1849    if (style == NULL) {
1850        xsltTransformError(NULL, NULL, inst,
1851                           "xsltExtElementTest: no transformation context\n");
1852        return (NULL);
1853    }
1854    if (testStyleData == NULL) {
1855        xsltGenericDebug(xsltGenericDebugContext,
1856                         "xsltExtElementPreCompTest: not initialized,"
1857                         " calling xsltStyleGetExtData\n");
1858        xsltStyleGetExtData(style, (const xmlChar *) XSLT_DEFAULT_URL);
1859        if (testStyleData == NULL) {
1860            xsltTransformError(NULL, style, inst,
1861                               "xsltExtElementPreCompTest: not initialized\n");
1862            if (style != NULL)
1863                style->errors++;
1864            return (NULL);
1865        }
1866    }
1867    if (inst == NULL) {
1868        xsltTransformError(NULL, style, inst,
1869                           "xsltExtElementPreCompTest: no instruction\n");
1870        if (style != NULL)
1871            style->errors++;
1872        return (NULL);
1873    }
1874    ret = xsltNewElemPreComp(style, inst, function);
1875    return (ret);
1876}
1877
1878/**
1879 * xsltExtElementTest:
1880 * @ctxt:  an XSLT processing context
1881 * @node:  The current node
1882 * @inst:  the instruction in the stylesheet
1883 * @comp:  precomputed informations
1884 *
1885 * Process a libxslt:test node
1886 */
1887static void
1888xsltExtElementTest(xsltTransformContextPtr ctxt, xmlNodePtr node,
1889                   xmlNodePtr inst,
1890                   xsltElemPreCompPtr comp ATTRIBUTE_UNUSED)
1891{
1892    xmlNodePtr commentNode;
1893
1894    if (testData == NULL) {
1895        xsltGenericDebug(xsltGenericDebugContext,
1896                         "xsltExtElementTest: not initialized,"
1897                         " calling xsltGetExtData\n");
1898        xsltGetExtData(ctxt, (const xmlChar *) XSLT_DEFAULT_URL);
1899        if (testData == NULL) {
1900            xsltTransformError(ctxt, NULL, inst,
1901                               "xsltExtElementTest: not initialized\n");
1902            return;
1903        }
1904    }
1905    if (ctxt == NULL) {
1906        xsltTransformError(ctxt, NULL, inst,
1907                           "xsltExtElementTest: no transformation context\n");
1908        return;
1909    }
1910    if (node == NULL) {
1911        xsltTransformError(ctxt, NULL, inst,
1912                           "xsltExtElementTest: no current node\n");
1913        return;
1914    }
1915    if (inst == NULL) {
1916        xsltTransformError(ctxt, NULL, inst,
1917                           "xsltExtElementTest: no instruction\n");
1918        return;
1919    }
1920    if (ctxt->insert == NULL) {
1921        xsltTransformError(ctxt, NULL, inst,
1922                           "xsltExtElementTest: no insertion point\n");
1923        return;
1924    }
1925    commentNode = xmlNewComment((const xmlChar *)
1926                                "libxslt:test element test worked");
1927    xmlAddChild(ctxt->insert, commentNode);
1928}
1929
1930/**
1931 * xsltExtInitTest:
1932 * @ctxt:  an XSLT transformation context
1933 * @URI:  the namespace URI for the extension
1934 *
1935 * A function called at initialization time of an XSLT extension module
1936 *
1937 * Returns a pointer to the module specific data for this transformation
1938 */
1939static void *
1940xsltExtInitTest(xsltTransformContextPtr ctxt, const xmlChar * URI)
1941{
1942    if (testStyleData == NULL) {
1943        xsltGenericDebug(xsltGenericErrorContext,
1944                         "xsltExtInitTest: not initialized,"
1945                         " calling xsltStyleGetExtData\n");
1946        xsltStyleGetExtData(ctxt->style, URI);
1947        if (testStyleData == NULL) {
1948            xsltTransformError(ctxt, NULL, NULL,
1949                               "xsltExtInitTest: not initialized\n");
1950            return (NULL);
1951        }
1952    }
1953    if (testData != NULL) {
1954        xsltTransformError(ctxt, NULL, NULL,
1955                           "xsltExtInitTest: already initialized\n");
1956        return (NULL);
1957    }
1958    testData = (void *) "test data";
1959    xsltGenericDebug(xsltGenericDebugContext,
1960                     "Registered test module : %s\n", URI);
1961    return (testData);
1962}
1963
1964
1965/**
1966 * xsltExtShutdownTest:
1967 * @ctxt:  an XSLT transformation context
1968 * @URI:  the namespace URI for the extension
1969 * @data:  the data associated to this module
1970 *
1971 * A function called at shutdown time of an XSLT extension module
1972 */
1973static void
1974xsltExtShutdownTest(xsltTransformContextPtr ctxt,
1975                    const xmlChar * URI, void *data)
1976{
1977    if (testData == NULL) {
1978        xsltTransformError(ctxt, NULL, NULL,
1979                           "xsltExtShutdownTest: not initialized\n");
1980        return;
1981    }
1982    if (data != testData) {
1983        xsltTransformError(ctxt, NULL, NULL,
1984                           "xsltExtShutdownTest: wrong data\n");
1985    }
1986    testData = NULL;
1987    xsltGenericDebug(xsltGenericDebugContext,
1988                     "Unregistered test module : %s\n", URI);
1989}
1990
1991/**
1992 * xsltExtStyleInitTest:
1993 * @style:  an XSLT stylesheet
1994 * @URI:  the namespace URI for the extension
1995 *
1996 * A function called at initialization time of an XSLT extension module
1997 *
1998 * Returns a pointer to the module specific data for this transformation
1999 */
2000static void *
2001xsltExtStyleInitTest(xsltStylesheetPtr style ATTRIBUTE_UNUSED,
2002                     const xmlChar * URI)
2003{
2004    if (testStyleData != NULL) {
2005        xsltTransformError(NULL, NULL, NULL,
2006                           "xsltExtInitTest: already initialized\n");
2007        return (NULL);
2008    }
2009    testStyleData = (void *) "test data";
2010    xsltGenericDebug(xsltGenericDebugContext,
2011                     "Registered test module : %s\n", URI);
2012    return (testStyleData);
2013}
2014
2015
2016/**
2017 * xsltExtStyleShutdownTest:
2018 * @style:  an XSLT stylesheet
2019 * @URI:  the namespace URI for the extension
2020 * @data:  the data associated to this module
2021 *
2022 * A function called at shutdown time of an XSLT extension module
2023 */
2024static void
2025xsltExtStyleShutdownTest(xsltStylesheetPtr style ATTRIBUTE_UNUSED,
2026                         const xmlChar * URI, void *data)
2027{
2028    if (testStyleData == NULL) {
2029        xsltGenericError(xsltGenericErrorContext,
2030                         "xsltExtShutdownTest: not initialized\n");
2031        return;
2032    }
2033    if (data != testStyleData) {
2034        xsltTransformError(NULL, NULL, NULL,
2035                           "xsltExtShutdownTest: wrong data\n");
2036    }
2037    testStyleData = NULL;
2038    xsltGenericDebug(xsltGenericDebugContext,
2039                     "Unregistered test module : %s\n", URI);
2040}
2041
2042/**
2043 * xsltRegisterTestModule:
2044 *
2045 * Registers the test module
2046 */
2047void
2048xsltRegisterTestModule(void)
2049{
2050    xsltRegisterExtModuleFull((const xmlChar *) XSLT_DEFAULT_URL,
2051                              xsltExtInitTest, xsltExtShutdownTest,
2052                              xsltExtStyleInitTest,
2053                              xsltExtStyleShutdownTest);
2054    xsltRegisterExtModuleFunction((const xmlChar *) "test",
2055                                  (const xmlChar *) XSLT_DEFAULT_URL,
2056                                  xsltExtFunctionTest);
2057    xsltRegisterExtModuleElement((const xmlChar *) "test",
2058                                 (const xmlChar *) XSLT_DEFAULT_URL,
2059                                 xsltExtElementPreCompTest,
2060                                 xsltExtElementTest);
2061}
2062
2063static void
2064xsltHashScannerModuleFree(void *payload, void *data ATTRIBUTE_UNUSED,
2065                          xmlChar * name ATTRIBUTE_UNUSED)
2066{
2067#ifdef WITH_MODULES
2068    xmlModuleClose(payload);
2069#endif
2070}
2071
2072/**
2073 * xsltCleanupGlobals:
2074 *
2075 * Unregister all global variables set up by the XSLT library
2076 */
2077void
2078xsltCleanupGlobals(void)
2079{
2080    xsltUnregisterAllExtModules();
2081    xsltUnregisterAllExtModuleFunction();
2082    xsltUnregisterAllExtModuleElement();
2083    xsltUnregisterAllExtModuleTopLevel();
2084
2085    /* cleanup dynamic module hash */
2086    if (NULL != xsltModuleHash) {
2087        xmlHashScan(xsltModuleHash, xsltHashScannerModuleFree, 0);
2088        xmlHashFree(xsltModuleHash, NULL);
2089        xsltModuleHash = NULL;
2090    }
2091
2092    xsltUninit();
2093}
2094
2095static void
2096xsltDebugDumpExtensionsCallback(void *function ATTRIBUTE_UNUSED,
2097                                FILE * output, const xmlChar * name,
2098                                const xmlChar * URI,
2099                                const xmlChar * not_used ATTRIBUTE_UNUSED)
2100{
2101    if (!name || !URI)
2102        return;
2103    fprintf(output, "{%s}%s\n", URI, name);
2104}
2105
2106static void
2107xsltDebugDumpExtModulesCallback(void *function ATTRIBUTE_UNUSED,
2108                                FILE * output, const xmlChar * URI,
2109                                const xmlChar * not_used ATTRIBUTE_UNUSED,
2110                                const xmlChar * not_used2 ATTRIBUTE_UNUSED)
2111{
2112    if (!URI)
2113        return;
2114    fprintf(output, "%s\n", URI);
2115}
2116
2117/**
2118 * xsltDebugDumpExtensions:
2119 * @output:  the FILE * for the output, if NULL stdout is used
2120 *
2121 * Dumps a list of the registered XSLT extension functions and elements
2122 */
2123void
2124xsltDebugDumpExtensions(FILE * output)
2125{
2126    if (output == NULL)
2127        output = stdout;
2128    fprintf(output,
2129            "Registered XSLT Extensions\n--------------------------\n");
2130    if (!xsltFunctionsHash)
2131        fprintf(output, "No registered extension functions\n");
2132    else {
2133        fprintf(output, "Registered Extension Functions:\n");
2134        xmlHashScanFull(xsltFunctionsHash,
2135                        (xmlHashScannerFull)
2136                        xsltDebugDumpExtensionsCallback, output);
2137    }
2138    if (!xsltElementsHash)
2139        fprintf(output, "\nNo registered extension elements\n");
2140    else {
2141        fprintf(output, "\nRegistered Extension Elements:\n");
2142        xmlHashScanFull(xsltElementsHash,
2143                        (xmlHashScannerFull)
2144                        xsltDebugDumpExtensionsCallback, output);
2145    }
2146    if (!xsltExtensionsHash)
2147        fprintf(output, "\nNo registered extension modules\n");
2148    else {
2149        fprintf(output, "\nRegistered Extension Modules:\n");
2150        xmlHashScanFull(xsltExtensionsHash,
2151                        (xmlHashScannerFull)
2152                        xsltDebugDumpExtModulesCallback, output);
2153    }
2154
2155}
2156