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