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