set eol-style:native
[reactos.git] / reactos / lib / libxml2 / catalog.c
index 1c7430b..202c288 100644 (file)
-/**\r
- * catalog.c: set of generic Catalog related routines \r
- *\r
- * Reference:  SGML Open Technical Resolution TR9401:1997.\r
- *             http://www.jclark.com/sp/catalog.htm\r
- *\r
- *             XML Catalogs Working Draft 06 August 2001\r
- *             http://www.oasis-open.org/committees/entity/spec-2001-08-06.html\r
- *\r
- * See Copyright for the status of this software.\r
- *\r
- * Daniel.Veillard@imag.fr\r
- */\r
-\r
-#define IN_LIBXML\r
-#include "libxml.h"\r
-\r
-#ifdef LIBXML_CATALOG_ENABLED\r
-#ifdef HAVE_SYS_TYPES_H\r
-#include <sys/types.h>\r
-#endif\r
-#ifdef HAVE_SYS_STAT_H\r
-#include <sys/stat.h>\r
-#endif\r
-#ifdef HAVE_UNISTD_H\r
-#include <unistd.h>\r
-#endif\r
-#ifdef HAVE_FCNTL_H\r
-#include <fcntl.h>\r
-#endif\r
-#ifdef HAVE_STDLIB_H\r
-#include <stdlib.h>\r
-#endif\r
-#include <string.h>\r
-#include <libxml/xmlmemory.h>\r
-#include <libxml/hash.h>\r
-#include <libxml/uri.h>\r
-#include <libxml/parserInternals.h>\r
-#include <libxml/catalog.h>\r
-#include <libxml/xmlerror.h>\r
-#include <libxml/threads.h>\r
-#include <libxml/globals.h>\r
-\r
-#define MAX_DELEGATE   50\r
-#define MAX_CATAL_DEPTH        50\r
-\r
-/**\r
- * TODO:\r
- *\r
- * macro to flag unimplemented blocks\r
- * XML_CATALOG_PREFER user env to select between system/public prefered\r
- * option. C.f. Richard Tobin <richard@cogsci.ed.ac.uk>\r
- *> Just FYI, I am using an environment variable XML_CATALOG_PREFER with\r
- *> values "system" and "public".  I have made the default be "system" to\r
- *> match yours.\r
- */\r
-#define TODO                                                           \\r
-    xmlGenericError(xmlGenericErrorContext,                            \\r
-           "Unimplemented block at %s:%d\n",                           \\r
-            __FILE__, __LINE__);\r
-\r
-#define XML_URN_PUBID "urn:publicid:"\r
-#define XML_CATAL_BREAK ((xmlChar *) -1)\r
-#ifndef XML_XML_DEFAULT_CATALOG\r
-#define XML_XML_DEFAULT_CATALOG "file:///etc/xml/catalog"\r
-#endif\r
-#ifndef XML_SGML_DEFAULT_CATALOG\r
-#define XML_SGML_DEFAULT_CATALOG "file:///etc/sgml/catalog"\r
-#endif\r
-\r
-#if defined(_WIN32) && defined(_MSC_VER)\r
-#undef XML_XML_DEFAULT_CATALOG\r
-static char XML_XML_DEFAULT_CATALOG[256] = "file:///etc/xml/catalog";\r
-void* __stdcall GetModuleHandleA(const char*);\r
-unsigned long __stdcall GetModuleFileNameA(void*, char*, unsigned long);\r
-#endif\r
-\r
-static xmlChar *xmlCatalogNormalizePublic(const xmlChar *pubID);\r
-static int xmlExpandCatalog(xmlCatalogPtr catal, const char *filename);\r
-\r
-/************************************************************************\r
- *                                                                     *\r
- *                     Types, all private                              *\r
- *                                                                     *\r
- ************************************************************************/\r
-\r
-typedef enum {\r
-    XML_CATA_REMOVED = -1,\r
-    XML_CATA_NONE = 0,\r
-    XML_CATA_CATALOG,\r
-    XML_CATA_BROKEN_CATALOG,\r
-    XML_CATA_NEXT_CATALOG,\r
-    XML_CATA_GROUP,\r
-    XML_CATA_PUBLIC,\r
-    XML_CATA_SYSTEM,\r
-    XML_CATA_REWRITE_SYSTEM,\r
-    XML_CATA_DELEGATE_PUBLIC,\r
-    XML_CATA_DELEGATE_SYSTEM,\r
-    XML_CATA_URI,\r
-    XML_CATA_REWRITE_URI,\r
-    XML_CATA_DELEGATE_URI,\r
-    SGML_CATA_SYSTEM,\r
-    SGML_CATA_PUBLIC,\r
-    SGML_CATA_ENTITY,\r
-    SGML_CATA_PENTITY,\r
-    SGML_CATA_DOCTYPE,\r
-    SGML_CATA_LINKTYPE,\r
-    SGML_CATA_NOTATION,\r
-    SGML_CATA_DELEGATE,\r
-    SGML_CATA_BASE,\r
-    SGML_CATA_CATALOG,\r
-    SGML_CATA_DOCUMENT,\r
-    SGML_CATA_SGMLDECL\r
-} xmlCatalogEntryType;\r
-\r
-typedef struct _xmlCatalogEntry xmlCatalogEntry;\r
-typedef xmlCatalogEntry *xmlCatalogEntryPtr;\r
-struct _xmlCatalogEntry {\r
-    struct _xmlCatalogEntry *next;\r
-    struct _xmlCatalogEntry *parent;\r
-    struct _xmlCatalogEntry *children;\r
-    xmlCatalogEntryType type;\r
-    xmlChar *name;\r
-    xmlChar *value;\r
-    xmlChar *URL;  /* The expanded URL using the base */\r
-    xmlCatalogPrefer prefer;\r
-    int dealloc;\r
-    int depth;\r
-    struct _xmlCatalogEntry *group;\r
-};\r
-\r
-typedef enum {\r
-    XML_XML_CATALOG_TYPE = 1,\r
-    XML_SGML_CATALOG_TYPE\r
-} xmlCatalogType;\r
-\r
-#define XML_MAX_SGML_CATA_DEPTH 10\r
-struct _xmlCatalog {\r
-    xmlCatalogType type;       /* either XML or SGML */\r
-\r
-    /*\r
-     * SGML Catalogs are stored as a simple hash table of catalog entries\r
-     * Catalog stack to check against overflows when building the\r
-     * SGML catalog\r
-     */\r
-    char *catalTab[XML_MAX_SGML_CATA_DEPTH];   /* stack of catals */\r
-    int          catalNr;      /* Number of current catal streams */\r
-    int          catalMax;     /* Max number of catal streams */\r
-    xmlHashTablePtr sgml;\r
-\r
-    /*\r
-     * XML Catalogs are stored as a tree of Catalog entries\r
-     */\r
-    xmlCatalogPrefer prefer;\r
-    xmlCatalogEntryPtr xml;\r
-};\r
-\r
-/************************************************************************\r
- *                                                                     *\r
- *                     Global variables                                *\r
- *                                                                     *\r
- ************************************************************************/\r
-\r
-/*\r
- * Those are preferences\r
- */\r
-static int xmlDebugCatalogs = 0;   /* used for debugging */\r
-static xmlCatalogAllow xmlCatalogDefaultAllow = XML_CATA_ALLOW_ALL;\r
-static xmlCatalogPrefer xmlCatalogDefaultPrefer = XML_CATA_PREFER_PUBLIC;\r
-\r
-/*\r
- * Hash table containing all the trees of XML catalogs parsed by\r
- * the application.\r
- */\r
-static xmlHashTablePtr xmlCatalogXMLFiles = NULL;\r
-\r
-/*\r
- * The default catalog in use by the application\r
- */\r
-static xmlCatalogPtr xmlDefaultCatalog = NULL;\r
-\r
-/*\r
- * A mutex for modifying the shared global catalog(s)\r
- * xmlDefaultCatalog tree.\r
- * It also protects xmlCatalogXMLFiles\r
- * The core of this readers/writer scheme is in xmlFetchXMLCatalogFile()\r
- */\r
-static xmlRMutexPtr xmlCatalogMutex = NULL;\r
-\r
-/*\r
- * Whether the catalog support was initialized.\r
- */\r
-static int xmlCatalogInitialized = 0;\r
-\r
-/************************************************************************\r
- *                                                                     *\r
- *                     Catalog error handlers                          *\r
- *                                                                     *\r
- ************************************************************************/\r
-\r
-/**\r
- * xmlCatalogErrMemory:\r
- * @extra:  extra informations\r
- *\r
- * Handle an out of memory condition\r
- */\r
-static void\r
-xmlCatalogErrMemory(const char *extra)\r
-{\r
-    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_CATALOG,\r
-                    XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0,\r
-                   extra, NULL, NULL, 0, 0,\r
-                   "Memory allocation failed : %s\n", extra);\r
-}\r
-\r
-/**\r
- * xmlCatalogErr:\r
- * @catal: the Catalog entry\r
- * @node: the context node\r
- * @msg:  the error message\r
- * @extra:  extra informations\r
- *\r
- * Handle a catalog error\r
- */\r
-static void\r
-xmlCatalogErr(xmlCatalogEntryPtr catal, xmlNodePtr node, int error,\r
-               const char *msg, const xmlChar *str1, const xmlChar *str2,\r
-              const xmlChar *str3)\r
-{\r
-    __xmlRaiseError(NULL, NULL, NULL, catal, node, XML_FROM_CATALOG,\r
-                    error, XML_ERR_ERROR, NULL, 0,\r
-                   (const char *) str1, (const char *) str2,\r
-                   (const char *) str3, 0, 0,\r
-                   msg, str1, str2, str3);\r
-}\r
-\r
-\r
-/************************************************************************\r
- *                                                                     *\r
- *                     Allocation and Freeing                          *\r
- *                                                                     *\r
- ************************************************************************/\r
-\r
-/**\r
- * xmlNewCatalogEntry:\r
- * @type:  type of entry\r
- * @name:  name of the entry\r
- * @value:  value of the entry\r
- * @prefer:  the PUBLIC vs. SYSTEM current preference value\r
- * @group:  for members of a group, the group entry \r
- *\r
- * create a new Catalog entry, this type is shared both by XML and \r
- * SGML catalogs, but the acceptable types values differs.\r
- *\r
- * Returns the xmlCatalogEntryPtr or NULL in case of error\r
- */\r
-static xmlCatalogEntryPtr\r
-xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name,\r
-          const xmlChar *value, const xmlChar *URL, xmlCatalogPrefer prefer,\r
-          xmlCatalogEntryPtr group) {\r
-    xmlCatalogEntryPtr ret;\r
-    xmlChar *normid = NULL;\r
-\r
-    ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));\r
-    if (ret == NULL) {\r
-        xmlCatalogErrMemory("allocating catalog entry");\r
-       return(NULL);\r
-    }\r
-    ret->next = NULL;\r
-    ret->parent = NULL;\r
-    ret->children = NULL;\r
-    ret->type = type;\r
-    if (type == XML_CATA_PUBLIC || type == XML_CATA_DELEGATE_PUBLIC) {\r
-        normid = xmlCatalogNormalizePublic(name);\r
-        if (normid != NULL)\r
-            name = (*normid != 0 ? normid : NULL);\r
-    }\r
-    if (name != NULL)\r
-       ret->name = xmlStrdup(name);\r
-    else\r
-       ret->name = NULL;\r
-    if (normid != NULL)\r
-        xmlFree(normid);\r
-    if (value != NULL)\r
-       ret->value = xmlStrdup(value);\r
-    else\r
-       ret->value = NULL;\r
-    if (URL == NULL)\r
-       URL = value;\r
-    if (URL != NULL)\r
-       ret->URL = xmlStrdup(URL);\r
-    else\r
-       ret->URL = NULL;\r
-    ret->prefer = prefer;\r
-    ret->dealloc = 0;\r
-    ret->depth = 0;\r
-    ret->group = group;\r
-    return(ret);\r
-}\r
-\r
-static void\r
-xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret);\r
-\r
-/**\r
- * xmlFreeCatalogEntry:\r
- * @ret:  a Catalog entry\r
- *\r
- * Free the memory allocated to a Catalog entry\r
- */\r
-static void\r
-xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) {\r
-    if (ret == NULL)\r
-       return;\r
-    /*\r
-     * Entries stored in the file hash must be deallocated\r
-     * only by the file hash cleaner !\r
-     */\r
-    if (ret->dealloc == 1)\r
-       return;\r
-\r
-    if (xmlDebugCatalogs) {\r
-       if (ret->name != NULL)\r
-           xmlGenericError(xmlGenericErrorContext,\r
-                   "Free catalog entry %s\n", ret->name);\r
-       else if (ret->value != NULL)\r
-           xmlGenericError(xmlGenericErrorContext,\r
-                   "Free catalog entry %s\n", ret->value);\r
-       else\r
-           xmlGenericError(xmlGenericErrorContext,\r
-                   "Free catalog entry\n");\r
-    }\r
-\r
-    if (ret->name != NULL)\r
-       xmlFree(ret->name);\r
-    if (ret->value != NULL)\r
-       xmlFree(ret->value);\r
-    if (ret->URL != NULL)\r
-       xmlFree(ret->URL);\r
-    xmlFree(ret);\r
-}\r
-\r
-/**\r
- * xmlFreeCatalogEntryList:\r
- * @ret:  a Catalog entry list\r
- *\r
- * Free the memory allocated to a full chained list of Catalog entries\r
- */\r
-static void\r
-xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) {\r
-    xmlCatalogEntryPtr next;\r
-\r
-    while (ret != NULL) {\r
-       next = ret->next;\r
-       xmlFreeCatalogEntry(ret);\r
-       ret = next;\r
-    }\r
-}\r
-\r
-/**\r
- * xmlFreeCatalogHashEntryList:\r
- * @ret:  a Catalog entry list\r
- *\r
- * Free the memory allocated to list of Catalog entries from the\r
- * catalog file hash.\r
- */\r
-static void\r
-xmlFreeCatalogHashEntryList(xmlCatalogEntryPtr catal) {\r
-    xmlCatalogEntryPtr children, next;\r
-\r
-    if (catal == NULL)\r
-       return;\r
-\r
-    children = catal->children;\r
-    while (children != NULL) {\r
-       next = children->next;\r
-       children->dealloc = 0;\r
-       children->children = NULL;\r
-       xmlFreeCatalogEntry(children);\r
-       children = next;\r
-    }\r
-    catal->dealloc = 0;\r
-    xmlFreeCatalogEntry(catal);\r
-}\r
-\r
-/**\r
- * xmlCreateNewCatalog:\r
- * @type:  type of catalog\r
- * @prefer:  the PUBLIC vs. SYSTEM current preference value\r
- *\r
- * create a new Catalog, this type is shared both by XML and \r
- * SGML catalogs, but the acceptable types values differs.\r
- *\r
- * Returns the xmlCatalogPtr or NULL in case of error\r
- */\r
-static xmlCatalogPtr\r
-xmlCreateNewCatalog(xmlCatalogType type, xmlCatalogPrefer prefer) {\r
-    xmlCatalogPtr ret;\r
-\r
-    ret = (xmlCatalogPtr) xmlMalloc(sizeof(xmlCatalog));\r
-    if (ret == NULL) {\r
-        xmlCatalogErrMemory("allocating catalog");\r
-       return(NULL);\r
-    }\r
-    memset(ret, 0, sizeof(xmlCatalog));\r
-    ret->type = type;\r
-    ret->catalNr = 0;\r
-    ret->catalMax = XML_MAX_SGML_CATA_DEPTH;\r
-    ret->prefer = prefer;\r
-    if (ret->type == XML_SGML_CATALOG_TYPE)\r
-       ret->sgml = xmlHashCreate(10);\r
-    return(ret);\r
-}\r
-\r
-/**\r
- * xmlFreeCatalog:\r
- * @catal:  a Catalog\r
- *\r
- * Free the memory allocated to a Catalog\r
- */\r
-void\r
-xmlFreeCatalog(xmlCatalogPtr catal) {\r
-    if (catal == NULL)\r
-       return;\r
-    if (catal->xml != NULL)\r
-       xmlFreeCatalogEntryList(catal->xml);\r
-    if (catal->sgml != NULL)\r
-       xmlHashFree(catal->sgml,\r
-               (xmlHashDeallocator) xmlFreeCatalogEntry);\r
-    xmlFree(catal);\r
-}\r
-\r
-/************************************************************************\r
- *                                                                     *\r
- *                     Serializing Catalogs                            *\r
- *                                                                     *\r
- ************************************************************************/\r
-\r
-#ifdef LIBXML_OUTPUT_ENABLED\r
-/**\r
- * xmlCatalogDumpEntry:\r
- * @entry:  the catalog entry\r
- * @out:  the file.\r
- *\r
- * Serialize an SGML Catalog entry\r
- */\r
-static void\r
-xmlCatalogDumpEntry(xmlCatalogEntryPtr entry, FILE *out) {\r
-    if ((entry == NULL) || (out == NULL))\r
-       return;\r
-    switch (entry->type) {\r
-       case SGML_CATA_ENTITY:\r
-           fprintf(out, "ENTITY "); break;\r
-       case SGML_CATA_PENTITY:\r
-           fprintf(out, "ENTITY %%"); break;\r
-       case SGML_CATA_DOCTYPE:\r
-           fprintf(out, "DOCTYPE "); break;\r
-       case SGML_CATA_LINKTYPE:\r
-           fprintf(out, "LINKTYPE "); break;\r
-       case SGML_CATA_NOTATION:\r
-           fprintf(out, "NOTATION "); break;\r
-       case SGML_CATA_PUBLIC:\r
-           fprintf(out, "PUBLIC "); break;\r
-       case SGML_CATA_SYSTEM:\r
-           fprintf(out, "SYSTEM "); break;\r
-       case SGML_CATA_DELEGATE:\r
-           fprintf(out, "DELEGATE "); break;\r
-       case SGML_CATA_BASE:\r
-           fprintf(out, "BASE "); break;\r
-       case SGML_CATA_CATALOG:\r
-           fprintf(out, "CATALOG "); break;\r
-       case SGML_CATA_DOCUMENT:\r
-           fprintf(out, "DOCUMENT "); break;\r
-       case SGML_CATA_SGMLDECL:\r
-           fprintf(out, "SGMLDECL "); break;\r
-       default:\r
-           return;\r
-    }\r
-    switch (entry->type) {\r
-       case SGML_CATA_ENTITY:\r
-       case SGML_CATA_PENTITY:\r
-       case SGML_CATA_DOCTYPE:\r
-       case SGML_CATA_LINKTYPE:\r
-       case SGML_CATA_NOTATION:\r
-           fprintf(out, "%s", (const char *) entry->name); break;\r
-       case SGML_CATA_PUBLIC:\r
-       case SGML_CATA_SYSTEM:\r
-       case SGML_CATA_SGMLDECL:\r
-       case SGML_CATA_DOCUMENT:\r
-       case SGML_CATA_CATALOG:\r
-       case SGML_CATA_BASE:\r
-       case SGML_CATA_DELEGATE:\r
-           fprintf(out, "\"%s\"", entry->name); break;\r
-       default:\r
-           break;\r
-    }\r
-    switch (entry->type) {\r
-       case SGML_CATA_ENTITY:\r
-       case SGML_CATA_PENTITY:\r
-       case SGML_CATA_DOCTYPE:\r
-       case SGML_CATA_LINKTYPE:\r
-       case SGML_CATA_NOTATION:\r
-       case SGML_CATA_PUBLIC:\r
-       case SGML_CATA_SYSTEM:\r
-       case SGML_CATA_DELEGATE:\r
-           fprintf(out, " \"%s\"", entry->value); break;\r
-       default:\r
-           break;\r
-    }\r
-    fprintf(out, "\n");\r
-}\r
-\r
-/**\r
- * xmlDumpXMLCatalogNode:\r
- * @catal:  top catalog entry\r
- * @catalog: pointer to the xml tree\r
- * @doc: the containing document\r
- * @ns: the current namespace\r
- * @cgroup: group node for group members\r
- *\r
- * Serializes a Catalog entry, called by xmlDumpXMLCatalog and recursively\r
- * for group entries\r
- */\r
-static void xmlDumpXMLCatalogNode(xmlCatalogEntryPtr catal, xmlNodePtr catalog,\r
-                   xmlDocPtr doc, xmlNsPtr ns, xmlCatalogEntryPtr cgroup) {\r
-    xmlNodePtr node;\r
-    xmlCatalogEntryPtr cur;\r
-    /*\r
-     * add all the catalog entries\r
-     */\r
-    cur = catal;\r
-    while (cur != NULL) {\r
-        if (cur->group == cgroup) {\r
-           switch (cur->type) {\r
-               case XML_CATA_REMOVED:\r
-                   break;\r
-               case XML_CATA_BROKEN_CATALOG:\r
-               case XML_CATA_CATALOG:\r
-                   if (cur == catal) {\r
-                       cur = cur->children;\r
-                       continue;\r
-                   }\r
-                   break;\r
-               case XML_CATA_NEXT_CATALOG:\r
-                   node = xmlNewDocNode(doc, ns, BAD_CAST "nextCatalog", NULL);\r
-                   xmlSetProp(node, BAD_CAST "catalog", cur->value);\r
-                   xmlAddChild(catalog, node);\r
-                    break;\r
-               case XML_CATA_NONE:\r
-                   break;\r
-               case XML_CATA_GROUP:\r
-                   node = xmlNewDocNode(doc, ns, BAD_CAST "group", NULL);\r
-                   xmlSetProp(node, BAD_CAST "id", cur->name);\r
-                   if (cur->value != NULL) {\r
-                       xmlNsPtr xns;\r
-                       xns = xmlSearchNsByHref(doc, node, XML_XML_NAMESPACE);\r
-                       if (xns != NULL)\r
-                           xmlSetNsProp(node, xns, BAD_CAST "base",\r
-                                        cur->value);\r
-                   }\r
-                   switch (cur->prefer) {\r
-                       case XML_CATA_PREFER_NONE:\r
-                           break;\r
-                       case XML_CATA_PREFER_PUBLIC:\r
-                           xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "public");\r
-                           break;\r
-                       case XML_CATA_PREFER_SYSTEM:\r
-                           xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "system");\r
-                           break;\r
-                   }\r
-                   xmlDumpXMLCatalogNode(cur->next, node, doc, ns, cur);\r
-                   xmlAddChild(catalog, node);\r
-                   break;\r
-               case XML_CATA_PUBLIC:\r
-                   node = xmlNewDocNode(doc, ns, BAD_CAST "public", NULL);\r
-                   xmlSetProp(node, BAD_CAST "publicId", cur->name);\r
-                   xmlSetProp(node, BAD_CAST "uri", cur->value);\r
-                   xmlAddChild(catalog, node);\r
-                   break;\r
-               case XML_CATA_SYSTEM:\r
-                   node = xmlNewDocNode(doc, ns, BAD_CAST "system", NULL);\r
-                   xmlSetProp(node, BAD_CAST "systemId", cur->name);\r
-                   xmlSetProp(node, BAD_CAST "uri", cur->value);\r
-                   xmlAddChild(catalog, node);\r
-                   break;\r
-               case XML_CATA_REWRITE_SYSTEM:\r
-                   node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteSystem", NULL);\r
-                   xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);\r
-                   xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);\r
-                   xmlAddChild(catalog, node);\r
-                   break;\r
-               case XML_CATA_DELEGATE_PUBLIC:\r
-                   node = xmlNewDocNode(doc, ns, BAD_CAST "delegatePublic", NULL);\r
-                   xmlSetProp(node, BAD_CAST "publicIdStartString", cur->name);\r
-                   xmlSetProp(node, BAD_CAST "catalog", cur->value);\r
-                   xmlAddChild(catalog, node);\r
-                   break;\r
-               case XML_CATA_DELEGATE_SYSTEM:\r
-                   node = xmlNewDocNode(doc, ns, BAD_CAST "delegateSystem", NULL);\r
-                   xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);\r
-                   xmlSetProp(node, BAD_CAST "catalog", cur->value);\r
-                   xmlAddChild(catalog, node);\r
-                   break;\r
-               case XML_CATA_URI:\r
-                   node = xmlNewDocNode(doc, ns, BAD_CAST "uri", NULL);\r
-                   xmlSetProp(node, BAD_CAST "name", cur->name);\r
-                   xmlSetProp(node, BAD_CAST "uri", cur->value);\r
-                   xmlAddChild(catalog, node);\r
-                   break;\r
-               case XML_CATA_REWRITE_URI:\r
-                   node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteURI", NULL);\r
-                   xmlSetProp(node, BAD_CAST "uriStartString", cur->name);\r
-                   xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);\r
-                   xmlAddChild(catalog, node);\r
-                   break;\r
-               case XML_CATA_DELEGATE_URI:\r
-                   node = xmlNewDocNode(doc, ns, BAD_CAST "delegateURI", NULL);\r
-                   xmlSetProp(node, BAD_CAST "uriStartString", cur->name);\r
-                   xmlSetProp(node, BAD_CAST "catalog", cur->value);\r
-                   xmlAddChild(catalog, node);\r
-                   break;\r
-               case SGML_CATA_SYSTEM:\r
-               case SGML_CATA_PUBLIC:\r
-               case SGML_CATA_ENTITY:\r
-               case SGML_CATA_PENTITY:\r
-               case SGML_CATA_DOCTYPE:\r
-               case SGML_CATA_LINKTYPE:\r
-               case SGML_CATA_NOTATION:\r
-               case SGML_CATA_DELEGATE:\r
-               case SGML_CATA_BASE:\r
-               case SGML_CATA_CATALOG:\r
-               case SGML_CATA_DOCUMENT:\r
-               case SGML_CATA_SGMLDECL:\r
-                   break;\r
-           }\r
-        }\r
-       cur = cur->next;\r
-    }\r
-}\r
-\r
-static int\r
-xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) {\r
-    int ret;\r
-    xmlDocPtr doc;\r
-    xmlNsPtr ns;\r
-    xmlDtdPtr dtd;\r
-    xmlNodePtr catalog;\r
-    xmlOutputBufferPtr buf;\r
-\r
-    /*\r
-     * Rebuild a catalog\r
-     */\r
-    doc = xmlNewDoc(NULL);\r
-    if (doc == NULL)\r
-       return(-1);\r
-    dtd = xmlNewDtd(doc, BAD_CAST "catalog",\r
-              BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN",\r
-BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd");\r
-\r
-    xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);\r
-\r
-    ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL);\r
-    if (ns == NULL) {\r
-       xmlFreeDoc(doc);\r
-       return(-1);\r
-    }\r
-    catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL);\r
-    if (catalog == NULL) {\r
-       xmlFreeNs(ns);\r
-       xmlFreeDoc(doc);\r
-       return(-1);\r
-    }\r
-    catalog->nsDef = ns;\r
-    xmlAddChild((xmlNodePtr) doc, catalog);\r
-\r
-    xmlDumpXMLCatalogNode(catal, catalog, doc, ns, NULL);\r
-    \r
-    /*\r
-     * reserialize it\r
-     */\r
-    buf = xmlOutputBufferCreateFile(out, NULL);\r
-    if (buf == NULL) {\r
-       xmlFreeDoc(doc);\r
-       return(-1);\r
-    }\r
-    ret = xmlSaveFormatFileTo(buf, doc, NULL, 1);\r
-\r
-    /*\r
-     * Free it\r
-     */\r
-    xmlFreeDoc(doc);\r
-\r
-    return(ret);\r
-}\r
-#endif /* LIBXML_OUTPUT_ENABLED */\r
-\r
-/************************************************************************\r
- *                                                                     *\r
- *                     Converting SGML Catalogs to XML                 *\r
- *                                                                     *\r
- ************************************************************************/\r
-\r
-/**\r
- * xmlCatalogConvertEntry:\r
- * @entry:  the entry\r
- * @catal:  pointer to the catalog being converted\r
- *\r
- * Convert one entry from the catalog\r
- */\r
-static void\r
-xmlCatalogConvertEntry(xmlCatalogEntryPtr entry, xmlCatalogPtr catal) {\r
-    if ((entry == NULL) || (catal == NULL) || (catal->sgml == NULL) ||\r
-       (catal->xml == NULL))\r
-       return;\r
-    switch (entry->type) {\r
-       case SGML_CATA_ENTITY:\r
-           entry->type = XML_CATA_PUBLIC;\r
-           break;\r
-       case SGML_CATA_PENTITY:\r
-           entry->type = XML_CATA_PUBLIC;\r
-           break;\r
-       case SGML_CATA_DOCTYPE:\r
-           entry->type = XML_CATA_PUBLIC;\r
-           break;\r
-       case SGML_CATA_LINKTYPE:\r
-           entry->type = XML_CATA_PUBLIC;\r
-           break;\r
-       case SGML_CATA_NOTATION:\r
-           entry->type = XML_CATA_PUBLIC;\r
-           break;\r
-       case SGML_CATA_PUBLIC:\r
-           entry->type = XML_CATA_PUBLIC;\r
-           break;\r
-       case SGML_CATA_SYSTEM:\r
-           entry->type = XML_CATA_SYSTEM;\r
-           break;\r
-       case SGML_CATA_DELEGATE:\r
-           entry->type = XML_CATA_DELEGATE_PUBLIC;\r
-           break;\r
-       case SGML_CATA_CATALOG:\r
-           entry->type = XML_CATA_CATALOG;\r
-           break;\r
-       default:\r
-           xmlHashRemoveEntry(catal->sgml, entry->name,\r
-                              (xmlHashDeallocator) xmlFreeCatalogEntry);\r
-           return;\r
-    }\r
-    /*\r
-     * Conversion successful, remove from the SGML catalog\r
-     * and add it to the default XML one\r
-     */\r
-    xmlHashRemoveEntry(catal->sgml, entry->name, NULL);\r
-    entry->parent = catal->xml;\r
-    entry->next = NULL;\r
-    if (catal->xml->children == NULL)\r
-       catal->xml->children = entry;\r
-    else {\r
-       xmlCatalogEntryPtr prev;\r
-\r
-       prev = catal->xml->children;\r
-       while (prev->next != NULL)\r
-           prev = prev->next;\r
-       prev->next = entry;\r
-    }\r
-}\r
-\r
-/**\r
- * xmlConvertSGMLCatalog:\r
- * @catal: the catalog\r
- *\r
- * Convert all the SGML catalog entries as XML ones\r
- *\r
- * Returns the number of entries converted if successful, -1 otherwise\r
- */\r
-int\r
-xmlConvertSGMLCatalog(xmlCatalogPtr catal) {\r
-\r
-    if ((catal == NULL) || (catal->type != XML_SGML_CATALOG_TYPE))\r
-       return(-1);\r
-\r
-    if (xmlDebugCatalogs) {\r
-       xmlGenericError(xmlGenericErrorContext,\r
-               "Converting SGML catalog to XML\n");\r
-    }\r
-    xmlHashScan(catal->sgml,\r
-               (xmlHashScanner) xmlCatalogConvertEntry,\r
-               &catal);\r
-    return(0);\r
-}\r
-\r
-/************************************************************************\r
- *                                                                     *\r
- *                     Helper function                                 *\r
- *                                                                     *\r
- ************************************************************************/\r
-\r
-/**\r
- * xmlCatalogUnWrapURN:\r
- * @urn:  an "urn:publicid:" to unwrap\r
- *\r
- * Expand the URN into the equivalent Public Identifier\r
- *\r
- * Returns the new identifier or NULL, the string must be deallocated\r
- *         by the caller.\r
- */\r
-static xmlChar *\r
-xmlCatalogUnWrapURN(const xmlChar *urn) {\r
-    xmlChar result[2000];\r
-    unsigned int i = 0;\r
-\r
-    if (xmlStrncmp(urn, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1))\r
-       return(NULL);\r
-    urn += sizeof(XML_URN_PUBID) - 1;\r
-    \r
-    while (*urn != 0) {\r
-       if (i > sizeof(result) - 4)\r
-           break;\r
-       if (*urn == '+') {\r
-           result[i++] = ' ';\r
-           urn++;\r
-       } else if (*urn == ':') {\r
-           result[i++] = '/';\r
-           result[i++] = '/';\r
-           urn++;\r
-       } else if (*urn == ';') {\r
-           result[i++] = ':';\r
-           result[i++] = ':';\r
-           urn++;\r
-       } else if (*urn == '%') {\r
-           if ((urn[1] == '2') && (urn[2] == 'B'))\r
-               result[i++] = '+';\r
-           else if ((urn[1] == '3') && (urn[2] == 'A'))\r
-               result[i++] = ':';\r
-           else if ((urn[1] == '2') && (urn[2] == 'F'))\r
-               result[i++] = '/';\r
-           else if ((urn[1] == '3') && (urn[2] == 'B'))\r
-               result[i++] = ';';\r
-           else if ((urn[1] == '2') && (urn[2] == '7'))\r
-               result[i++] = '\'';\r
-           else if ((urn[1] == '3') && (urn[2] == 'F'))\r
-               result[i++] = '?';\r
-           else if ((urn[1] == '2') && (urn[2] == '3'))\r
-               result[i++] = '#';\r
-           else if ((urn[1] == '2') && (urn[2] == '5'))\r
-               result[i++] = '%';\r
-           else {\r
-               result[i++] = *urn;\r
-               urn++;\r
-               continue;\r
-           }\r
-           urn += 3;\r
-       } else {\r
-           result[i++] = *urn;\r
-           urn++;\r
-       }\r
-    }\r
-    result[i] = 0;\r
-\r
-    return(xmlStrdup(result));\r
-}\r
-\r
-/**\r
- * xmlParseCatalogFile:\r
- * @filename:  the filename\r
- *\r
- * parse an XML file and build a tree. It's like xmlParseFile()\r
- * except it bypass all catalog lookups.\r
- *\r
- * Returns the resulting document tree or NULL in case of error\r
- */\r
-\r
-xmlDocPtr\r
-xmlParseCatalogFile(const char *filename) {\r
-    xmlDocPtr ret;\r
-    xmlParserCtxtPtr ctxt;\r
-    char *directory = NULL;\r
-    xmlParserInputPtr inputStream;\r
-    xmlParserInputBufferPtr buf;\r
-\r
-    ctxt = xmlNewParserCtxt();\r
-    if (ctxt == NULL) {\r
-#ifdef LIBXML_SAX1_ENABLED\r
-       if (xmlDefaultSAXHandler.error != NULL) {\r
-           xmlDefaultSAXHandler.error(NULL, "out of memory\n");\r
-       }\r
-#endif\r
-       return(NULL);\r
-    }\r
-\r
-    buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE);\r
-    if (buf == NULL) {\r
-       xmlFreeParserCtxt(ctxt);\r
-       return(NULL);\r
-    }\r
-\r
-    inputStream = xmlNewInputStream(ctxt);\r
-    if (inputStream == NULL) {\r
-       xmlFreeParserCtxt(ctxt);\r
-       return(NULL);\r
-    }\r
-\r
-    inputStream->filename = (char *) xmlCanonicPath((const xmlChar *)filename);\r
-    inputStream->buf = buf;\r
-    inputStream->base = inputStream->buf->buffer->content;\r
-    inputStream->cur = inputStream->buf->buffer->content;\r
-    inputStream->end = \r
-       &inputStream->buf->buffer->content[inputStream->buf->buffer->use];\r
-\r
-    inputPush(ctxt, inputStream);\r
-    if ((ctxt->directory == NULL) && (directory == NULL))\r
-        directory = xmlParserGetDirectory(filename);\r
-    if ((ctxt->directory == NULL) && (directory != NULL))\r
-        ctxt->directory = directory;\r
-    ctxt->valid = 0;\r
-    ctxt->validate = 0;\r
-    ctxt->loadsubset = 0;\r
-    ctxt->pedantic = 0;\r
-    ctxt->dictNames = 1;\r
-\r
-    xmlParseDocument(ctxt);\r
-\r
-    if (ctxt->wellFormed)\r
-       ret = ctxt->myDoc;\r
-    else {\r
-        ret = NULL;\r
-        xmlFreeDoc(ctxt->myDoc);\r
-        ctxt->myDoc = NULL;\r
-    }\r
-    xmlFreeParserCtxt(ctxt);\r
-    \r
-    return(ret);\r
-}\r
-\r
-/**\r
- * xmlLoadFileContent:\r
- * @filename:  a file path\r
- *\r
- * Load a file content into memory.\r
- *\r
- * Returns a pointer to the 0 terminated string or NULL in case of error\r
- */\r
-static xmlChar *\r
-xmlLoadFileContent(const char *filename)\r
-{\r
-#ifdef HAVE_STAT\r
-    int fd;\r
-#else\r
-    FILE *fd;\r
-#endif\r
-    int len;\r
-    long size;\r
-\r
-#ifdef HAVE_STAT\r
-    struct stat info;\r
-#endif\r
-    xmlChar *content;\r
-\r
-    if (filename == NULL)\r
-        return (NULL);\r
-\r
-#ifdef HAVE_STAT\r
-    if (stat(filename, &info) < 0)\r
-        return (NULL);\r
-#endif\r
-\r
-#ifdef HAVE_STAT\r
-    if ((fd = open(filename, O_RDONLY)) < 0)\r
-#else\r
-    if ((fd = fopen(filename, "rb")) == NULL)\r
-#endif\r
-    {\r
-        return (NULL);\r
-    }\r
-#ifdef HAVE_STAT\r
-    size = info.st_size;\r
-#else\r
-    if (fseek(fd, 0, SEEK_END) || (size = ftell(fd)) == EOF || fseek(fd, 0, SEEK_SET)) {        /* File operations denied? ok, just close and return failure */\r
-        fclose(fd);\r
-        return (NULL);\r
-    }\r
-#endif\r
-    content = xmlMallocAtomic(size + 10);\r
-    if (content == NULL) {\r
-        xmlCatalogErrMemory("allocating catalog data");\r
-        return (NULL);\r
-    }\r
-#ifdef HAVE_STAT\r
-    len = read(fd, content, size);\r
-#else\r
-    len = fread(content, 1, size, fd);\r
-#endif\r
-    if (len < 0) {\r
-        xmlFree(content);\r
-        return (NULL);\r
-    }\r
-#ifdef HAVE_STAT\r
-    close(fd);\r
-#else\r
-    fclose(fd);\r
-#endif\r
-    content[len] = 0;\r
-\r
-    return(content);\r
-}\r
-\r
-/**\r
- * xmlCatalogNormalizePublic:\r
- * @pubID:  the public ID string\r
- *\r
- *  Normalizes the Public Identifier\r
- *\r
- * Implements 6.2. Public Identifier Normalization\r
- * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html\r
- *\r
- * Returns the new string or NULL, the string must be deallocated\r
- *         by the caller.\r
- */\r
-static xmlChar *\r
-xmlCatalogNormalizePublic(const xmlChar *pubID)\r
-{\r
-    int ok = 1;\r
-    int white;\r
-    const xmlChar *p;\r
-    xmlChar *ret;\r
-    xmlChar *q;\r
-\r
-    if (pubID == NULL)\r
-        return(NULL);\r
-\r
-    white = 1;\r
-    for (p = pubID;*p != 0 && ok;p++) {\r
-        if (!xmlIsBlank_ch(*p))\r
-            white = 0;\r
-        else if (*p == 0x20 && !white)\r
-            white = 1;\r
-        else\r
-            ok = 0;\r
-    }\r
-    if (ok && !white)  /* is normalized */\r
-        return(NULL);\r
-\r
-    ret = xmlStrdup(pubID);\r
-    q = ret;\r
-    white = 0;\r
-    for (p = pubID;*p != 0;p++) {\r
-        if (xmlIsBlank_ch(*p)) {\r
-            if (q != ret)\r
-                white = 1;\r
-        } else {\r
-            if (white) {\r
-                *(q++) = 0x20;\r
-                white = 0;\r
-            }\r
-            *(q++) = *p;\r
-        }\r
-    }\r
-    *q = 0;\r
-    return(ret);\r
-}\r
-\r
-/************************************************************************\r
- *                                                                     *\r
- *                     The XML Catalog parser                          *\r
- *                                                                     *\r
- ************************************************************************/\r
-\r
-static xmlCatalogEntryPtr\r
-xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);\r
-static void\r
-xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,\r
-                          xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup);\r
-static xmlChar *\r
-xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,\r
-                     const xmlChar *sysID);\r
-static xmlChar *\r
-xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI);\r
-\r
-\r
-/**\r
- * xmlGetXMLCatalogEntryType:\r
- * @name:  the name\r
- *\r
- * lookup the internal type associated to an XML catalog entry name\r
- *\r
- * Returns the type associated with that name\r
- */\r
-static xmlCatalogEntryType\r
-xmlGetXMLCatalogEntryType(const xmlChar *name) {\r
-    xmlCatalogEntryType type = XML_CATA_NONE;\r
-    if (xmlStrEqual(name, (const xmlChar *) "system"))\r
-       type = XML_CATA_SYSTEM;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "public"))\r
-       type = XML_CATA_PUBLIC;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem"))\r
-       type = XML_CATA_REWRITE_SYSTEM;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic"))\r
-       type = XML_CATA_DELEGATE_PUBLIC;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem"))\r
-       type = XML_CATA_DELEGATE_SYSTEM;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "uri"))\r
-       type = XML_CATA_URI;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI"))\r
-       type = XML_CATA_REWRITE_URI;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "delegateURI"))\r
-       type = XML_CATA_DELEGATE_URI;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog"))\r
-       type = XML_CATA_NEXT_CATALOG;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "catalog"))\r
-       type = XML_CATA_CATALOG;\r
-    return(type);\r
-}\r
-\r
-/**\r
- * xmlParseXMLCatalogOneNode:\r
- * @cur:  the XML node\r
- * @type:  the type of Catalog entry\r
- * @name:  the name of the node\r
- * @attrName:  the attribute holding the value\r
- * @uriAttrName:  the attribute holding the URI-Reference\r
- * @prefer:  the PUBLIC vs. SYSTEM current preference value\r
- * @cgroup:  the group which includes this node\r
- *\r
- * Finishes the examination of an XML tree node of a catalog and build\r
- * a Catalog entry from it.\r
- *\r
- * Returns the new Catalog entry node or NULL in case of error.\r
- */\r
-static xmlCatalogEntryPtr\r
-xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type,\r
-                         const xmlChar *name, const xmlChar *attrName,\r
-                         const xmlChar *uriAttrName, xmlCatalogPrefer prefer,\r
-                         xmlCatalogEntryPtr cgroup) {\r
-    int ok = 1;\r
-    xmlChar *uriValue;\r
-    xmlChar *nameValue = NULL;\r
-    xmlChar *base = NULL;\r
-    xmlChar *URL = NULL;\r
-    xmlCatalogEntryPtr ret = NULL;\r
-\r
-    if (attrName != NULL) {\r
-       nameValue = xmlGetProp(cur, attrName);\r
-       if (nameValue == NULL) {\r
-           xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,\r
-                         "%s entry lacks '%s'\n", name, attrName, NULL);\r
-           ok = 0;\r
-       }\r
-    }\r
-    uriValue = xmlGetProp(cur, uriAttrName);\r
-    if (uriValue == NULL) {\r
-       xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,\r
-               "%s entry lacks '%s'\n", name, uriAttrName, NULL);\r
-       ok = 0;\r
-    }\r
-    if (!ok) {\r
-       if (nameValue != NULL)\r
-           xmlFree(nameValue);\r
-       if (uriValue != NULL)\r
-           xmlFree(uriValue);\r
-       return(NULL);\r
-    }\r
-\r
-    base = xmlNodeGetBase(cur->doc, cur);\r
-    URL = xmlBuildURI(uriValue, base);\r
-    if (URL != NULL) {\r
-       if (xmlDebugCatalogs > 1) {\r
-           if (nameValue != NULL)\r
-               xmlGenericError(xmlGenericErrorContext,\r
-                       "Found %s: '%s' '%s'\n", name, nameValue, URL);\r
-           else\r
-               xmlGenericError(xmlGenericErrorContext,\r
-                       "Found %s: '%s'\n", name, URL);\r
-       }\r
-       ret = xmlNewCatalogEntry(type, nameValue, uriValue, URL, prefer, cgroup);\r
-    } else {\r
-       xmlCatalogErr(ret, cur, XML_CATALOG_ENTRY_BROKEN,\r
-               "%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue);\r
-    }\r
-    if (nameValue != NULL)\r
-       xmlFree(nameValue);\r
-    if (uriValue != NULL)\r
-       xmlFree(uriValue);\r
-    if (base != NULL)\r
-       xmlFree(base);\r
-    if (URL != NULL)\r
-       xmlFree(URL);\r
-    return(ret);\r
-}\r
-\r
-/**\r
- * xmlParseXMLCatalogNode:\r
- * @cur:  the XML node\r
- * @prefer:  the PUBLIC vs. SYSTEM current preference value\r
- * @parent:  the parent Catalog entry\r
- * @cgroup:  the group which includes this node\r
- *\r
- * Examines an XML tree node of a catalog and build\r
- * a Catalog entry from it adding it to its parent. The examination can\r
- * be recursive.\r
- */\r
-static void\r
-xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer,\r
-                      xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup)\r
-{\r
-    xmlChar *uri = NULL;\r
-    xmlChar *URL = NULL;\r
-    xmlChar *base = NULL;\r
-    xmlCatalogEntryPtr entry = NULL;\r
-\r
-    if (cur == NULL)\r
-        return;\r
-    if (xmlStrEqual(cur->name, BAD_CAST "group")) {\r
-        xmlChar *prop;\r
-       xmlCatalogPrefer pref = XML_CATA_PREFER_NONE;\r
-\r
-        prop = xmlGetProp(cur, BAD_CAST "prefer");\r
-        if (prop != NULL) {\r
-            if (xmlStrEqual(prop, BAD_CAST "system")) {\r
-                prefer = XML_CATA_PREFER_SYSTEM;\r
-            } else if (xmlStrEqual(prop, BAD_CAST "public")) {\r
-                prefer = XML_CATA_PREFER_PUBLIC;\r
-            } else {\r
-               xmlCatalogErr(parent, cur, XML_CATALOG_PREFER_VALUE,\r
-                              "Invalid value for prefer: '%s'\n",\r
-                             prop, NULL, NULL);\r
-            }\r
-            xmlFree(prop);\r
-           pref = prefer;\r
-        }\r
-       prop = xmlGetProp(cur, BAD_CAST "id");\r
-       base = xmlGetNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE);\r
-       entry = xmlNewCatalogEntry(XML_CATA_GROUP, prop, base, NULL, pref, cgroup);\r
-       xmlFree(prop);\r
-    } else if (xmlStrEqual(cur->name, BAD_CAST "public")) {\r
-       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC,\r
-               BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer, cgroup);\r
-    } else if (xmlStrEqual(cur->name, BAD_CAST "system")) {\r
-       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM,\r
-               BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer, cgroup);\r
-    } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) {\r
-       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM,\r
-               BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString",\r
-               BAD_CAST "rewritePrefix", prefer, cgroup);\r
-    } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) {\r
-       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC,\r
-               BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString",\r
-               BAD_CAST "catalog", prefer, cgroup);\r
-    } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) {\r
-       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM,\r
-               BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString",\r
-               BAD_CAST "catalog", prefer, cgroup);\r
-    } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {\r
-       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,\r
-               BAD_CAST "uri", BAD_CAST "name",\r
-               BAD_CAST "uri", prefer, cgroup);\r
-    } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) {\r
-       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI,\r
-               BAD_CAST "rewriteURI", BAD_CAST "uriStartString",\r
-               BAD_CAST "rewritePrefix", prefer, cgroup);\r
-    } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) {\r
-       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI,\r
-               BAD_CAST "delegateURI", BAD_CAST "uriStartString",\r
-               BAD_CAST "catalog", prefer, cgroup);\r
-    } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {\r
-       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,\r
-               BAD_CAST "nextCatalog", NULL,\r
-               BAD_CAST "catalog", prefer, cgroup);\r
-    }\r
-    if (entry != NULL) {\r
-        if (parent != NULL) {\r
-           entry->parent = parent;\r
-           if (parent->children == NULL)\r
-               parent->children = entry;\r
-           else {\r
-               xmlCatalogEntryPtr prev;\r
-\r
-               prev = parent->children;\r
-               while (prev->next != NULL)\r
-                   prev = prev->next;\r
-               prev->next = entry;\r
-           }\r
-       }\r
-       if (entry->type == XML_CATA_GROUP) {\r
-           /*\r
-            * Recurse to propagate prefer to the subtree\r
-            * (xml:base handling is automated)\r
-            */\r
-            xmlParseXMLCatalogNodeList(cur->children, prefer, parent, entry);\r
-       }\r
-    }\r
-    if (base != NULL)\r
-       xmlFree(base);\r
-    if (uri != NULL)\r
-       xmlFree(uri);\r
-    if (URL != NULL)\r
-       xmlFree(URL);\r
-}\r
-\r
-/**\r
- * xmlParseXMLCatalogNodeList:\r
- * @cur:  the XML node list of siblings\r
- * @prefer:  the PUBLIC vs. SYSTEM current preference value\r
- * @parent:  the parent Catalog entry\r
- * @cgroup:  the group which includes this list\r
- *\r
- * Examines a list of XML sibling nodes of a catalog and build\r
- * a list of Catalog entry from it adding it to the parent.\r
- * The examination will recurse to examine node subtrees.\r
- */\r
-static void\r
-xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,\r
-                          xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup) {\r
-    while (cur != NULL) {\r
-       if ((cur->ns != NULL) && (cur->ns->href != NULL) &&\r
-           (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {\r
-           xmlParseXMLCatalogNode(cur, prefer, parent, cgroup);\r
-       }\r
-       cur = cur->next;\r
-    }\r
-    /* TODO: sort the list according to REWRITE lengths and prefer value */\r
-}\r
-\r
-/**\r
- * xmlParseXMLCatalogFile:\r
- * @prefer:  the PUBLIC vs. SYSTEM current preference value\r
- * @filename:  the filename for the catalog\r
- *\r
- * Parses the catalog file to extract the XML tree and then analyze the\r
- * tree to build a list of Catalog entries corresponding to this catalog\r
- * \r
- * Returns the resulting Catalog entries list\r
- */\r
-static xmlCatalogEntryPtr\r
-xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) {\r
-    xmlDocPtr doc;\r
-    xmlNodePtr cur;\r
-    xmlChar *prop;\r
-    xmlCatalogEntryPtr parent = NULL;\r
-\r
-    if (filename == NULL)\r
-        return(NULL);\r
-\r
-    doc = xmlParseCatalogFile((const char *) filename);\r
-    if (doc == NULL) {\r
-       if (xmlDebugCatalogs)\r
-           xmlGenericError(xmlGenericErrorContext,\r
-                   "Failed to parse catalog %s\n", filename);\r
-       return(NULL);\r
-    }\r
-\r
-    if (xmlDebugCatalogs)\r
-       xmlGenericError(xmlGenericErrorContext,\r
-               "%d Parsing catalog %s\n", xmlGetThreadId(), filename);\r
-\r
-    cur = xmlDocGetRootElement(doc);\r
-    if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) &&\r
-       (cur->ns != NULL) && (cur->ns->href != NULL) &&\r
-       (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {\r
-\r
-       parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,\r
-                                   (const xmlChar *)filename, NULL, prefer, NULL);\r
-        if (parent == NULL) {\r
-           xmlFreeDoc(doc);\r
-           return(NULL);\r
-       }\r
-\r
-       prop = xmlGetProp(cur, BAD_CAST "prefer");\r
-       if (prop != NULL) {\r
-           if (xmlStrEqual(prop, BAD_CAST "system")) {\r
-               prefer = XML_CATA_PREFER_SYSTEM;\r
-           } else if (xmlStrEqual(prop, BAD_CAST "public")) {\r
-               prefer = XML_CATA_PREFER_PUBLIC;\r
-           } else {\r
-               xmlCatalogErr(NULL, cur, XML_CATALOG_PREFER_VALUE,\r
-                             "Invalid value for prefer: '%s'\n",\r
-                             prop, NULL, NULL);\r
-           }\r
-           xmlFree(prop);\r
-       }\r
-       cur = cur->children;\r
-       xmlParseXMLCatalogNodeList(cur, prefer, parent, NULL);\r
-    } else {\r
-       xmlCatalogErr(NULL, (xmlNodePtr) doc, XML_CATALOG_NOT_CATALOG,\r
-                     "File %s is not an XML Catalog\n",\r
-                     filename, NULL, NULL);\r
-       xmlFreeDoc(doc);\r
-       return(NULL);\r
-    }\r
-    xmlFreeDoc(doc);\r
-    return(parent);\r
-}\r
-\r
-/**\r
- * xmlFetchXMLCatalogFile:\r
- * @catal:  an existing but incomplete catalog entry\r
- *\r
- * Fetch and parse the subcatalog referenced by an entry\r
- * \r
- * Returns 0 in case of success, -1 otherwise\r
- */\r
-static int\r
-xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) {\r
-    xmlCatalogEntryPtr doc;\r
-\r
-    if (catal == NULL) \r
-       return(-1);\r
-    if (catal->URL == NULL)\r
-       return(-1);\r
-    if (catal->children != NULL)\r
-       return(-1);\r
-\r
-    /*\r
-     * lock the whole catalog for modification\r
-     */\r
-    xmlRMutexLock(xmlCatalogMutex);\r
-    if (catal->children != NULL) {\r
-       /* Okay someone else did it in the meantime */\r
-       xmlRMutexUnlock(xmlCatalogMutex);\r
-       return(0);\r
-    }\r
-\r
-    if (xmlCatalogXMLFiles != NULL) {\r
-       doc = (xmlCatalogEntryPtr)\r
-           xmlHashLookup(xmlCatalogXMLFiles, catal->URL);\r
-       if (doc != NULL) {\r
-           if (xmlDebugCatalogs)\r
-               xmlGenericError(xmlGenericErrorContext,\r
-                   "Found %s in file hash\n", catal->URL);\r
-\r
-           if (catal->type == XML_CATA_CATALOG)\r
-               catal->children = doc->children;\r
-           else\r
-               catal->children = doc;\r
-           catal->dealloc = 0;\r
-           xmlRMutexUnlock(xmlCatalogMutex);\r
-           return(0);\r
-       }\r
-       if (xmlDebugCatalogs)\r
-           xmlGenericError(xmlGenericErrorContext,\r
-               "%s not found in file hash\n", catal->URL);\r
-    }\r
-\r
-    /*\r
-     * Fetch and parse. Note that xmlParseXMLCatalogFile does not\r
-     * use the existing catalog, there is no recursion allowed at\r
-     * that level.\r
-     */\r
-    doc = xmlParseXMLCatalogFile(catal->prefer, catal->URL);\r
-    if (doc == NULL) {\r
-       catal->type = XML_CATA_BROKEN_CATALOG;\r
-       xmlRMutexUnlock(xmlCatalogMutex);\r
-       return(-1);\r
-    }\r
-\r
-    if (catal->type == XML_CATA_CATALOG)\r
-       catal->children = doc->children;\r
-    else\r
-       catal->children = doc;\r
-\r
-    doc->dealloc = 1;\r
-\r
-    if (xmlCatalogXMLFiles == NULL)\r
-       xmlCatalogXMLFiles = xmlHashCreate(10);\r
-    if (xmlCatalogXMLFiles != NULL) {\r
-       if (xmlDebugCatalogs)\r
-           xmlGenericError(xmlGenericErrorContext,\r
-               "%s added to file hash\n", catal->URL);\r
-       xmlHashAddEntry(xmlCatalogXMLFiles, catal->URL, doc);\r
-    }\r
-    xmlRMutexUnlock(xmlCatalogMutex);\r
-    return(0);\r
-}\r
-\r
-/************************************************************************\r
- *                                                                     *\r
- *                     XML Catalog handling                            *\r
- *                                                                     *\r
- ************************************************************************/\r
-\r
-/**\r
- * xmlAddXMLCatalog:\r
- * @catal:  top of an XML catalog\r
- * @type:  the type of record to add to the catalog\r
- * @orig:  the system, public or prefix to match (or NULL)\r
- * @replace:  the replacement value for the match\r
- *\r
- * Add an entry in the XML catalog, it may overwrite existing but\r
- * different entries.\r
- *\r
- * Returns 0 if successful, -1 otherwise\r
- */\r
-static int\r
-xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type,\r
-             const xmlChar *orig, const xmlChar *replace) {\r
-    xmlCatalogEntryPtr cur;\r
-    xmlCatalogEntryType typ;\r
-    int doregister = 0;\r
-\r
-    if ((catal == NULL) || \r
-       ((catal->type != XML_CATA_CATALOG) &&\r
-        (catal->type != XML_CATA_BROKEN_CATALOG)))\r
-       return(-1);\r
-    if (catal->children == NULL) {\r
-       xmlFetchXMLCatalogFile(catal);\r
-    }\r
-    if (catal->children == NULL)\r
-       doregister = 1;\r
-\r
-    typ = xmlGetXMLCatalogEntryType(type);\r
-    if (typ == XML_CATA_NONE) {\r
-       if (xmlDebugCatalogs)\r
-           xmlGenericError(xmlGenericErrorContext,\r
-                   "Failed to add unknown element %s to catalog\n", type);\r
-       return(-1);\r
-    }\r
-\r
-    cur = catal->children;\r
-    /*\r
-     * Might be a simple "update in place"\r
-     */\r
-    if (cur != NULL) {\r
-       while (cur != NULL) {\r
-           if ((orig != NULL) && (cur->type == typ) &&\r
-               (xmlStrEqual(orig, cur->name))) {\r
-               if (xmlDebugCatalogs)\r
-                   xmlGenericError(xmlGenericErrorContext,\r
-                           "Updating element %s to catalog\n", type);\r
-               if (cur->value != NULL)\r
-                   xmlFree(cur->value);\r
-               if (cur->URL != NULL)\r
-                   xmlFree(cur->URL);\r
-               cur->value = xmlStrdup(replace);\r
-               cur->URL = xmlStrdup(replace);\r
-               return(0);\r
-           }\r
-           if (cur->next == NULL)\r
-               break;\r
-           cur = cur->next;\r
-       }\r
-    }\r
-    if (xmlDebugCatalogs)\r
-       xmlGenericError(xmlGenericErrorContext,\r
-               "Adding element %s to catalog\n", type);\r
-    if (cur == NULL)\r
-       catal->children = xmlNewCatalogEntry(typ, orig, replace,\r
-                                            NULL, catal->prefer, NULL);\r
-    else\r
-       cur->next = xmlNewCatalogEntry(typ, orig, replace,\r
-                                      NULL, catal->prefer, NULL);\r
-    if (doregister) {\r
-       cur = xmlHashLookup(xmlCatalogXMLFiles, catal->URL);\r
-       if (cur != NULL)\r
-           cur->children = catal->children;\r
-    }\r
-\r
-    return(0);\r
-}\r
-\r
-/**\r
- * xmlDelXMLCatalog:\r
- * @catal:  top of an XML catalog\r
- * @value:  the value to remove from the catalog\r
- *\r
- * Remove entries in the XML catalog where the value or the URI\r
- * is equal to @value\r
- *\r
- * Returns the number of entries removed if successful, -1 otherwise\r
- */\r
-static int\r
-xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) {\r
-    xmlCatalogEntryPtr cur;\r
-    int ret = 0;\r
-\r
-    if ((catal == NULL) || \r
-       ((catal->type != XML_CATA_CATALOG) &&\r
-        (catal->type != XML_CATA_BROKEN_CATALOG)))\r
-       return(-1);\r
-    if (value == NULL)\r
-       return(-1);\r
-    if (catal->children == NULL) {\r
-       xmlFetchXMLCatalogFile(catal);\r
-    }\r
-\r
-    /*\r
-     * Scan the children\r
-     */\r
-    cur = catal->children;\r
-    while (cur != NULL) {\r
-       if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) ||\r
-           (xmlStrEqual(value, cur->value))) {\r
-           if (xmlDebugCatalogs) {\r
-               if (cur->name != NULL)\r
-                   xmlGenericError(xmlGenericErrorContext,\r
-                           "Removing element %s from catalog\n", cur->name);\r
-               else\r
-                   xmlGenericError(xmlGenericErrorContext,\r
-                           "Removing element %s from catalog\n", cur->value);\r
-           }\r
-           cur->type = XML_CATA_REMOVED;\r
-       }\r
-       cur = cur->next;\r
-    }\r
-    return(ret);\r
-}\r
-\r
-/**\r
- * xmlCatalogXMLResolve:\r
- * @catal:  a catalog list\r
- * @pubID:  the public ID string\r
- * @sysID:  the system ID string\r
- *\r
- * Do a complete resolution lookup of an External Identifier for a\r
- * list of catalog entries.\r
- *\r
- * Implements (or tries to) 7.1. External Identifier Resolution\r
- * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html\r
- *\r
- * Returns the URI of the resource or NULL if not found\r
- */\r
-static xmlChar *\r
-xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,\r
-                     const xmlChar *sysID) {\r
-    xmlChar *ret = NULL;\r
-    xmlCatalogEntryPtr cur;\r
-    int haveDelegate = 0;\r
-    int haveNext = 0;\r
-\r
-    /*\r
-     * protection against loops\r
-     */\r
-    if (catal->depth > MAX_CATAL_DEPTH) {\r
-       xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,\r
-                     "Detected recursion in catalog %s\n",\r
-                     catal->name, NULL, NULL);\r
-       return(NULL);\r
-    }\r
-    catal->depth++;\r
-\r
-    /*\r
-     * First tries steps 2/ 3/ 4/ if a system ID is provided.\r
-     */\r
-    if (sysID != NULL) {\r
-       xmlCatalogEntryPtr rewrite = NULL;\r
-       int lenrewrite = 0, len;\r
-       cur = catal;\r
-       haveDelegate = 0;\r
-       while (cur != NULL) {\r
-           switch (cur->type) {\r
-               case XML_CATA_SYSTEM:\r
-                   if (xmlStrEqual(sysID, cur->name)) {\r
-                       if (xmlDebugCatalogs)\r
-                           xmlGenericError(xmlGenericErrorContext,\r
-                                   "Found system match %s\n", cur->name);\r
-                       catal->depth--;\r
-                       return(xmlStrdup(cur->URL));\r
-                   }\r
-                   break;\r
-               case XML_CATA_REWRITE_SYSTEM:\r
-                   len = xmlStrlen(cur->name);\r
-                   if ((len > lenrewrite) &&\r
-                       (!xmlStrncmp(sysID, cur->name, len))) {\r
-                       lenrewrite = len;\r
-                       rewrite = cur;\r
-                   }\r
-                   break;\r
-               case XML_CATA_DELEGATE_SYSTEM:\r
-                   if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))\r
-                       haveDelegate++;\r
-                   break;\r
-               case XML_CATA_NEXT_CATALOG:\r
-                   haveNext++;\r
-                   break;\r
-               default:\r
-                   break;\r
-           }\r
-           cur = cur->next;\r
-       }\r
-       if (rewrite != NULL) {\r
-           if (xmlDebugCatalogs)\r
-               xmlGenericError(xmlGenericErrorContext,\r
-                       "Using rewriting rule %s\n", rewrite->name);\r
-           ret = xmlStrdup(rewrite->URL);\r
-           if (ret != NULL)\r
-               ret = xmlStrcat(ret, &sysID[lenrewrite]);\r
-           catal->depth--;\r
-           return(ret);\r
-       }\r
-       if (haveDelegate) {\r
-           const xmlChar *delegates[MAX_DELEGATE];\r
-           int nbList = 0, i;\r
-\r
-           /*\r
-            * Assume the entries have been sorted by decreasing substring\r
-            * matches when the list was produced.\r
-            */\r
-           cur = catal;\r
-           while (cur != NULL) {\r
-               if ((cur->type == XML_CATA_DELEGATE_SYSTEM) &&\r
-                   (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) {\r
-                   for (i = 0;i < nbList;i++)\r
-                       if (xmlStrEqual(cur->URL, delegates[i]))\r
-                           break;\r
-                   if (i < nbList) {\r
-                       cur = cur->next;\r
-                       continue;\r
-                   }\r
-                   if (nbList < MAX_DELEGATE)\r
-                       delegates[nbList++] = cur->URL;\r
-\r
-                   if (cur->children == NULL) {\r
-                       xmlFetchXMLCatalogFile(cur);\r
-                   }\r
-                   if (cur->children != NULL) {\r
-                       if (xmlDebugCatalogs)\r
-                           xmlGenericError(xmlGenericErrorContext,\r
-                                   "Trying system delegate %s\n", cur->URL);\r
-                       ret = xmlCatalogListXMLResolve(\r
-                               cur->children, NULL, sysID);\r
-                       if (ret != NULL) {\r
-                           catal->depth--;\r
-                           return(ret);\r
-                       }\r
-                   }\r
-               }\r
-               cur = cur->next;\r
-           }\r
-           /*\r
-            * Apply the cut algorithm explained in 4/\r
-            */\r
-           catal->depth--;\r
-           return(XML_CATAL_BREAK);\r
-       }\r
-    }\r
-    /*\r
-     * Then tries 5/ 6/ if a public ID is provided\r
-     */\r
-    if (pubID != NULL) {\r
-       cur = catal;\r
-       haveDelegate = 0;\r
-       while (cur != NULL) {\r
-           switch (cur->type) {\r
-               case XML_CATA_PUBLIC:\r
-                   if (xmlStrEqual(pubID, cur->name)) {\r
-                       if (xmlDebugCatalogs)\r
-                           xmlGenericError(xmlGenericErrorContext,\r
-                                   "Found public match %s\n", cur->name);\r
-                       catal->depth--;\r
-                       return(xmlStrdup(cur->URL));\r
-                   }\r
-                   break;\r
-               case XML_CATA_DELEGATE_PUBLIC:\r
-                   if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) &&\r
-                       (cur->prefer == XML_CATA_PREFER_PUBLIC))\r
-                       haveDelegate++;\r
-                   break;\r
-               case XML_CATA_NEXT_CATALOG:\r
-                   if (sysID == NULL)\r
-                       haveNext++;\r
-                   break;\r
-               default:\r
-                   break;\r
-           }\r
-           cur = cur->next;\r
-       }\r
-       if (haveDelegate) {\r
-           const xmlChar *delegates[MAX_DELEGATE];\r
-           int nbList = 0, i;\r
-\r
-           /*\r
-            * Assume the entries have been sorted by decreasing substring\r
-            * matches when the list was produced.\r
-            */\r
-           cur = catal;\r
-           while (cur != NULL) {\r
-               if ((cur->type == XML_CATA_DELEGATE_PUBLIC) &&\r
-                   (cur->prefer == XML_CATA_PREFER_PUBLIC) &&\r
-                   (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) {\r
-\r
-                   for (i = 0;i < nbList;i++)\r
-                       if (xmlStrEqual(cur->URL, delegates[i]))\r
-                           break;\r
-                   if (i < nbList) {\r
-                       cur = cur->next;\r
-                       continue;\r
-                   }\r
-                   if (nbList < MAX_DELEGATE)\r
-                       delegates[nbList++] = cur->URL;\r
-                           \r
-                   if (cur->children == NULL) {\r
-                       xmlFetchXMLCatalogFile(cur);\r
-                   }\r
-                   if (cur->children != NULL) {\r
-                       if (xmlDebugCatalogs)\r
-                           xmlGenericError(xmlGenericErrorContext,\r
-                                   "Trying public delegate %s\n", cur->URL);\r
-                       ret = xmlCatalogListXMLResolve(\r
-                               cur->children, pubID, NULL);\r
-                       if (ret != NULL) {\r
-                           catal->depth--;\r
-                           return(ret);\r
-                       }\r
-                   }\r
-               }\r
-               cur = cur->next;\r
-           }\r
-           /*\r
-            * Apply the cut algorithm explained in 4/\r
-            */\r
-           catal->depth--;\r
-           return(XML_CATAL_BREAK);\r
-       }\r
-    }\r
-    if (haveNext) {\r
-       cur = catal;\r
-       while (cur != NULL) {\r
-           if (cur->type == XML_CATA_NEXT_CATALOG) {\r
-               if (cur->children == NULL) {\r
-                   xmlFetchXMLCatalogFile(cur);\r
-               }\r
-               if (cur->children != NULL) {\r
-                   ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID);\r
-                   if (ret != NULL) {\r
-                       catal->depth--;\r
-                       return(ret);\r
-                   }\r
-               }\r
-           }\r
-           cur = cur->next;\r
-       }\r
-    }\r
-\r
-    catal->depth--;\r
-    return(NULL);\r
-}\r
-\r
-/**\r
- * xmlCatalogXMLResolveURI:\r
- * @catal:  a catalog list\r
- * @URI:  the URI\r
- * @sysID:  the system ID string\r
- *\r
- * Do a complete resolution lookup of an External Identifier for a\r
- * list of catalog entries.\r
- *\r
- * Implements (or tries to) 7.2.2. URI Resolution\r
- * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html\r
- *\r
- * Returns the URI of the resource or NULL if not found\r
- */\r
-static xmlChar *\r
-xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {\r
-    xmlChar *ret = NULL;\r
-    xmlCatalogEntryPtr cur;\r
-    int haveDelegate = 0;\r
-    int haveNext = 0;\r
-    xmlCatalogEntryPtr rewrite = NULL;\r
-    int lenrewrite = 0, len;\r
-\r
-    if (catal == NULL)\r
-       return(NULL);\r
-\r
-    if (URI == NULL)\r
-       return(NULL);\r
-\r
-    /*\r
-     * First tries steps 2/ 3/ 4/ if a system ID is provided.\r
-     */\r
-    cur = catal;\r
-    haveDelegate = 0;\r
-    while (cur != NULL) {\r
-       switch (cur->type) {\r
-           case XML_CATA_URI:\r
-               if (xmlStrEqual(URI, cur->name)) {\r
-                   if (xmlDebugCatalogs)\r
-                       xmlGenericError(xmlGenericErrorContext,\r
-                               "Found URI match %s\n", cur->name);\r
-                   return(xmlStrdup(cur->URL));\r
-               }\r
-               break;\r
-           case XML_CATA_REWRITE_URI:\r
-               len = xmlStrlen(cur->name);\r
-               if ((len > lenrewrite) &&\r
-                   (!xmlStrncmp(URI, cur->name, len))) {\r
-                   lenrewrite = len;\r
-                   rewrite = cur;\r
-               }\r
-               break;\r
-           case XML_CATA_DELEGATE_URI:\r
-               if (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))\r
-                   haveDelegate++;\r
-               break;\r
-           case XML_CATA_NEXT_CATALOG:\r
-               haveNext++;\r
-               break;\r
-           default:\r
-               break;\r
-       }\r
-       cur = cur->next;\r
-    }\r
-    if (rewrite != NULL) {\r
-       if (xmlDebugCatalogs)\r
-           xmlGenericError(xmlGenericErrorContext,\r
-                   "Using rewriting rule %s\n", rewrite->name);\r
-       ret = xmlStrdup(rewrite->URL);\r
-       if (ret != NULL)\r
-           ret = xmlStrcat(ret, &URI[lenrewrite]);\r
-       return(ret);\r
-    }\r
-    if (haveDelegate) {\r
-       const xmlChar *delegates[MAX_DELEGATE];\r
-       int nbList = 0, i;\r
-\r
-       /*\r
-        * Assume the entries have been sorted by decreasing substring\r
-        * matches when the list was produced.\r
-        */\r
-       cur = catal;\r
-       while (cur != NULL) {\r
-           if (((cur->type == XML_CATA_DELEGATE_SYSTEM) ||\r
-                (cur->type == XML_CATA_DELEGATE_URI)) &&\r
-               (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) {\r
-               for (i = 0;i < nbList;i++)\r
-                   if (xmlStrEqual(cur->URL, delegates[i]))\r
-                       break;\r
-               if (i < nbList) {\r
-                   cur = cur->next;\r
-                   continue;\r
-               }\r
-               if (nbList < MAX_DELEGATE)\r
-                   delegates[nbList++] = cur->URL;\r
-\r
-               if (cur->children == NULL) {\r
-                   xmlFetchXMLCatalogFile(cur);\r
-               }\r
-               if (cur->children != NULL) {\r
-                   if (xmlDebugCatalogs)\r
-                       xmlGenericError(xmlGenericErrorContext,\r
-                               "Trying URI delegate %s\n", cur->URL);\r
-                   ret = xmlCatalogListXMLResolveURI(\r
-                           cur->children, URI);\r
-                   if (ret != NULL)\r
-                       return(ret);\r
-               }\r
-           }\r
-           cur = cur->next;\r
-       }\r
-       /*\r
-        * Apply the cut algorithm explained in 4/\r
-        */\r
-       return(XML_CATAL_BREAK);\r
-    }\r
-    if (haveNext) {\r
-       cur = catal;\r
-       while (cur != NULL) {\r
-           if (cur->type == XML_CATA_NEXT_CATALOG) {\r
-               if (cur->children == NULL) {\r
-                   xmlFetchXMLCatalogFile(cur);\r
-               }\r
-               if (cur->children != NULL) {\r
-                   ret = xmlCatalogListXMLResolveURI(cur->children, URI);\r
-                   if (ret != NULL)\r
-                       return(ret);\r
-               }\r
-           }\r
-           cur = cur->next;\r
-       }\r
-    }\r
-\r
-    return(NULL);\r
-}\r
-\r
-/**\r
- * xmlCatalogListXMLResolve:\r
- * @catal:  a catalog list\r
- * @pubID:  the public ID string\r
- * @sysID:  the system ID string\r
- *\r
- * Do a complete resolution lookup of an External Identifier for a\r
- * list of catalogs\r
- *\r
- * Implements (or tries to) 7.1. External Identifier Resolution\r
- * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html\r
- *\r
- * Returns the URI of the resource or NULL if not found\r
- */\r
-static xmlChar *\r
-xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,\r
-                     const xmlChar *sysID) {\r
-    xmlChar *ret = NULL;\r
-    xmlChar *urnID = NULL;\r
-    xmlChar *normid;\r
-    \r
-    if (catal == NULL)\r
-        return(NULL);\r
-    if ((pubID == NULL) && (sysID == NULL))\r
-       return(NULL);\r
-\r
-    normid = xmlCatalogNormalizePublic(pubID);\r
-    if (normid != NULL)\r
-        pubID = (*normid != 0 ? normid : NULL);\r
-    \r
-    if (!xmlStrncmp(pubID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {\r
-       urnID = xmlCatalogUnWrapURN(pubID);\r
-       if (xmlDebugCatalogs) {\r
-           if (urnID == NULL)\r
-               xmlGenericError(xmlGenericErrorContext,\r
-                       "Public URN ID %s expanded to NULL\n", pubID);\r
-           else\r
-               xmlGenericError(xmlGenericErrorContext,\r
-                       "Public URN ID expanded to %s\n", urnID);\r
-       }\r
-       ret = xmlCatalogListXMLResolve(catal, urnID, sysID);\r
-       if (urnID != NULL)\r
-           xmlFree(urnID);\r
-       if (normid != NULL)\r
-           xmlFree(normid);\r
-       return(ret);\r
-    }\r
-    if (!xmlStrncmp(sysID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {\r
-       urnID = xmlCatalogUnWrapURN(sysID);\r
-       if (xmlDebugCatalogs) {\r
-           if (urnID == NULL)\r
-               xmlGenericError(xmlGenericErrorContext,\r
-                       "System URN ID %s expanded to NULL\n", sysID);\r
-           else\r
-               xmlGenericError(xmlGenericErrorContext,\r
-                       "System URN ID expanded to %s\n", urnID);\r
-       }\r
-       if (pubID == NULL)\r
-           ret = xmlCatalogListXMLResolve(catal, urnID, NULL);\r
-       else if (xmlStrEqual(pubID, urnID))\r
-           ret = xmlCatalogListXMLResolve(catal, pubID, NULL);\r
-       else {\r
-           ret = xmlCatalogListXMLResolve(catal, pubID, urnID);\r
-       }\r
-       if (urnID != NULL)\r
-           xmlFree(urnID);\r
-       if (normid != NULL)\r
-           xmlFree(normid);\r
-       return(ret);\r
-    }\r
-    while (catal != NULL) {\r
-       if (catal->type == XML_CATA_CATALOG) {\r
-           if (catal->children == NULL) {\r
-               xmlFetchXMLCatalogFile(catal);\r
-           }\r
-           if (catal->children != NULL) {\r
-               ret = xmlCatalogXMLResolve(catal->children, pubID, sysID);\r
-               if (ret != NULL) {\r
-                    if (normid != NULL)\r
-                        xmlFree(normid);\r
-                   return(ret);\r
-                }\r
-           }\r
-       }\r
-       catal = catal->next;\r
-    }\r
-       if (normid != NULL)\r
-           xmlFree(normid);\r
-    return(ret);\r
-}\r
-\r
-/**\r
- * xmlCatalogListXMLResolveURI:\r
- * @catal:  a catalog list\r
- * @URI:  the URI\r
- *\r
- * Do a complete resolution lookup of an URI for a list of catalogs\r
- *\r
- * Implements (or tries to) 7.2. URI Resolution\r
- * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html\r
- *\r
- * Returns the URI of the resource or NULL if not found\r
- */\r
-static xmlChar *\r
-xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {\r
-    xmlChar *ret = NULL;\r
-    xmlChar *urnID = NULL;\r
-    \r
-    if (catal == NULL)\r
-        return(NULL);\r
-    if (URI == NULL)\r
-       return(NULL);\r
-\r
-    if (!xmlStrncmp(URI, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {\r
-       urnID = xmlCatalogUnWrapURN(URI);\r
-       if (xmlDebugCatalogs) {\r
-           if (urnID == NULL)\r
-               xmlGenericError(xmlGenericErrorContext,\r
-                       "URN ID %s expanded to NULL\n", URI);\r
-           else\r
-               xmlGenericError(xmlGenericErrorContext,\r
-                       "URN ID expanded to %s\n", urnID);\r
-       }\r
-       ret = xmlCatalogListXMLResolve(catal, urnID, NULL);\r
-       if (urnID != NULL)\r
-           xmlFree(urnID);\r
-       return(ret);\r
-    }\r
-    while (catal != NULL) {\r
-       if (catal->type == XML_CATA_CATALOG) {\r
-           if (catal->children == NULL) {\r
-               xmlFetchXMLCatalogFile(catal);\r
-           }\r
-           if (catal->children != NULL) {\r
-               ret = xmlCatalogXMLResolveURI(catal->children, URI);\r
-               if (ret != NULL)\r
-                   return(ret);\r
-           }\r
-       }\r
-       catal = catal->next;\r
-    }\r
-    return(ret);\r
-}\r
-\r
-/************************************************************************\r
- *                                                                     *\r
- *                     The SGML Catalog parser                         *\r
- *                                                                     *\r
- ************************************************************************/\r
-\r
-\r
-#define RAW *cur\r
-#define NEXT cur++;\r
-#define SKIP(x) cur += x;\r
-\r
-#define SKIP_BLANKS while (IS_BLANK_CH(*cur)) NEXT;\r
-\r
-/**\r
- * xmlParseSGMLCatalogComment:\r
- * @cur:  the current character\r
- *\r
- * Skip a comment in an SGML catalog\r
- *\r
- * Returns new current character\r
- */\r
-static const xmlChar *\r
-xmlParseSGMLCatalogComment(const xmlChar *cur) {\r
-    if ((cur[0] != '-') || (cur[1] != '-')) \r
-       return(cur);\r
-    SKIP(2);\r
-    while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-'))))\r
-       NEXT;\r
-    if (cur[0] == 0) {\r
-       return(NULL);\r
-    }\r
-    return(cur + 2);\r
-}\r
-\r
-/**\r
- * xmlParseSGMLCatalogPubid:\r
- * @cur:  the current character\r
- * @id:  the return location\r
- *\r
- * Parse an SGML catalog ID\r
- *\r
- * Returns new current character and store the value in @id\r
- */\r
-static const xmlChar *\r
-xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) {\r
-    xmlChar *buf = NULL, *tmp;\r
-    int len = 0;\r
-    int size = 50;\r
-    xmlChar stop;\r
-    int count = 0;\r
-\r
-    *id = NULL;\r
-\r
-    if (RAW == '"') {\r
-        NEXT;\r
-       stop = '"';\r
-    } else if (RAW == '\'') {\r
-        NEXT;\r
-       stop = '\'';\r
-    } else {\r
-       stop = ' ';\r
-    }\r
-    buf = (xmlChar *) xmlMallocAtomic(size * sizeof(xmlChar));\r
-    if (buf == NULL) {\r
-        xmlCatalogErrMemory("allocating public ID");\r
-       return(NULL);\r
-    }\r
-    while (IS_PUBIDCHAR_CH(*cur) || (*cur == '?')) {\r
-       if ((*cur == stop) && (stop != ' '))\r
-           break;\r
-       if ((stop == ' ') && (IS_BLANK_CH(*cur)))\r
-           break;\r
-       if (len + 1 >= size) {\r
-           size *= 2;\r
-           tmp = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));\r
-           if (tmp == NULL) {\r
-               xmlCatalogErrMemory("allocating public ID");\r
-               xmlFree(buf);\r
-               return(NULL);\r
-           }\r
-           buf = tmp;\r
-       }\r
-       buf[len++] = *cur;\r
-       count++;\r
-       NEXT;\r
-    }\r
-    buf[len] = 0;\r
-    if (stop == ' ') {\r
-       if (!IS_BLANK_CH(*cur)) {\r
-           xmlFree(buf);\r
-           return(NULL);\r
-       }\r
-    } else {\r
-       if (*cur != stop) {\r
-           xmlFree(buf);\r
-           return(NULL);\r
-       }\r
-       NEXT;\r
-    }\r
-    *id = buf;\r
-    return(cur);\r
-}\r
-\r
-/**\r
- * xmlParseSGMLCatalogName:\r
- * @cur:  the current character\r
- * @name:  the return location\r
- *\r
- * Parse an SGML catalog name\r
- *\r
- * Returns new current character and store the value in @name\r
- */\r
-static const xmlChar *\r
-xmlParseSGMLCatalogName(const xmlChar *cur, xmlChar **name) {\r
-    xmlChar buf[XML_MAX_NAMELEN + 5];\r
-    int len = 0;\r
-    int c;\r
-\r
-    *name = NULL;\r
-\r
-    /*\r
-     * Handler for more complex cases\r
-     */\r
-    c = *cur;\r
-    if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) {\r
-       return(NULL);\r
-    }\r
-\r
-    while (((IS_LETTER(c)) || (IS_DIGIT(c)) ||\r
-            (c == '.') || (c == '-') ||\r
-           (c == '_') || (c == ':'))) {\r
-       buf[len++] = c;\r
-       cur++;\r
-       c = *cur;\r
-       if (len >= XML_MAX_NAMELEN)\r
-           return(NULL);\r
-    }\r
-    *name = xmlStrndup(buf, len);\r
-    return(cur);\r
-}\r
-\r
-/**\r
- * xmlGetSGMLCatalogEntryType:\r
- * @name:  the entry name\r
- *\r
- * Get the Catalog entry type for a given SGML Catalog name\r
- *\r
- * Returns Catalog entry type\r
- */\r
-static xmlCatalogEntryType\r
-xmlGetSGMLCatalogEntryType(const xmlChar *name) {\r
-    xmlCatalogEntryType type = XML_CATA_NONE;\r
-    if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))\r
-       type = SGML_CATA_SYSTEM;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))\r
-       type = SGML_CATA_PUBLIC;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))\r
-       type = SGML_CATA_DELEGATE;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))\r
-       type = SGML_CATA_ENTITY;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))\r
-       type = SGML_CATA_DOCTYPE;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))\r
-       type = SGML_CATA_LINKTYPE;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))\r
-       type = SGML_CATA_NOTATION;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))\r
-       type = SGML_CATA_SGMLDECL;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))\r
-       type = SGML_CATA_DOCUMENT;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))\r
-       type = SGML_CATA_CATALOG;\r
-    else if (xmlStrEqual(name, (const xmlChar *) "BASE"))\r
-       type = SGML_CATA_BASE;\r
-    return(type);\r
-}\r
-\r
-/**\r
- * xmlParseSGMLCatalog:\r
- * @catal:  the SGML Catalog\r
- * @value:  the content of the SGML Catalog serialization\r
- * @file:  the filepath for the catalog\r
- * @super:  should this be handled as a Super Catalog in which case\r
- *          parsing is not recursive\r
- *\r
- * Parse an SGML catalog content and fill up the @catal hash table with\r
- * the new entries found.\r
- *\r
- * Returns 0 in case of success, -1 in case of error.\r
- */\r
-static int\r
-xmlParseSGMLCatalog(xmlCatalogPtr catal, const xmlChar *value,\r
-                   const char *file, int super) {\r
-    const xmlChar *cur = value;\r
-    xmlChar *base = NULL;\r
-    int res;\r
-\r
-    if ((cur == NULL) || (file == NULL))\r
-        return(-1);\r
-    base = xmlStrdup((const xmlChar *) file);\r
-\r
-    while ((cur != NULL) && (cur[0] != 0)) {\r
-       SKIP_BLANKS;\r
-       if (cur[0] == 0)\r
-           break;\r
-       if ((cur[0] == '-') && (cur[1] == '-')) {\r
-           cur = xmlParseSGMLCatalogComment(cur);\r
-           if (cur == NULL) {\r
-               /* error */\r
-               break;\r
-           }\r
-       } else {\r
-           xmlChar *sysid = NULL;\r
-           xmlChar *name = NULL;\r
-           xmlCatalogEntryType type = XML_CATA_NONE;\r
-\r
-           cur = xmlParseSGMLCatalogName(cur, &name);\r
-           if (name == NULL) {\r
-               /* error */\r
-               break;\r
-           }\r
-           if (!IS_BLANK_CH(*cur)) {\r
-               /* error */\r
-               break;\r
-           }\r
-           SKIP_BLANKS;\r
-           if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))\r
-                type = SGML_CATA_SYSTEM;\r
-           else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))\r
-                type = SGML_CATA_PUBLIC;\r
-           else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))\r
-                type = SGML_CATA_DELEGATE;\r
-           else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))\r
-                type = SGML_CATA_ENTITY;\r
-           else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))\r
-                type = SGML_CATA_DOCTYPE;\r
-           else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))\r
-                type = SGML_CATA_LINKTYPE;\r
-           else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))\r
-                type = SGML_CATA_NOTATION;\r
-           else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))\r
-                type = SGML_CATA_SGMLDECL;\r
-           else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))\r
-                type = SGML_CATA_DOCUMENT;\r
-           else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))\r
-                type = SGML_CATA_CATALOG;\r
-           else if (xmlStrEqual(name, (const xmlChar *) "BASE"))\r
-                type = SGML_CATA_BASE;\r
-           else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {\r
-               xmlFree(name);\r
-               cur = xmlParseSGMLCatalogName(cur, &name);\r
-               if (name == NULL) {\r
-                   /* error */\r
-                   break;\r
-               }\r
-               xmlFree(name);\r
-               continue;\r
-           }\r
-           xmlFree(name);\r
-           name = NULL;\r
-\r
-           switch(type) {\r
-               case SGML_CATA_ENTITY:\r
-                   if (*cur == '%')\r
-                       type = SGML_CATA_PENTITY;\r
-               case SGML_CATA_PENTITY:\r
-               case SGML_CATA_DOCTYPE:\r
-               case SGML_CATA_LINKTYPE:\r
-               case SGML_CATA_NOTATION:\r
-                   cur = xmlParseSGMLCatalogName(cur, &name);\r
-                   if (cur == NULL) {\r
-                       /* error */\r
-                       break;\r
-                   }\r
-                   if (!IS_BLANK_CH(*cur)) {\r
-                       /* error */\r
-                       break;\r
-                   }\r
-                   SKIP_BLANKS;\r
-                   cur = xmlParseSGMLCatalogPubid(cur, &sysid);\r
-                   if (cur == NULL) {\r
-                       /* error */\r
-                       break;\r
-                   }\r
-                   break;\r
-               case SGML_CATA_PUBLIC:\r
-               case SGML_CATA_SYSTEM:\r
-               case SGML_CATA_DELEGATE:\r
-                   cur = xmlParseSGMLCatalogPubid(cur, &name);\r
-                   if (cur == NULL) {\r
-                       /* error */\r
-                       break;\r
-                   }\r
-                   if (type != SGML_CATA_SYSTEM) {\r
-                       xmlChar *normid;\r
-\r
-                       normid = xmlCatalogNormalizePublic(name);\r
-                       if (normid != NULL) {\r
-                           if (name != NULL)\r
-                               xmlFree(name);\r
-                           if (*normid != 0)\r
-                               name = normid;\r
-                           else {\r
-                               xmlFree(normid);\r
-                               name = NULL;\r
-                           }\r
-                       }\r
-                   }\r
-                   if (!IS_BLANK_CH(*cur)) {\r
-                       /* error */\r
-                       break;\r
-                   }\r
-                   SKIP_BLANKS;\r
-                   cur = xmlParseSGMLCatalogPubid(cur, &sysid);\r
-                   if (cur == NULL) {\r
-                       /* error */\r
-                       break;\r
-                   }\r
-                   break;\r
-               case SGML_CATA_BASE:\r
-               case SGML_CATA_CATALOG:\r
-               case SGML_CATA_DOCUMENT:\r
-               case SGML_CATA_SGMLDECL:\r
-                   cur = xmlParseSGMLCatalogPubid(cur, &sysid);\r
-                   if (cur == NULL) {\r
-                       /* error */\r
-                       break;\r
-                   }\r
-                   break;\r
-               default:\r
-                   break;\r
-           }\r
-           if (cur == NULL) {\r
-               if (name != NULL)\r
-                   xmlFree(name);\r
-               if (sysid != NULL)\r
-                   xmlFree(sysid);\r
-               break;\r
-           } else if (type == SGML_CATA_BASE) {\r
-               if (base != NULL)\r
-                   xmlFree(base);\r
-               base = xmlStrdup(sysid);\r
-           } else if ((type == SGML_CATA_PUBLIC) ||\r
-                      (type == SGML_CATA_SYSTEM)) {\r
-               xmlChar *filename;\r
-\r
-               filename = xmlBuildURI(sysid, base);\r
-               if (filename != NULL) {\r
-                   xmlCatalogEntryPtr entry;\r
-\r
-                   entry = xmlNewCatalogEntry(type, name, filename,\r
-                                              NULL, XML_CATA_PREFER_NONE, NULL);\r
-                   res = xmlHashAddEntry(catal->sgml, name, entry);\r
-                   if (res < 0) {\r
-                       xmlFreeCatalogEntry(entry);\r
-                   }\r
-                   xmlFree(filename);\r
-               }\r
-\r
-           } else if (type == SGML_CATA_CATALOG) {\r
-               if (super) {\r
-                   xmlCatalogEntryPtr entry;\r
-\r
-                   entry = xmlNewCatalogEntry(type, sysid, NULL, NULL,\r
-                                              XML_CATA_PREFER_NONE, NULL);\r
-                   res = xmlHashAddEntry(catal->sgml, sysid, entry);\r
-                   if (res < 0) {\r
-                       xmlFreeCatalogEntry(entry);\r
-                   }\r
-               } else {\r
-                   xmlChar *filename;\r
-\r
-                   filename = xmlBuildURI(sysid, base);\r
-                   if (filename != NULL) {\r
-                       xmlExpandCatalog(catal, (const char *)filename);\r
-                       xmlFree(filename);\r
-                   }\r
-               }\r
-           }\r
-           /*\r
-            * drop anything else we won't handle it\r
-            */\r
-           if (name != NULL)\r
-               xmlFree(name);\r
-           if (sysid != NULL)\r
-               xmlFree(sysid);\r
-       }\r
-    }\r
-    if (base != NULL)\r
-       xmlFree(base);\r
-    if (cur == NULL)\r
-       return(-1);\r
-    return(0);\r
-}\r
-\r
-/************************************************************************\r
- *                                                                     *\r
- *                     SGML Catalog handling                           *\r
- *                                                                     *\r
- ************************************************************************/\r
-\r
-/**\r
- * xmlCatalogGetSGMLPublic:\r
- * @catal:  an SGML catalog hash\r
- * @pubID:  the public ID string\r
- *\r
- * Try to lookup the catalog local reference associated to a public ID\r
- *\r
- * Returns the local resource if found or NULL otherwise.\r
- */\r
-static const xmlChar *\r
-xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) {\r
-    xmlCatalogEntryPtr entry;\r
-    xmlChar *normid;\r
-\r
-    if (catal == NULL)\r
-       return(NULL);\r
-\r
-    normid = xmlCatalogNormalizePublic(pubID);\r
-    if (normid != NULL)\r
-        pubID = (*normid != 0 ? normid : NULL);\r
-\r
-    entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID);\r
-    if (entry == NULL) {\r
-       if (normid != NULL)\r
-           xmlFree(normid);\r
-       return(NULL);\r
-    }\r
-    if (entry->type == SGML_CATA_PUBLIC) {\r
-       if (normid != NULL)\r
-           xmlFree(normid);\r
-       return(entry->URL);\r
-    }\r
-    if (normid != NULL)\r
-        xmlFree(normid);\r
-    return(NULL);\r
-}\r
-\r
-/**\r
- * xmlCatalogGetSGMLSystem:\r
- * @catal:  an SGML catalog hash\r
- * @sysID:  the system ID string\r
- *\r
- * Try to lookup the catalog local reference for a system ID\r
- *\r
- * Returns the local resource if found or NULL otherwise.\r
- */\r
-static const xmlChar *\r
-xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) {\r
-    xmlCatalogEntryPtr entry;\r
-\r
-    if (catal == NULL)\r
-       return(NULL);\r
-\r
-    entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID);\r
-    if (entry == NULL)\r
-       return(NULL);\r
-    if (entry->type == SGML_CATA_SYSTEM)\r
-       return(entry->URL);\r
-    return(NULL);\r
-}\r
-\r
-/**\r
- * xmlCatalogSGMLResolve:\r
- * @catal:  the SGML catalog\r
- * @pubID:  the public ID string\r
- * @sysID:  the system ID string\r
- *\r
- * Do a complete resolution lookup of an External Identifier\r
- *\r
- * Returns the URI of the resource or NULL if not found\r
- */\r
-static const xmlChar *\r
-xmlCatalogSGMLResolve(xmlCatalogPtr catal, const xmlChar *pubID,\r
-                     const xmlChar *sysID) {\r
-    const xmlChar *ret = NULL;\r
-\r
-    if (catal->sgml == NULL)\r
-       return(NULL);\r
-\r
-    if (pubID != NULL)\r
-       ret = xmlCatalogGetSGMLPublic(catal->sgml, pubID);\r
-    if (ret != NULL)\r
-       return(ret);\r
-    if (sysID != NULL)\r
-       ret = xmlCatalogGetSGMLSystem(catal->sgml, sysID);\r
-    return(NULL);\r
-}\r
-\r
-/************************************************************************\r
- *                                                                     *\r
- *                     Specific Public interfaces                      *\r
- *                                                                     *\r
- ************************************************************************/\r
-\r
-/**\r
- * xmlLoadSGMLSuperCatalog:\r
- * @filename:  a file path\r
- *\r
- * Load an SGML super catalog. It won't expand CATALOG or DELEGATE\r
- * references. This is only needed for manipulating SGML Super Catalogs\r
- * like adding and removing CATALOG or DELEGATE entries.\r
- *\r
- * Returns the catalog parsed or NULL in case of error\r
- */\r
-xmlCatalogPtr\r
-xmlLoadSGMLSuperCatalog(const char *filename)\r
-{\r
-    xmlChar *content;\r
-    xmlCatalogPtr catal;\r
-    int ret;\r
-\r
-    content = xmlLoadFileContent(filename);\r
-    if (content == NULL)\r
-        return(NULL);\r
-\r
-    catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);\r
-    if (catal == NULL) {\r
-       xmlFree(content);\r
-       return(NULL);\r
-    }\r
-\r
-    ret = xmlParseSGMLCatalog(catal, content, filename, 1);\r
-    xmlFree(content);\r
-    if (ret < 0) {\r
-       xmlFreeCatalog(catal);\r
-       return(NULL);\r
-    }\r
-    return (catal);\r
-}\r
-\r
-/**\r
- * xmlLoadACatalog:\r
- * @filename:  a file path\r
- *\r
- * Load the catalog and build the associated data structures.\r
- * This can be either an XML Catalog or an SGML Catalog\r
- * It will recurse in SGML CATALOG entries. On the other hand XML\r
- * Catalogs are not handled recursively.\r
- *\r
- * Returns the catalog parsed or NULL in case of error\r
- */\r
-xmlCatalogPtr\r
-xmlLoadACatalog(const char *filename)\r
-{\r
-    xmlChar *content;\r
-    xmlChar *first;\r
-    xmlCatalogPtr catal;\r
-    int ret;\r
-\r
-    content = xmlLoadFileContent(filename);\r
-    if (content == NULL)\r
-        return(NULL);\r
-\r
-\r
-    first = content;\r
-   \r
-    while ((*first != 0) && (*first != '-') && (*first != '<') &&\r
-          (!(((*first >= 'A') && (*first <= 'Z')) ||\r
-             ((*first >= 'a') && (*first <= 'z')))))\r
-       first++;\r
-\r
-    if (*first != '<') {\r
-       catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);\r
-       if (catal == NULL) {\r
-           xmlFree(content);\r
-           return(NULL);\r
-       }\r
-        ret = xmlParseSGMLCatalog(catal, content, filename, 0);\r
-       if (ret < 0) {\r
-           xmlFreeCatalog(catal);\r
-           xmlFree(content);\r
-           return(NULL);\r
-       }\r
-    } else {\r
-       catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, xmlCatalogDefaultPrefer);\r
-       if (catal == NULL) {\r
-           xmlFree(content);\r
-           return(NULL);\r
-       }\r
-        catal->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,\r
-                      NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);\r
-    }\r
-    xmlFree(content);\r
-    return (catal);\r
-}\r
-\r
-/**\r
- * xmlExpandCatalog:\r
- * @catal:  a catalog\r
- * @filename:  a file path\r
- *\r
- * Load the catalog and expand the existing catal structure.\r
- * This can be either an XML Catalog or an SGML Catalog\r
- *\r
- * Returns 0 in case of success, -1 in case of error\r
- */\r
-static int\r
-xmlExpandCatalog(xmlCatalogPtr catal, const char *filename)\r
-{\r
-    int ret;\r
-\r
-    if ((catal == NULL) || (filename == NULL))\r
-       return(-1);\r
-\r
-\r
-    if (catal->type == XML_SGML_CATALOG_TYPE) {\r
-       xmlChar *content;\r
-\r
-       content = xmlLoadFileContent(filename);\r
-       if (content == NULL)\r
-           return(-1);\r
-\r
-        ret = xmlParseSGMLCatalog(catal, content, filename, 0);\r
-       if (ret < 0) {\r
-           xmlFree(content);\r
-           return(-1);\r
-       }\r
-       xmlFree(content);\r
-    } else {\r
-       xmlCatalogEntryPtr tmp, cur;\r
-       tmp = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,\r
-                      NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);\r
-\r
-       cur = catal->xml;\r
-       if (cur == NULL) {\r
-           catal->xml = tmp;\r
-       } else {\r
-           while (cur->next != NULL) cur = cur->next;\r
-           cur->next = tmp;\r
-       }\r
-    }\r
-    return (0);\r
-}\r
-\r
-/**\r
- * xmlACatalogResolveSystem:\r
- * @catal:  a Catalog\r
- * @sysID:  the system ID string\r
- *\r
- * Try to lookup the catalog resource for a system ID\r
- *\r
- * Returns the resource if found or NULL otherwise, the value returned\r
- *      must be freed by the caller.\r
- */\r
-xmlChar *\r
-xmlACatalogResolveSystem(xmlCatalogPtr catal, const xmlChar *sysID) {\r
-    xmlChar *ret = NULL;\r
-\r
-    if ((sysID == NULL) || (catal == NULL))\r
-       return(NULL);\r
-    \r
-    if (xmlDebugCatalogs)\r
-       xmlGenericError(xmlGenericErrorContext,\r
-               "Resolve sysID %s\n", sysID);\r
-\r
-    if (catal->type == XML_XML_CATALOG_TYPE) {\r
-       ret = xmlCatalogListXMLResolve(catal->xml, NULL, sysID);\r
-       if (ret == XML_CATAL_BREAK)\r
-           ret = NULL;\r
-    } else {\r
-       const xmlChar *sgml;\r
-\r
-       sgml = xmlCatalogGetSGMLSystem(catal->sgml, sysID);\r
-       if (sgml != NULL)\r
-           ret = xmlStrdup(sgml);\r
-    }\r
-    return(ret);\r
-}\r
-\r
-/**\r
- * xmlACatalogResolvePublic:\r
- * @catal:  a Catalog\r
- * @pubID:  the public ID string\r
- *\r
- * Try to lookup the catalog local reference associated to a public ID in that catalog\r
- *\r
- * Returns the local resource if found or NULL otherwise, the value returned\r
- *      must be freed by the caller.\r
- */\r
-xmlChar *\r
-xmlACatalogResolvePublic(xmlCatalogPtr catal, const xmlChar *pubID) {\r
-    xmlChar *ret = NULL;\r
-\r
-    if ((pubID == NULL) || (catal == NULL))\r
-       return(NULL);\r
-    \r
-    if (xmlDebugCatalogs)\r
-       xmlGenericError(xmlGenericErrorContext,\r
-               "Resolve pubID %s\n", pubID);\r
-\r
-    if (catal->type == XML_XML_CATALOG_TYPE) {\r
-       ret = xmlCatalogListXMLResolve(catal->xml, pubID, NULL);\r
-       if (ret == XML_CATAL_BREAK)\r
-           ret = NULL;\r
-    } else {\r
-       const xmlChar *sgml;\r
-\r
-       sgml = xmlCatalogGetSGMLPublic(catal->sgml, pubID);\r
-       if (sgml != NULL)\r
-           ret = xmlStrdup(sgml);\r
-    }\r
-    return(ret);\r
-}\r
-\r
-/**\r
- * xmlACatalogResolve:\r
- * @catal:  a Catalog\r
- * @pubID:  the public ID string\r
- * @sysID:  the system ID string\r
- *\r
- * Do a complete resolution lookup of an External Identifier\r
- *\r
- * Returns the URI of the resource or NULL if not found, it must be freed\r
- *      by the caller.\r
- */\r
-xmlChar *\r
-xmlACatalogResolve(xmlCatalogPtr catal, const xmlChar * pubID,\r
-                   const xmlChar * sysID)\r
-{\r
-    xmlChar *ret = NULL;\r
-\r
-    if (((pubID == NULL) && (sysID == NULL)) || (catal == NULL))\r
-        return (NULL);\r
-\r
-    if (xmlDebugCatalogs) {\r
-         if ((pubID != NULL) && (sysID != NULL)) {\r
-             xmlGenericError(xmlGenericErrorContext,\r
-                             "Resolve: pubID %s sysID %s\n", pubID, sysID);\r
-         } else if (pubID != NULL) {\r
-             xmlGenericError(xmlGenericErrorContext,\r
-                             "Resolve: pubID %s\n", pubID);\r
-         } else {\r
-             xmlGenericError(xmlGenericErrorContext,\r
-                             "Resolve: sysID %s\n", sysID);\r
-         }\r
-    }\r
-\r
-    if (catal->type == XML_XML_CATALOG_TYPE) {\r
-        ret = xmlCatalogListXMLResolve(catal->xml, pubID, sysID);\r
-       if (ret == XML_CATAL_BREAK)\r
-           ret = NULL;\r
-    } else {\r
-        const xmlChar *sgml;\r
-\r
-        sgml = xmlCatalogSGMLResolve(catal, pubID, sysID);\r
-        if (sgml != NULL)\r
-            ret = xmlStrdup(sgml);\r
-    }\r
-    return (ret);\r
-}\r
-\r
-/**\r
- * xmlACatalogResolveURI:\r
- * @catal:  a Catalog\r
- * @URI:  the URI\r
- *\r
- * Do a complete resolution lookup of an URI\r
- *\r
- * Returns the URI of the resource or NULL if not found, it must be freed\r
- *      by the caller.\r
- */\r
-xmlChar *\r
-xmlACatalogResolveURI(xmlCatalogPtr catal, const xmlChar *URI) {\r
-    xmlChar *ret = NULL;\r
-\r
-    if ((URI == NULL) || (catal == NULL))\r
-       return(NULL);\r
-\r
-    if (xmlDebugCatalogs)\r
-       xmlGenericError(xmlGenericErrorContext,\r
-               "Resolve URI %s\n", URI);\r
-\r
-    if (catal->type == XML_XML_CATALOG_TYPE) {\r
-       ret = xmlCatalogListXMLResolveURI(catal->xml, URI);\r
-       if (ret == XML_CATAL_BREAK)\r
-           ret = NULL;\r
-    } else {\r
-       const xmlChar *sgml;\r
-\r
-       sgml = xmlCatalogSGMLResolve(catal, NULL, URI);\r
-       if (sgml != NULL)\r
-            sgml = xmlStrdup(sgml);\r
-    }\r
-    return(ret);\r
-}\r
-\r
-#ifdef LIBXML_OUTPUT_ENABLED\r
-/**\r
- * xmlACatalogDump:\r
- * @catal:  a Catalog\r
- * @out:  the file.\r
- *\r
- * Dump the given catalog to the given file.\r
- */\r
-void\r
-xmlACatalogDump(xmlCatalogPtr catal, FILE *out) {\r
-    if ((out == NULL) || (catal == NULL))\r
-       return;\r
-\r
-    if (catal->type == XML_XML_CATALOG_TYPE) {\r
-       xmlDumpXMLCatalog(out, catal->xml);\r
-    } else {\r
-       xmlHashScan(catal->sgml,\r
-                   (xmlHashScanner) xmlCatalogDumpEntry, out);\r
-    } \r
-}\r
-#endif /* LIBXML_OUTPUT_ENABLED */\r
-\r
-/**\r
- * xmlACatalogAdd:\r
- * @catal:  a Catalog\r
- * @type:  the type of record to add to the catalog\r
- * @orig:  the system, public or prefix to match \r
- * @replace:  the replacement value for the match\r
- *\r
- * Add an entry in the catalog, it may overwrite existing but\r
- * different entries.\r
- *\r
- * Returns 0 if successful, -1 otherwise\r
- */\r
-int\r
-xmlACatalogAdd(xmlCatalogPtr catal, const xmlChar * type,\r
-              const xmlChar * orig, const xmlChar * replace)\r
-{\r
-    int res = -1;\r
-\r
-    if (catal == NULL)\r
-       return(-1);\r
-\r
-    if (catal->type == XML_XML_CATALOG_TYPE) {\r
-        res = xmlAddXMLCatalog(catal->xml, type, orig, replace);\r
-    } else {\r
-        xmlCatalogEntryType cattype;\r
-\r
-        cattype = xmlGetSGMLCatalogEntryType(type);\r
-        if (cattype != XML_CATA_NONE) {\r
-            xmlCatalogEntryPtr entry;\r
-\r
-            entry = xmlNewCatalogEntry(cattype, orig, replace, NULL,\r
-                                       XML_CATA_PREFER_NONE, NULL);\r
-           if (catal->sgml == NULL)\r
-               catal->sgml = xmlHashCreate(10);\r
-            res = xmlHashAddEntry(catal->sgml, orig, entry);\r
-        }\r
-    }\r
-    return (res);\r
-}\r
-\r
-/**\r
- * xmlACatalogRemove:\r
- * @catal:  a Catalog\r
- * @value:  the value to remove\r
- *\r
- * Remove an entry from the catalog\r
- *\r
- * Returns the number of entries removed if successful, -1 otherwise\r
- */\r
-int\r
-xmlACatalogRemove(xmlCatalogPtr catal, const xmlChar *value) {\r
-    int res = -1;\r
-\r
-    if ((catal == NULL) || (value == NULL))\r
-       return(-1);\r
-\r
-    if (catal->type == XML_XML_CATALOG_TYPE) {\r
-       res = xmlDelXMLCatalog(catal->xml, value);\r
-    } else {\r
-       res = xmlHashRemoveEntry(catal->sgml, value,\r
-               (xmlHashDeallocator) xmlFreeCatalogEntry);\r
-       if (res == 0)\r
-           res = 1;\r
-    } \r
-    return(res);\r
-}\r
-\r
-/**\r
- * xmlNewCatalog:\r
- * @sgml:  should this create an SGML catalog\r
- *\r
- * create a new Catalog.\r
- *\r
- * Returns the xmlCatalogPtr or NULL in case of error\r
- */\r
-xmlCatalogPtr\r
-xmlNewCatalog(int sgml) {\r
-    xmlCatalogPtr catal = NULL;\r
-\r
-    if (sgml) {\r
-       catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE,\r
-                                   xmlCatalogDefaultPrefer);\r
-        if ((catal != NULL) && (catal->sgml == NULL))\r
-           catal->sgml = xmlHashCreate(10);\r
-    } else\r
-       catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,\r
-                                   xmlCatalogDefaultPrefer);\r
-    return(catal);\r
-}\r
-\r
-/**\r
- * xmlCatalogIsEmpty:\r
- * @catal:  should this create an SGML catalog\r
- *\r
- * Check is a catalog is empty\r
- *\r
- * Returns 1 if the catalog is empty, 0 if not, amd -1 in case of error.\r
- */\r
-int\r
-xmlCatalogIsEmpty(xmlCatalogPtr catal) {\r
-    if (catal == NULL)\r
-       return(-1);\r
-\r
-    if (catal->type == XML_XML_CATALOG_TYPE) {\r
-       if (catal->xml == NULL)\r
-           return(1);\r
-       if ((catal->xml->type != XML_CATA_CATALOG) &&\r
-           (catal->xml->type != XML_CATA_BROKEN_CATALOG))\r
-           return(-1);\r
-       if (catal->xml->children == NULL)\r
-           return(1);\r
-        return(0);\r
-    } else {\r
-       int res;\r
-\r
-       if (catal->sgml == NULL)\r
-           return(1);\r
-       res = xmlHashSize(catal->sgml);\r
-       if (res == 0)\r
-           return(1);\r
-       if (res < 0)\r
-           return(-1);\r
-    } \r
-    return(0);\r
-}\r
-\r
-/************************************************************************\r
- *                                                                     *\r
- *   Public interfaces manipulating the global shared default catalog  *\r
- *                                                                     *\r
- ************************************************************************/\r
-\r
-/**\r
- * xmlInitializeCatalogData:\r
- *\r
- * Do the catalog initialization only of global data, doesn't try to load\r
- * any catalog actually.\r
- * this function is not thread safe, catalog initialization should\r
- * preferably be done once at startup\r
- */\r
-static void\r
-xmlInitializeCatalogData(void) {\r
-    if (xmlCatalogInitialized != 0)\r
-       return;\r
-\r
-    if (getenv("XML_DEBUG_CATALOG")) \r
-       xmlDebugCatalogs = 1;\r
-    xmlCatalogMutex = xmlNewRMutex();\r
-\r
-    xmlCatalogInitialized = 1;\r
-}\r
-/**\r
- * xmlInitializeCatalog:\r
- *\r
- * Do the catalog initialization.\r
- * this function is not thread safe, catalog initialization should\r
- * preferably be done once at startup\r
- */\r
-void\r
-xmlInitializeCatalog(void) {\r
-    if (xmlCatalogInitialized != 0)\r
-       return;\r
-\r
-    xmlInitializeCatalogData();\r
-    xmlRMutexLock(xmlCatalogMutex);\r
-\r
-    if (getenv("XML_DEBUG_CATALOG")) \r
-       xmlDebugCatalogs = 1;\r
-\r
-    if (xmlDefaultCatalog == NULL) {\r
-       const char *catalogs;\r
-       char *path;\r
-       const char *cur, *paths;\r
-       xmlCatalogPtr catal;\r
-       xmlCatalogEntryPtr *nextent;\r
-\r
-       catalogs = (const char *) getenv("XML_CATALOG_FILES");\r
-       if (catalogs == NULL)\r
-#if defined(_WIN32) && defined(_MSC_VER)\r
-    {\r
-               void* hmodule;\r
-               hmodule = GetModuleHandleA("libxml2.dll");\r
-               if (hmodule == NULL)\r
-                       hmodule = GetModuleHandleA(NULL);\r
-               if (hmodule != NULL) {\r
-                       char buf[256];\r
-                       unsigned long len = GetModuleFileNameA(hmodule, buf, 255);\r
-                       if (len != 0) {\r
-                               char* p = &(buf[len]);\r
-                               while (*p != '\\' && p > buf) \r
-                                       p--;\r
-                               if (p != buf) {\r
-                                       xmlChar* uri;\r
-                                       strncpy(p, "\\..\\etc\\catalog", 255 - (p - buf));\r
-                                       uri = xmlCanonicPath(buf);\r
-                                       if (uri != NULL) {\r
-                                               strncpy(XML_XML_DEFAULT_CATALOG, uri, 255);\r
-                                               xmlFree(uri);\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-               catalogs = XML_XML_DEFAULT_CATALOG;\r
-    }\r
-#else\r
-           catalogs = XML_XML_DEFAULT_CATALOG;\r
-#endif\r
-\r
-       catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, \r
-               xmlCatalogDefaultPrefer);\r
-       if (catal != NULL) {\r
-           /* the XML_CATALOG_FILES envvar is allowed to contain a \r
-              space-separated list of entries. */\r
-           cur = catalogs;\r
-           nextent = &catal->xml;\r
-           while (*cur != '\0') {\r
-               while (xmlIsBlank_ch(*cur)) \r
-                   cur++;\r
-               if (*cur != 0) {\r
-                   paths = cur;\r
-                   while ((*cur != 0) && (!xmlIsBlank_ch(*cur)))\r
-                       cur++;\r
-                   path = (char *) xmlStrndup((const xmlChar *)paths, cur - paths);\r
-                   if (path != NULL) {\r
-                       *nextent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,\r
-                               NULL, BAD_CAST path, xmlCatalogDefaultPrefer, NULL);\r
-                       if (*nextent != NULL)\r
-                           nextent = &((*nextent)->next);\r
-                       xmlFree(path);\r
-                   }\r
-               }\r
-           }\r
-           xmlDefaultCatalog = catal;\r
-       }\r
-    }\r
-\r
-    xmlRMutexUnlock(xmlCatalogMutex);\r
-}\r
-\r
-\r
-/**\r
- * xmlLoadCatalog:\r
- * @filename:  a file path\r
- *\r
- * Load the catalog and makes its definitions effective for the default\r
- * external entity loader. It will recurse in SGML CATALOG entries.\r
- * this function is not thread safe, catalog initialization should\r
- * preferably be done once at startup\r
- *\r
- * Returns 0 in case of success -1 in case of error\r
- */\r
-int\r
-xmlLoadCatalog(const char *filename)\r
-{\r
-    int ret;\r
-    xmlCatalogPtr catal;\r
-\r
-    if (!xmlCatalogInitialized)\r
-       xmlInitializeCatalogData();\r
-\r
-    xmlRMutexLock(xmlCatalogMutex);\r
-\r
-    if (xmlDefaultCatalog == NULL) {\r
-       catal = xmlLoadACatalog(filename);\r
-       if (catal == NULL) {\r
-           xmlRMutexUnlock(xmlCatalogMutex);\r
-           return(-1);\r
-       }\r
-\r
-       xmlDefaultCatalog = catal;\r
-       xmlRMutexUnlock(xmlCatalogMutex);\r
-       return(0);\r
-    }\r
-\r
-    ret = xmlExpandCatalog(xmlDefaultCatalog, filename);\r
-    xmlRMutexUnlock(xmlCatalogMutex);\r
-    return(ret);\r
-}\r
-\r
-/**\r
- * xmlLoadCatalogs:\r
- * @pathss:  a list of directories separated by a colon or a space.\r
- *\r
- * Load the catalogs and makes their definitions effective for the default\r
- * external entity loader.\r
- * this function is not thread safe, catalog initialization should\r
- * preferably be done once at startup\r
- */\r
-void\r
-xmlLoadCatalogs(const char *pathss) {\r
-    const char *cur;\r
-    const char *paths;\r
-    xmlChar *path;\r
-\r
-    if (pathss == NULL)\r
-       return;\r
-\r
-    cur = pathss;\r
-    while ((cur != NULL) && (*cur != 0)) {\r
-       while (xmlIsBlank_ch(*cur)) cur++;\r
-       if (*cur != 0) {\r
-           paths = cur;\r
-           while ((*cur != 0) && (*cur != ':') && (!xmlIsBlank_ch(*cur)))\r
-               cur++;\r
-           path = xmlStrndup((const xmlChar *)paths, cur - paths);\r
-           if (path != NULL) {\r
-               xmlLoadCatalog((const char *) path);\r
-               xmlFree(path);\r
-           }\r
-       }\r
-       while (*cur == ':')\r
-           cur++;\r
-    }\r
-}\r
-\r
-/**\r
- * xmlCatalogCleanup:\r
- *\r
- * Free up all the memory associated with catalogs\r
- */\r
-void\r
-xmlCatalogCleanup(void) {\r
-    if (xmlCatalogInitialized == 0)\r
-        return;\r
-\r
-    xmlRMutexLock(xmlCatalogMutex);\r
-    if (xmlDebugCatalogs)\r
-       xmlGenericError(xmlGenericErrorContext,\r
-               "Catalogs cleanup\n");\r
-    if (xmlCatalogXMLFiles != NULL)\r
-       xmlHashFree(xmlCatalogXMLFiles, \r
-                   (xmlHashDeallocator)xmlFreeCatalogHashEntryList);\r
-    xmlCatalogXMLFiles = NULL;\r
-    if (xmlDefaultCatalog != NULL)\r
-       xmlFreeCatalog(xmlDefaultCatalog);\r
-    xmlDefaultCatalog = NULL;\r
-    xmlDebugCatalogs = 0;\r
-    xmlCatalogInitialized = 0;\r
-    xmlRMutexUnlock(xmlCatalogMutex);\r
-    xmlFreeRMutex(xmlCatalogMutex);\r
-}\r
-\r
-/**\r
- * xmlCatalogResolveSystem:\r
- * @sysID:  the system ID string\r
- *\r
- * Try to lookup the catalog resource for a system ID\r
- *\r
- * Returns the resource if found or NULL otherwise, the value returned\r
- *      must be freed by the caller.\r
- */\r
-xmlChar *\r
-xmlCatalogResolveSystem(const xmlChar *sysID) {\r
-    xmlChar *ret;\r
-\r
-    if (!xmlCatalogInitialized)\r
-       xmlInitializeCatalog();\r
-\r
-    ret = xmlACatalogResolveSystem(xmlDefaultCatalog, sysID);\r
-    return(ret);\r
-}\r
-\r
-/**\r
- * xmlCatalogResolvePublic:\r
- * @pubID:  the public ID string\r
- *\r
- * Try to lookup the catalog reference associated to a public ID\r
- *\r
- * Returns the resource if found or NULL otherwise, the value returned\r
- *      must be freed by the caller.\r
- */\r
-xmlChar *\r
-xmlCatalogResolvePublic(const xmlChar *pubID) {\r
-    xmlChar *ret;\r
-\r
-    if (!xmlCatalogInitialized)\r
-       xmlInitializeCatalog();\r
-\r
-    ret = xmlACatalogResolvePublic(xmlDefaultCatalog, pubID);\r
-    return(ret);\r
-}\r
-\r
-/**\r
- * xmlCatalogResolve:\r
- * @pubID:  the public ID string\r
- * @sysID:  the system ID string\r
- *\r
- * Do a complete resolution lookup of an External Identifier\r
- *\r
- * Returns the URI of the resource or NULL if not found, it must be freed\r
- *      by the caller.\r
- */\r
-xmlChar *\r
-xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) {\r
-    xmlChar *ret;\r
-\r
-    if (!xmlCatalogInitialized)\r
-       xmlInitializeCatalog();\r
-\r
-    ret = xmlACatalogResolve(xmlDefaultCatalog, pubID, sysID);\r
-    return(ret);\r
-}\r
-\r
-/**\r
- * xmlCatalogResolveURI:\r
- * @URI:  the URI\r
- *\r
- * Do a complete resolution lookup of an URI\r
- *\r
- * Returns the URI of the resource or NULL if not found, it must be freed\r
- *      by the caller.\r
- */\r
-xmlChar *\r
-xmlCatalogResolveURI(const xmlChar *URI) {\r
-    xmlChar *ret;\r
-\r
-    if (!xmlCatalogInitialized)\r
-       xmlInitializeCatalog();\r
-\r
-    ret = xmlACatalogResolveURI(xmlDefaultCatalog, URI);\r
-    return(ret);\r
-}\r
-\r
-#ifdef LIBXML_OUTPUT_ENABLED\r
-/**\r
- * xmlCatalogDump:\r
- * @out:  the file.\r
- *\r
- * Dump all the global catalog content to the given file.\r
- */\r
-void\r
-xmlCatalogDump(FILE *out) {\r
-    if (out == NULL)\r
-       return;\r
-\r
-    if (!xmlCatalogInitialized)\r
-       xmlInitializeCatalog();\r
-\r
-    xmlACatalogDump(xmlDefaultCatalog, out);\r
-}\r
-#endif /* LIBXML_OUTPUT_ENABLED */\r
-\r
-/**\r
- * xmlCatalogAdd:\r
- * @type:  the type of record to add to the catalog\r
- * @orig:  the system, public or prefix to match \r
- * @replace:  the replacement value for the match\r
- *\r
- * Add an entry in the catalog, it may overwrite existing but\r
- * different entries.\r
- * If called before any other catalog routine, allows to override the\r
- * default shared catalog put in place by xmlInitializeCatalog();\r
- *\r
- * Returns 0 if successful, -1 otherwise\r
- */\r
-int\r
-xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) {\r
-    int res = -1;\r
-\r
-    if (!xmlCatalogInitialized)\r
-       xmlInitializeCatalogData();\r
-\r
-    xmlRMutexLock(xmlCatalogMutex);\r
-    /*\r
-     * Specific case where one want to override the default catalog\r
-     * put in place by xmlInitializeCatalog();\r
-     */\r
-    if ((xmlDefaultCatalog == NULL) &&\r
-       (xmlStrEqual(type, BAD_CAST "catalog"))) {\r
-       xmlDefaultCatalog = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,\r
-                                         xmlCatalogDefaultPrefer);\r
-       xmlDefaultCatalog->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,\r
-                                   orig, NULL,  xmlCatalogDefaultPrefer, NULL);\r
-\r
-       xmlRMutexUnlock(xmlCatalogMutex);\r
-       return(0);\r
-    } \r
-\r
-    res = xmlACatalogAdd(xmlDefaultCatalog, type, orig, replace);\r
-    xmlRMutexUnlock(xmlCatalogMutex);\r
-    return(res);\r
-}\r
-\r
-/**\r
- * xmlCatalogRemove:\r
- * @value:  the value to remove\r
- *\r
- * Remove an entry from the catalog\r
- *\r
- * Returns the number of entries removed if successful, -1 otherwise\r
- */\r
-int\r
-xmlCatalogRemove(const xmlChar *value) {\r
-    int res;\r
-\r
-    if (!xmlCatalogInitialized)\r
-       xmlInitializeCatalog();\r
-\r
-    xmlRMutexLock(xmlCatalogMutex);\r
-    res = xmlACatalogRemove(xmlDefaultCatalog, value);\r
-    xmlRMutexUnlock(xmlCatalogMutex);\r
-    return(res);\r
-}\r
-\r
-/**\r
- * xmlCatalogConvert:\r
- *\r
- * Convert all the SGML catalog entries as XML ones\r
- *\r
- * Returns the number of entries converted if successful, -1 otherwise\r
- */\r
-int\r
-xmlCatalogConvert(void) {\r
-    int res = -1;\r
-\r
-    if (!xmlCatalogInitialized)\r
-       xmlInitializeCatalog();\r
-\r
-    xmlRMutexLock(xmlCatalogMutex);\r
-    res = xmlConvertSGMLCatalog(xmlDefaultCatalog);\r
-    xmlRMutexUnlock(xmlCatalogMutex);\r
-    return(res);\r
-}\r
-\r
-/************************************************************************\r
- *                                                                     *\r
- *     Public interface manipulating the common preferences            *\r
- *                                                                     *\r
- ************************************************************************/\r
-\r
-/**\r
- * xmlCatalogGetDefaults:\r
- *\r
- * Used to get the user preference w.r.t. to what catalogs should\r
- * be accepted\r
- *\r
- * Returns the current xmlCatalogAllow value\r
- */\r
-xmlCatalogAllow\r
-xmlCatalogGetDefaults(void) {\r
-    return(xmlCatalogDefaultAllow);\r
-}\r
-\r
-/**\r
- * xmlCatalogSetDefaults:\r
- * @allow:  what catalogs should be accepted\r
- *\r
- * Used to set the user preference w.r.t. to what catalogs should\r
- * be accepted\r
- */\r
-void\r
-xmlCatalogSetDefaults(xmlCatalogAllow allow) {\r
-    if (xmlDebugCatalogs) {\r
-       switch (allow) {\r
-           case XML_CATA_ALLOW_NONE:\r
-               xmlGenericError(xmlGenericErrorContext,\r
-                       "Disabling catalog usage\n");\r
-               break;\r
-           case XML_CATA_ALLOW_GLOBAL:\r
-               xmlGenericError(xmlGenericErrorContext,\r
-                       "Allowing only global catalogs\n");\r
-               break;\r
-           case XML_CATA_ALLOW_DOCUMENT:\r
-               xmlGenericError(xmlGenericErrorContext,\r
-                       "Allowing only catalogs from the document\n");\r
-               break;\r
-           case XML_CATA_ALLOW_ALL:\r
-               xmlGenericError(xmlGenericErrorContext,\r
-                       "Allowing all catalogs\n");\r
-               break;\r
-       }\r
-    }\r
-    xmlCatalogDefaultAllow = allow;\r
-}\r
-\r
-/**\r
- * xmlCatalogSetDefaultPrefer:\r
- * @prefer:  the default preference for delegation\r
- *\r
- * Allows to set the preference between public and system for deletion\r
- * in XML Catalog resolution. C.f. section 4.1.1 of the spec\r
- * Values accepted are XML_CATA_PREFER_PUBLIC or XML_CATA_PREFER_SYSTEM\r
- *\r
- * Returns the previous value of the default preference for delegation\r
- */\r
-xmlCatalogPrefer\r
-xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer) {\r
-    xmlCatalogPrefer ret = xmlCatalogDefaultPrefer;\r
-\r
-    if (prefer == XML_CATA_PREFER_NONE)\r
-       return(ret);\r
-\r
-    if (xmlDebugCatalogs) {\r
-       switch (prefer) {\r
-           case XML_CATA_PREFER_PUBLIC:\r
-               xmlGenericError(xmlGenericErrorContext,\r
-                       "Setting catalog preference to PUBLIC\n");\r
-               break;\r
-           case XML_CATA_PREFER_SYSTEM:\r
-               xmlGenericError(xmlGenericErrorContext,\r
-                       "Setting catalog preference to SYSTEM\n");\r
-               break;\r
-           case XML_CATA_PREFER_NONE:\r
-               break;\r
-       }\r
-    }\r
-    xmlCatalogDefaultPrefer = prefer;\r
-    return(ret);\r
-}\r
-\r
-/**\r
- * xmlCatalogSetDebug:\r
- * @level:  the debug level of catalogs required\r
- *\r
- * Used to set the debug level for catalog operation, 0 disable\r
- * debugging, 1 enable it\r
- *\r
- * Returns the previous value of the catalog debugging level\r
- */\r
-int\r
-xmlCatalogSetDebug(int level) {\r
-    int ret = xmlDebugCatalogs;\r
-\r
-    if (level <= 0)\r
-        xmlDebugCatalogs = 0;\r
-    else\r
-       xmlDebugCatalogs = level;\r
-    return(ret);\r
-}\r
-\r
-/************************************************************************\r
- *                                                                     *\r
- *   Minimal interfaces used for per-document catalogs by the parser   *\r
- *                                                                     *\r
- ************************************************************************/\r
-\r
-/**\r
- * xmlCatalogFreeLocal:\r
- * @catalogs:  a document's list of catalogs\r
- *\r
- * Free up the memory associated to the catalog list\r
- */\r
-void\r
-xmlCatalogFreeLocal(void *catalogs) {\r
-    xmlCatalogEntryPtr catal;\r
-\r
-    if (!xmlCatalogInitialized)\r
-       xmlInitializeCatalog();\r
-\r
-    catal = (xmlCatalogEntryPtr) catalogs;\r
-    if (catal != NULL)\r
-       xmlFreeCatalogEntryList(catal);\r
-}\r
-\r
-\r
-/**\r
- * xmlCatalogAddLocal:\r
- * @catalogs:  a document's list of catalogs\r
- * @URL:  the URL to a new local catalog\r
- *\r
- * Add the new entry to the catalog list\r
- *\r
- * Returns the updated list\r
- */\r
-void * \r
-xmlCatalogAddLocal(void *catalogs, const xmlChar *URL) {\r
-    xmlCatalogEntryPtr catal, add;\r
-\r
-    if (!xmlCatalogInitialized)\r
-       xmlInitializeCatalog();\r
-\r
-    if (URL == NULL)\r
-       return(catalogs);\r
-\r
-    if (xmlDebugCatalogs)\r
-       xmlGenericError(xmlGenericErrorContext,\r
-               "Adding document catalog %s\n", URL);\r
-\r
-    add = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, URL, NULL,\r
-                            xmlCatalogDefaultPrefer, NULL);\r
-    if (add == NULL)\r
-       return(catalogs);\r
-\r
-    catal = (xmlCatalogEntryPtr) catalogs;\r
-    if (catal == NULL) \r
-       return((void *) add);\r
-\r
-    while (catal->next != NULL)\r
-       catal = catal->next;\r
-    catal->next = add;\r
-    return(catalogs);\r
-}\r
-\r
-/**\r
- * xmlCatalogLocalResolve:\r
- * @catalogs:  a document's list of catalogs\r
- * @pubID:  the public ID string\r
- * @sysID:  the system ID string\r
- *\r
- * Do a complete resolution lookup of an External Identifier using a \r
- * document's private catalog list\r
- *\r
- * Returns the URI of the resource or NULL if not found, it must be freed\r
- *      by the caller.\r
- */\r
-xmlChar *\r
-xmlCatalogLocalResolve(void *catalogs, const xmlChar *pubID,\r
-                      const xmlChar *sysID) {\r
-    xmlCatalogEntryPtr catal;\r
-    xmlChar *ret;\r
-\r
-    if (!xmlCatalogInitialized)\r
-       xmlInitializeCatalog();\r
-\r
-    if ((pubID == NULL) && (sysID == NULL))\r
-       return(NULL);\r
-\r
-    if (xmlDebugCatalogs) {\r
-        if ((pubID != NULL) && (sysID != NULL)) {\r
-            xmlGenericError(xmlGenericErrorContext,\r
-                            "Local Resolve: pubID %s sysID %s\n", pubID, sysID);\r
-        } else if (pubID != NULL) {\r
-            xmlGenericError(xmlGenericErrorContext,\r
-                            "Local Resolve: pubID %s\n", pubID);\r
-        } else {\r
-            xmlGenericError(xmlGenericErrorContext,\r
-                            "Local Resolve: sysID %s\n", sysID);\r
-        }\r
-    }\r
-\r
-    catal = (xmlCatalogEntryPtr) catalogs;\r
-    if (catal == NULL)\r
-       return(NULL);\r
-    ret = xmlCatalogListXMLResolve(catal, pubID, sysID);\r
-    if ((ret != NULL) && (ret != XML_CATAL_BREAK))\r
-       return(ret);\r
-    return(NULL);\r
-}\r
-\r
-/**\r
- * xmlCatalogLocalResolveURI:\r
- * @catalogs:  a document's list of catalogs\r
- * @URI:  the URI\r
- *\r
- * Do a complete resolution lookup of an URI using a \r
- * document's private catalog list\r
- *\r
- * Returns the URI of the resource or NULL if not found, it must be freed\r
- *      by the caller.\r
- */\r
-xmlChar *\r
-xmlCatalogLocalResolveURI(void *catalogs, const xmlChar *URI) {\r
-    xmlCatalogEntryPtr catal;\r
-    xmlChar *ret;\r
-\r
-    if (!xmlCatalogInitialized)\r
-       xmlInitializeCatalog();\r
-\r
-    if (URI == NULL)\r
-       return(NULL);\r
-\r
-    if (xmlDebugCatalogs)\r
-       xmlGenericError(xmlGenericErrorContext,\r
-               "Resolve URI %s\n", URI);\r
-\r
-    catal = (xmlCatalogEntryPtr) catalogs;\r
-    if (catal == NULL)\r
-       return(NULL);\r
-    ret = xmlCatalogListXMLResolveURI(catal, URI);\r
-    if ((ret != NULL) && (ret != XML_CATAL_BREAK))\r
-       return(ret);\r
-    return(NULL);\r
-}\r
-\r
-/************************************************************************\r
- *                                                                     *\r
- *                     Deprecated interfaces                           *\r
- *                                                                     *\r
- ************************************************************************/\r
-/**\r
- * xmlCatalogGetSystem:\r
- * @sysID:  the system ID string\r
- *\r
- * Try to lookup the catalog reference associated to a system ID\r
- * DEPRECATED, use xmlCatalogResolveSystem()\r
- *\r
- * Returns the resource if found or NULL otherwise.\r
- */\r
-const xmlChar *\r
-xmlCatalogGetSystem(const xmlChar *sysID) {\r
-    xmlChar *ret;\r
-    static xmlChar result[1000];\r
-    static int msg = 0;\r
-\r
-    if (!xmlCatalogInitialized)\r
-       xmlInitializeCatalog();\r
-\r
-    if (msg == 0) {\r
-       xmlGenericError(xmlGenericErrorContext,\r
-               "Use of deprecated xmlCatalogGetSystem() call\n");\r
-       msg++;\r
-    }\r
-\r
-    if (sysID == NULL)\r
-       return(NULL);\r
-    \r
-    /*\r
-     * Check first the XML catalogs\r
-     */\r
-    if (xmlDefaultCatalog != NULL) {\r
-       ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, NULL, sysID);\r
-       if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {\r
-           snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);\r
-           result[sizeof(result) - 1] = 0;\r
-           return(result);\r
-       }\r
-    }\r
-\r
-    if (xmlDefaultCatalog != NULL)\r
-       return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog->sgml, sysID));\r
-    return(NULL);\r
-}\r
-\r
-/**\r
- * xmlCatalogGetPublic:\r
- * @pubID:  the public ID string\r
- *\r
- * Try to lookup the catalog reference associated to a public ID\r
- * DEPRECATED, use xmlCatalogResolvePublic()\r
- *\r
- * Returns the resource if found or NULL otherwise.\r
- */\r
-const xmlChar *\r
-xmlCatalogGetPublic(const xmlChar *pubID) {\r
-    xmlChar *ret;\r
-    static xmlChar result[1000];\r
-    static int msg = 0;\r
-\r
-    if (!xmlCatalogInitialized)\r
-       xmlInitializeCatalog();\r
-\r
-    if (msg == 0) {\r
-       xmlGenericError(xmlGenericErrorContext,\r
-               "Use of deprecated xmlCatalogGetPublic() call\n");\r
-       msg++;\r
-    }\r
-\r
-    if (pubID == NULL)\r
-       return(NULL);\r
-    \r
-    /*\r
-     * Check first the XML catalogs\r
-     */\r
-    if (xmlDefaultCatalog != NULL) {\r
-       ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, pubID, NULL);\r
-       if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {\r
-           snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);\r
-           result[sizeof(result) - 1] = 0;\r
-           return(result);\r
-       }\r
-    }\r
-\r
-    if (xmlDefaultCatalog != NULL)\r
-       return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog->sgml, pubID));\r
-    return(NULL);\r
-}\r
-\r
-#define bottom_catalog\r
-#include "elfgcchack.h"\r
-#endif /* LIBXML_CATALOG_ENABLED */\r
+/**
+ * catalog.c: set of generic Catalog related routines 
+ *
+ * Reference:  SGML Open Technical Resolution TR9401:1997.
+ *             http://www.jclark.com/sp/catalog.htm
+ *
+ *             XML Catalogs Working Draft 06 August 2001
+ *             http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
+ *
+ * See Copyright for the status of this software.
+ *
+ * Daniel.Veillard@imag.fr
+ */
+
+#define IN_LIBXML
+#include "libxml.h"
+
+#ifdef LIBXML_CATALOG_ENABLED
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/hash.h>
+#include <libxml/uri.h>
+#include <libxml/parserInternals.h>
+#include <libxml/catalog.h>
+#include <libxml/xmlerror.h>
+#include <libxml/threads.h>
+#include <libxml/globals.h>
+
+#define MAX_DELEGATE   50
+#define MAX_CATAL_DEPTH        50
+
+/**
+ * TODO:
+ *
+ * macro to flag unimplemented blocks
+ * XML_CATALOG_PREFER user env to select between system/public prefered
+ * option. C.f. Richard Tobin <richard@cogsci.ed.ac.uk>
+ *> Just FYI, I am using an environment variable XML_CATALOG_PREFER with
+ *> values "system" and "public".  I have made the default be "system" to
+ *> match yours.
+ */
+#define TODO                                                           \
+    xmlGenericError(xmlGenericErrorContext,                            \
+           "Unimplemented block at %s:%d\n",                           \
+            __FILE__, __LINE__);
+
+#define XML_URN_PUBID "urn:publicid:"
+#define XML_CATAL_BREAK ((xmlChar *) -1)
+#ifndef XML_XML_DEFAULT_CATALOG
+#define XML_XML_DEFAULT_CATALOG "file:///etc/xml/catalog"
+#endif
+#ifndef XML_SGML_DEFAULT_CATALOG
+#define XML_SGML_DEFAULT_CATALOG "file:///etc/sgml/catalog"
+#endif
+
+#if defined(_WIN32) && defined(_MSC_VER)
+#undef XML_XML_DEFAULT_CATALOG
+static char XML_XML_DEFAULT_CATALOG[256] = "file:///etc/xml/catalog";
+void* __stdcall GetModuleHandleA(const char*);
+unsigned long __stdcall GetModuleFileNameA(void*, char*, unsigned long);
+#endif
+
+static xmlChar *xmlCatalogNormalizePublic(const xmlChar *pubID);
+static int xmlExpandCatalog(xmlCatalogPtr catal, const char *filename);
+
+/************************************************************************
+ *                                                                     *
+ *                     Types, all private                              *
+ *                                                                     *
+ ************************************************************************/
+
+typedef enum {
+    XML_CATA_REMOVED = -1,
+    XML_CATA_NONE = 0,
+    XML_CATA_CATALOG,
+    XML_CATA_BROKEN_CATALOG,
+    XML_CATA_NEXT_CATALOG,
+    XML_CATA_GROUP,
+    XML_CATA_PUBLIC,
+    XML_CATA_SYSTEM,
+    XML_CATA_REWRITE_SYSTEM,
+    XML_CATA_DELEGATE_PUBLIC,
+    XML_CATA_DELEGATE_SYSTEM,
+    XML_CATA_URI,
+    XML_CATA_REWRITE_URI,
+    XML_CATA_DELEGATE_URI,
+    SGML_CATA_SYSTEM,
+    SGML_CATA_PUBLIC,
+    SGML_CATA_ENTITY,
+    SGML_CATA_PENTITY,
+    SGML_CATA_DOCTYPE,
+    SGML_CATA_LINKTYPE,
+    SGML_CATA_NOTATION,
+    SGML_CATA_DELEGATE,
+    SGML_CATA_BASE,
+    SGML_CATA_CATALOG,
+    SGML_CATA_DOCUMENT,
+    SGML_CATA_SGMLDECL
+} xmlCatalogEntryType;
+
+typedef struct _xmlCatalogEntry xmlCatalogEntry;
+typedef xmlCatalogEntry *xmlCatalogEntryPtr;
+struct _xmlCatalogEntry {
+    struct _xmlCatalogEntry *next;
+    struct _xmlCatalogEntry *parent;
+    struct _xmlCatalogEntry *children;
+    xmlCatalogEntryType type;
+    xmlChar *name;
+    xmlChar *value;
+    xmlChar *URL;  /* The expanded URL using the base */
+    xmlCatalogPrefer prefer;
+    int dealloc;
+    int depth;
+    struct _xmlCatalogEntry *group;
+};
+
+typedef enum {
+    XML_XML_CATALOG_TYPE = 1,
+    XML_SGML_CATALOG_TYPE
+} xmlCatalogType;
+
+#define XML_MAX_SGML_CATA_DEPTH 10
+struct _xmlCatalog {
+    xmlCatalogType type;       /* either XML or SGML */
+
+    /*
+     * SGML Catalogs are stored as a simple hash table of catalog entries
+     * Catalog stack to check against overflows when building the
+     * SGML catalog
+     */
+    char *catalTab[XML_MAX_SGML_CATA_DEPTH];   /* stack of catals */
+    int          catalNr;      /* Number of current catal streams */
+    int          catalMax;     /* Max number of catal streams */
+    xmlHashTablePtr sgml;
+
+    /*
+     * XML Catalogs are stored as a tree of Catalog entries
+     */
+    xmlCatalogPrefer prefer;
+    xmlCatalogEntryPtr xml;
+};
+
+/************************************************************************
+ *                                                                     *
+ *                     Global variables                                *
+ *                                                                     *
+ ************************************************************************/
+
+/*
+ * Those are preferences
+ */
+static int xmlDebugCatalogs = 0;   /* used for debugging */
+static xmlCatalogAllow xmlCatalogDefaultAllow = XML_CATA_ALLOW_ALL;
+static xmlCatalogPrefer xmlCatalogDefaultPrefer = XML_CATA_PREFER_PUBLIC;
+
+/*
+ * Hash table containing all the trees of XML catalogs parsed by
+ * the application.
+ */
+static xmlHashTablePtr xmlCatalogXMLFiles = NULL;
+
+/*
+ * The default catalog in use by the application
+ */
+static xmlCatalogPtr xmlDefaultCatalog = NULL;
+
+/*
+ * A mutex for modifying the shared global catalog(s)
+ * xmlDefaultCatalog tree.
+ * It also protects xmlCatalogXMLFiles
+ * The core of this readers/writer scheme is in xmlFetchXMLCatalogFile()
+ */
+static xmlRMutexPtr xmlCatalogMutex = NULL;
+
+/*
+ * Whether the catalog support was initialized.
+ */
+static int xmlCatalogInitialized = 0;
+
+/************************************************************************
+ *                                                                     *
+ *                     Catalog error handlers                          *
+ *                                                                     *
+ ************************************************************************/
+
+/**
+ * xmlCatalogErrMemory:
+ * @extra:  extra informations
+ *
+ * Handle an out of memory condition
+ */
+static void
+xmlCatalogErrMemory(const char *extra)
+{
+    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_CATALOG,
+                    XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0,
+                   extra, NULL, NULL, 0, 0,
+                   "Memory allocation failed : %s\n", extra);
+}
+
+/**
+ * xmlCatalogErr:
+ * @catal: the Catalog entry
+ * @node: the context node
+ * @msg:  the error message
+ * @extra:  extra informations
+ *
+ * Handle a catalog error
+ */
+static void
+xmlCatalogErr(xmlCatalogEntryPtr catal, xmlNodePtr node, int error,
+               const char *msg, const xmlChar *str1, const xmlChar *str2,
+              const xmlChar *str3)
+{
+    __xmlRaiseError(NULL, NULL, NULL, catal, node, XML_FROM_CATALOG,
+                    error, XML_ERR_ERROR, NULL, 0,
+                   (const char *) str1, (const char *) str2,
+                   (const char *) str3, 0, 0,
+                   msg, str1, str2, str3);
+}
+
+
+/************************************************************************
+ *                                                                     *
+ *                     Allocation and Freeing                          *
+ *                                                                     *
+ ************************************************************************/
+
+/**
+ * xmlNewCatalogEntry:
+ * @type:  type of entry
+ * @name:  name of the entry
+ * @value:  value of the entry
+ * @prefer:  the PUBLIC vs. SYSTEM current preference value
+ * @group:  for members of a group, the group entry 
+ *
+ * create a new Catalog entry, this type is shared both by XML and 
+ * SGML catalogs, but the acceptable types values differs.
+ *
+ * Returns the xmlCatalogEntryPtr or NULL in case of error
+ */
+static xmlCatalogEntryPtr
+xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name,
+          const xmlChar *value, const xmlChar *URL, xmlCatalogPrefer prefer,
+          xmlCatalogEntryPtr group) {
+    xmlCatalogEntryPtr ret;
+    xmlChar *normid = NULL;
+
+    ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));
+    if (ret == NULL) {
+        xmlCatalogErrMemory("allocating catalog entry");
+       return(NULL);
+    }
+    ret->next = NULL;
+    ret->parent = NULL;
+    ret->children = NULL;
+    ret->type = type;
+    if (type == XML_CATA_PUBLIC || type == XML_CATA_DELEGATE_PUBLIC) {
+        normid = xmlCatalogNormalizePublic(name);
+        if (normid != NULL)
+            name = (*normid != 0 ? normid : NULL);
+    }
+    if (name != NULL)
+       ret->name = xmlStrdup(name);
+    else
+       ret->name = NULL;
+    if (normid != NULL)
+        xmlFree(normid);
+    if (value != NULL)
+       ret->value = xmlStrdup(value);
+    else
+       ret->value = NULL;
+    if (URL == NULL)
+       URL = value;
+    if (URL != NULL)
+       ret->URL = xmlStrdup(URL);
+    else
+       ret->URL = NULL;
+    ret->prefer = prefer;
+    ret->dealloc = 0;
+    ret->depth = 0;
+    ret->group = group;
+    return(ret);
+}
+
+static void
+xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret);
+
+/**
+ * xmlFreeCatalogEntry:
+ * @ret:  a Catalog entry
+ *
+ * Free the memory allocated to a Catalog entry
+ */
+static void
+xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) {
+    if (ret == NULL)
+       return;
+    /*
+     * Entries stored in the file hash must be deallocated
+     * only by the file hash cleaner !
+     */
+    if (ret->dealloc == 1)
+       return;
+
+    if (xmlDebugCatalogs) {
+       if (ret->name != NULL)
+           xmlGenericError(xmlGenericErrorContext,
+                   "Free catalog entry %s\n", ret->name);
+       else if (ret->value != NULL)
+           xmlGenericError(xmlGenericErrorContext,
+                   "Free catalog entry %s\n", ret->value);
+       else
+           xmlGenericError(xmlGenericErrorContext,
+                   "Free catalog entry\n");
+    }
+
+    if (ret->name != NULL)
+       xmlFree(ret->name);
+    if (ret->value != NULL)
+       xmlFree(ret->value);
+    if (ret->URL != NULL)
+       xmlFree(ret->URL);
+    xmlFree(ret);
+}
+
+/**
+ * xmlFreeCatalogEntryList:
+ * @ret:  a Catalog entry list
+ *
+ * Free the memory allocated to a full chained list of Catalog entries
+ */
+static void
+xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) {
+    xmlCatalogEntryPtr next;
+
+    while (ret != NULL) {
+       next = ret->next;
+       xmlFreeCatalogEntry(ret);
+       ret = next;
+    }
+}
+
+/**
+ * xmlFreeCatalogHashEntryList:
+ * @ret:  a Catalog entry list
+ *
+ * Free the memory allocated to list of Catalog entries from the
+ * catalog file hash.
+ */
+static void
+xmlFreeCatalogHashEntryList(xmlCatalogEntryPtr catal) {
+    xmlCatalogEntryPtr children, next;
+
+    if (catal == NULL)
+       return;
+
+    children = catal->children;
+    while (children != NULL) {
+       next = children->next;
+       children->dealloc = 0;
+       children->children = NULL;
+       xmlFreeCatalogEntry(children);
+       children = next;
+    }
+    catal->dealloc = 0;
+    xmlFreeCatalogEntry(catal);
+}
+
+/**
+ * xmlCreateNewCatalog:
+ * @type:  type of catalog
+ * @prefer:  the PUBLIC vs. SYSTEM current preference value
+ *
+ * create a new Catalog, this type is shared both by XML and 
+ * SGML catalogs, but the acceptable types values differs.
+ *
+ * Returns the xmlCatalogPtr or NULL in case of error
+ */
+static xmlCatalogPtr
+xmlCreateNewCatalog(xmlCatalogType type, xmlCatalogPrefer prefer) {
+    xmlCatalogPtr ret;
+
+    ret = (xmlCatalogPtr) xmlMalloc(sizeof(xmlCatalog));
+    if (ret == NULL) {
+        xmlCatalogErrMemory("allocating catalog");
+       return(NULL);
+    }
+    memset(ret, 0, sizeof(xmlCatalog));
+    ret->type = type;
+    ret->catalNr = 0;
+    ret->catalMax = XML_MAX_SGML_CATA_DEPTH;
+    ret->prefer = prefer;
+    if (ret->type == XML_SGML_CATALOG_TYPE)
+       ret->sgml = xmlHashCreate(10);
+    return(ret);
+}
+
+/**
+ * xmlFreeCatalog:
+ * @catal:  a Catalog
+ *
+ * Free the memory allocated to a Catalog
+ */
+void
+xmlFreeCatalog(xmlCatalogPtr catal) {
+    if (catal == NULL)
+       return;
+    if (catal->xml != NULL)
+       xmlFreeCatalogEntryList(catal->xml);
+    if (catal->sgml != NULL)
+       xmlHashFree(catal->sgml,
+               (xmlHashDeallocator) xmlFreeCatalogEntry);
+    xmlFree(catal);
+}
+
+/************************************************************************
+ *                                                                     *
+ *                     Serializing Catalogs                            *
+ *                                                                     *
+ ************************************************************************/
+
+#ifdef LIBXML_OUTPUT_ENABLED
+/**
+ * xmlCatalogDumpEntry:
+ * @entry:  the catalog entry
+ * @out:  the file.
+ *
+ * Serialize an SGML Catalog entry
+ */
+static void
+xmlCatalogDumpEntry(xmlCatalogEntryPtr entry, FILE *out) {
+    if ((entry == NULL) || (out == NULL))
+       return;
+    switch (entry->type) {
+       case SGML_CATA_ENTITY:
+           fprintf(out, "ENTITY "); break;
+       case SGML_CATA_PENTITY:
+           fprintf(out, "ENTITY %%"); break;
+       case SGML_CATA_DOCTYPE:
+           fprintf(out, "DOCTYPE "); break;
+       case SGML_CATA_LINKTYPE:
+           fprintf(out, "LINKTYPE "); break;
+       case SGML_CATA_NOTATION:
+           fprintf(out, "NOTATION "); break;
+       case SGML_CATA_PUBLIC:
+           fprintf(out, "PUBLIC "); break;
+       case SGML_CATA_SYSTEM:
+           fprintf(out, "SYSTEM "); break;
+       case SGML_CATA_DELEGATE:
+           fprintf(out, "DELEGATE "); break;
+       case SGML_CATA_BASE:
+           fprintf(out, "BASE "); break;
+       case SGML_CATA_CATALOG:
+           fprintf(out, "CATALOG "); break;
+       case SGML_CATA_DOCUMENT:
+           fprintf(out, "DOCUMENT "); break;
+       case SGML_CATA_SGMLDECL:
+           fprintf(out, "SGMLDECL "); break;
+       default:
+           return;
+    }
+    switch (entry->type) {
+       case SGML_CATA_ENTITY:
+       case SGML_CATA_PENTITY:
+       case SGML_CATA_DOCTYPE:
+       case SGML_CATA_LINKTYPE:
+       case SGML_CATA_NOTATION:
+           fprintf(out, "%s", (const char *) entry->name); break;
+       case SGML_CATA_PUBLIC:
+       case SGML_CATA_SYSTEM:
+       case SGML_CATA_SGMLDECL:
+       case SGML_CATA_DOCUMENT:
+       case SGML_CATA_CATALOG:
+       case SGML_CATA_BASE:
+       case SGML_CATA_DELEGATE:
+           fprintf(out, "\"%s\"", entry->name); break;
+       default:
+           break;
+    }
+    switch (entry->type) {
+       case SGML_CATA_ENTITY:
+       case SGML_CATA_PENTITY:
+       case SGML_CATA_DOCTYPE:
+       case SGML_CATA_LINKTYPE:
+       case SGML_CATA_NOTATION:
+       case SGML_CATA_PUBLIC:
+       case SGML_CATA_SYSTEM:
+       case SGML_CATA_DELEGATE:
+           fprintf(out, " \"%s\"", entry->value); break;
+       default:
+           break;
+    }
+    fprintf(out, "\n");
+}
+
+/**
+ * xmlDumpXMLCatalogNode:
+ * @catal:  top catalog entry
+ * @catalog: pointer to the xml tree
+ * @doc: the containing document
+ * @ns: the current namespace
+ * @cgroup: group node for group members
+ *
+ * Serializes a Catalog entry, called by xmlDumpXMLCatalog and recursively
+ * for group entries
+ */
+static void xmlDumpXMLCatalogNode(xmlCatalogEntryPtr catal, xmlNodePtr catalog,
+                   xmlDocPtr doc, xmlNsPtr ns, xmlCatalogEntryPtr cgroup) {
+    xmlNodePtr node;
+    xmlCatalogEntryPtr cur;
+    /*
+     * add all the catalog entries
+     */
+    cur = catal;
+    while (cur != NULL) {
+        if (cur->group == cgroup) {
+           switch (cur->type) {
+               case XML_CATA_REMOVED:
+                   break;
+               case XML_CATA_BROKEN_CATALOG:
+               case XML_CATA_CATALOG:
+                   if (cur == catal) {
+                       cur = cur->children;
+                       continue;
+                   }
+                   break;
+               case XML_CATA_NEXT_CATALOG:
+                   node = xmlNewDocNode(doc, ns, BAD_CAST "nextCatalog", NULL);
+                   xmlSetProp(node, BAD_CAST "catalog", cur->value);
+                   xmlAddChild(catalog, node);
+                    break;
+               case XML_CATA_NONE:
+                   break;
+               case XML_CATA_GROUP:
+                   node = xmlNewDocNode(doc, ns, BAD_CAST "group", NULL);
+                   xmlSetProp(node, BAD_CAST "id", cur->name);
+                   if (cur->value != NULL) {
+                       xmlNsPtr xns;
+                       xns = xmlSearchNsByHref(doc, node, XML_XML_NAMESPACE);
+                       if (xns != NULL)
+                           xmlSetNsProp(node, xns, BAD_CAST "base",
+                                        cur->value);
+                   }
+                   switch (cur->prefer) {
+                       case XML_CATA_PREFER_NONE:
+                           break;
+                       case XML_CATA_PREFER_PUBLIC:
+                           xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "public");
+                           break;
+                       case XML_CATA_PREFER_SYSTEM:
+                           xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "system");
+                           break;
+                   }
+                   xmlDumpXMLCatalogNode(cur->next, node, doc, ns, cur);
+                   xmlAddChild(catalog, node);
+                   break;
+               case XML_CATA_PUBLIC:
+                   node = xmlNewDocNode(doc, ns, BAD_CAST "public", NULL);
+                   xmlSetProp(node, BAD_CAST "publicId", cur->name);
+                   xmlSetProp(node, BAD_CAST "uri", cur->value);
+                   xmlAddChild(catalog, node);
+                   break;
+               case XML_CATA_SYSTEM:
+                   node = xmlNewDocNode(doc, ns, BAD_CAST "system", NULL);
+                   xmlSetProp(node, BAD_CAST "systemId", cur->name);
+                   xmlSetProp(node, BAD_CAST "uri", cur->value);
+                   xmlAddChild(catalog, node);
+                   break;
+               case XML_CATA_REWRITE_SYSTEM:
+                   node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteSystem", NULL);
+                   xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
+                   xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
+                   xmlAddChild(catalog, node);
+                   break;
+               case XML_CATA_DELEGATE_PUBLIC:
+                   node = xmlNewDocNode(doc, ns, BAD_CAST "delegatePublic", NULL);
+                   xmlSetProp(node, BAD_CAST "publicIdStartString", cur->name);
+                   xmlSetProp(node, BAD_CAST "catalog", cur->value);
+                   xmlAddChild(catalog, node);
+                   break;
+               case XML_CATA_DELEGATE_SYSTEM:
+                   node = xmlNewDocNode(doc, ns, BAD_CAST "delegateSystem", NULL);
+                   xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
+                   xmlSetProp(node, BAD_CAST "catalog", cur->value);
+                   xmlAddChild(catalog, node);
+                   break;
+               case XML_CATA_URI:
+                   node = xmlNewDocNode(doc, ns, BAD_CAST "uri", NULL);
+                   xmlSetProp(node, BAD_CAST "name", cur->name);
+                   xmlSetProp(node, BAD_CAST "uri", cur->value);
+                   xmlAddChild(catalog, node);
+                   break;
+               case XML_CATA_REWRITE_URI:
+                   node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteURI", NULL);
+                   xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
+                   xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
+                   xmlAddChild(catalog, node);
+                   break;
+               case XML_CATA_DELEGATE_URI:
+                   node = xmlNewDocNode(doc, ns, BAD_CAST "delegateURI", NULL);
+                   xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
+                   xmlSetProp(node, BAD_CAST "catalog", cur->value);
+                   xmlAddChild(catalog, node);
+                   break;
+               case SGML_CATA_SYSTEM:
+               case SGML_CATA_PUBLIC:
+               case SGML_CATA_ENTITY:
+               case SGML_CATA_PENTITY:
+               case SGML_CATA_DOCTYPE:
+               case SGML_CATA_LINKTYPE:
+               case SGML_CATA_NOTATION:
+               case SGML_CATA_DELEGATE:
+               case SGML_CATA_BASE:
+               case SGML_CATA_CATALOG:
+               case SGML_CATA_DOCUMENT:
+               case SGML_CATA_SGMLDECL:
+                   break;
+           }
+        }
+       cur = cur->next;
+    }
+}
+
+static int
+xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) {
+    int ret;
+    xmlDocPtr doc;
+    xmlNsPtr ns;
+    xmlDtdPtr dtd;
+    xmlNodePtr catalog;
+    xmlOutputBufferPtr buf;
+
+    /*
+     * Rebuild a catalog
+     */
+    doc = xmlNewDoc(NULL);
+    if (doc == NULL)
+       return(-1);
+    dtd = xmlNewDtd(doc, BAD_CAST "catalog",
+              BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN",
+BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd");
+
+    xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
+
+    ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL);
+    if (ns == NULL) {
+       xmlFreeDoc(doc);
+       return(-1);
+    }
+    catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL);
+    if (catalog == NULL) {
+       xmlFreeNs(ns);
+       xmlFreeDoc(doc);
+       return(-1);
+    }
+    catalog->nsDef = ns;
+    xmlAddChild((xmlNodePtr) doc, catalog);
+
+    xmlDumpXMLCatalogNode(catal, catalog, doc, ns, NULL);
+    
+    /*
+     * reserialize it
+     */
+    buf = xmlOutputBufferCreateFile(out, NULL);
+    if (buf == NULL) {
+       xmlFreeDoc(doc);
+       return(-1);
+    }
+    ret = xmlSaveFormatFileTo(buf, doc, NULL, 1);
+
+    /*
+     * Free it
+     */
+    xmlFreeDoc(doc);
+
+    return(ret);
+}
+#endif /* LIBXML_OUTPUT_ENABLED */
+
+/************************************************************************
+ *                                                                     *
+ *                     Converting SGML Catalogs to XML                 *
+ *                                                                     *
+ ************************************************************************/
+
+/**
+ * xmlCatalogConvertEntry:
+ * @entry:  the entry
+ * @catal:  pointer to the catalog being converted
+ *
+ * Convert one entry from the catalog
+ */
+static void
+xmlCatalogConvertEntry(xmlCatalogEntryPtr entry, xmlCatalogPtr catal) {
+    if ((entry == NULL) || (catal == NULL) || (catal->sgml == NULL) ||
+       (catal->xml == NULL))
+       return;
+    switch (entry->type) {
+       case SGML_CATA_ENTITY:
+           entry->type = XML_CATA_PUBLIC;
+           break;
+       case SGML_CATA_PENTITY:
+           entry->type = XML_CATA_PUBLIC;
+           break;
+       case SGML_CATA_DOCTYPE:
+           entry->type = XML_CATA_PUBLIC;
+           break;
+       case SGML_CATA_LINKTYPE:
+           entry->type = XML_CATA_PUBLIC;
+           break;
+       case SGML_CATA_NOTATION:
+           entry->type = XML_CATA_PUBLIC;
+           break;
+       case SGML_CATA_PUBLIC:
+           entry->type = XML_CATA_PUBLIC;
+           break;
+       case SGML_CATA_SYSTEM:
+           entry->type = XML_CATA_SYSTEM;
+           break;
+       case SGML_CATA_DELEGATE:
+           entry->type = XML_CATA_DELEGATE_PUBLIC;
+           break;
+       case SGML_CATA_CATALOG:
+           entry->type = XML_CATA_CATALOG;
+           break;
+       default:
+           xmlHashRemoveEntry(catal->sgml, entry->name,
+                              (xmlHashDeallocator) xmlFreeCatalogEntry);
+           return;
+    }
+    /*
+     * Conversion successful, remove from the SGML catalog
+     * and add it to the default XML one
+     */
+    xmlHashRemoveEntry(catal->sgml, entry->name, NULL);
+    entry->parent = catal->xml;
+    entry->next = NULL;
+    if (catal->xml->children == NULL)
+       catal->xml->children = entry;
+    else {
+       xmlCatalogEntryPtr prev;
+
+       prev = catal->xml->children;
+       while (prev->next != NULL)
+           prev = prev->next;
+       prev->next = entry;
+    }
+}
+
+/**
+ * xmlConvertSGMLCatalog:
+ * @catal: the catalog
+ *
+ * Convert all the SGML catalog entries as XML ones
+ *
+ * Returns the number of entries converted if successful, -1 otherwise
+ */
+int
+xmlConvertSGMLCatalog(xmlCatalogPtr catal) {
+
+    if ((catal == NULL) || (catal->type != XML_SGML_CATALOG_TYPE))
+       return(-1);
+
+    if (xmlDebugCatalogs) {
+       xmlGenericError(xmlGenericErrorContext,
+               "Converting SGML catalog to XML\n");
+    }
+    xmlHashScan(catal->sgml,
+               (xmlHashScanner) xmlCatalogConvertEntry,
+               &catal);
+    return(0);
+}
+
+/************************************************************************
+ *                                                                     *
+ *                     Helper function                                 *
+ *                                                                     *
+ ************************************************************************/
+
+/**
+ * xmlCatalogUnWrapURN:
+ * @urn:  an "urn:publicid:" to unwrap
+ *
+ * Expand the URN into the equivalent Public Identifier
+ *
+ * Returns the new identifier or NULL, the string must be deallocated
+ *         by the caller.
+ */
+static xmlChar *
+xmlCatalogUnWrapURN(const xmlChar *urn) {
+    xmlChar result[2000];
+    unsigned int i = 0;
+
+    if (xmlStrncmp(urn, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1))
+       return(NULL);
+    urn += sizeof(XML_URN_PUBID) - 1;
+    
+    while (*urn != 0) {
+       if (i > sizeof(result) - 4)
+           break;
+       if (*urn == '+') {
+           result[i++] = ' ';
+           urn++;
+       } else if (*urn == ':') {
+           result[i++] = '/';
+           result[i++] = '/';
+           urn++;
+       } else if (*urn == ';') {
+           result[i++] = ':';
+           result[i++] = ':';
+           urn++;
+       } else if (*urn == '%') {
+           if ((urn[1] == '2') && (urn[2] == 'B'))
+               result[i++] = '+';
+           else if ((urn[1] == '3') && (urn[2] == 'A'))
+               result[i++] = ':';
+           else if ((urn[1] == '2') && (urn[2] == 'F'))
+               result[i++] = '/';
+           else if ((urn[1] == '3') && (urn[2] == 'B'))
+               result[i++] = ';';
+           else if ((urn[1] == '2') && (urn[2] == '7'))
+               result[i++] = '\'';
+           else if ((urn[1] == '3') && (urn[2] == 'F'))
+               result[i++] = '?';
+           else if ((urn[1] == '2') && (urn[2] == '3'))
+               result[i++] = '#';
+           else if ((urn[1] == '2') && (urn[2] == '5'))
+               result[i++] = '%';
+           else {
+               result[i++] = *urn;
+               urn++;
+               continue;
+           }
+           urn += 3;
+       } else {
+           result[i++] = *urn;
+           urn++;
+       }
+    }
+    result[i] = 0;
+
+    return(xmlStrdup(result));
+}
+
+/**
+ * xmlParseCatalogFile:
+ * @filename:  the filename
+ *
+ * parse an XML file and build a tree. It's like xmlParseFile()
+ * except it bypass all catalog lookups.
+ *
+ * Returns the resulting document tree or NULL in case of error
+ */
+
+xmlDocPtr
+xmlParseCatalogFile(const char *filename) {
+    xmlDocPtr ret;
+    xmlParserCtxtPtr ctxt;
+    char *directory = NULL;
+    xmlParserInputPtr inputStream;
+    xmlParserInputBufferPtr buf;
+
+    ctxt = xmlNewParserCtxt();
+    if (ctxt == NULL) {
+#ifdef LIBXML_SAX1_ENABLED
+       if (xmlDefaultSAXHandler.error != NULL) {
+           xmlDefaultSAXHandler.error(NULL, "out of memory\n");
+       }
+#endif
+       return(NULL);
+    }
+
+    buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE);
+    if (buf == NULL) {
+       xmlFreeParserCtxt(ctxt);
+       return(NULL);
+    }
+
+    inputStream = xmlNewInputStream(ctxt);
+    if (inputStream == NULL) {
+       xmlFreeParserCtxt(ctxt);
+       return(NULL);
+    }
+
+    inputStream->filename = (char *) xmlCanonicPath((const xmlChar *)filename);
+    inputStream->buf = buf;
+    inputStream->base = inputStream->buf->buffer->content;
+    inputStream->cur = inputStream->buf->buffer->content;
+    inputStream->end = 
+       &inputStream->buf->buffer->content[inputStream->buf->buffer->use];
+
+    inputPush(ctxt, inputStream);
+    if ((ctxt->directory == NULL) && (directory == NULL))
+        directory = xmlParserGetDirectory(filename);
+    if ((ctxt->directory == NULL) && (directory != NULL))
+        ctxt->directory = directory;
+    ctxt->valid = 0;
+    ctxt->validate = 0;
+    ctxt->loadsubset = 0;
+    ctxt->pedantic = 0;
+    ctxt->dictNames = 1;
+
+    xmlParseDocument(ctxt);
+
+    if (ctxt->wellFormed)
+       ret = ctxt->myDoc;
+    else {
+        ret = NULL;
+        xmlFreeDoc(ctxt->myDoc);
+        ctxt->myDoc = NULL;
+    }
+    xmlFreeParserCtxt(ctxt);
+    
+    return(ret);
+}
+
+/**
+ * xmlLoadFileContent:
+ * @filename:  a file path
+ *
+ * Load a file content into memory.
+ *
+ * Returns a pointer to the 0 terminated string or NULL in case of error
+ */
+static xmlChar *
+xmlLoadFileContent(const char *filename)
+{
+#ifdef HAVE_STAT
+    int fd;
+#else
+    FILE *fd;
+#endif
+    int len;
+    long size;
+
+#ifdef HAVE_STAT
+    struct stat info;
+#endif
+    xmlChar *content;
+
+    if (filename == NULL)
+        return (NULL);
+
+#ifdef HAVE_STAT
+    if (stat(filename, &info) < 0)
+        return (NULL);
+#endif
+
+#ifdef HAVE_STAT
+    if ((fd = open(filename, O_RDONLY)) < 0)
+#else
+    if ((fd = fopen(filename, "rb")) == NULL)
+#endif
+    {
+        return (NULL);
+    }
+#ifdef HAVE_STAT
+    size = info.st_size;
+#else
+    if (fseek(fd, 0, SEEK_END) || (size = ftell(fd)) == EOF || fseek(fd, 0, SEEK_SET)) {        /* File operations denied? ok, just close and return failure */
+        fclose(fd);
+        return (NULL);
+    }
+#endif
+    content = xmlMallocAtomic(size + 10);
+    if (content == NULL) {
+        xmlCatalogErrMemory("allocating catalog data");
+        return (NULL);
+    }
+#ifdef HAVE_STAT
+    len = read(fd, content, size);
+#else
+    len = fread(content, 1, size, fd);
+#endif
+    if (len < 0) {
+        xmlFree(content);
+        return (NULL);
+    }
+#ifdef HAVE_STAT
+    close(fd);
+#else
+    fclose(fd);
+#endif
+    content[len] = 0;
+
+    return(content);
+}
+
+/**
+ * xmlCatalogNormalizePublic:
+ * @pubID:  the public ID string
+ *
+ *  Normalizes the Public Identifier
+ *
+ * Implements 6.2. Public Identifier Normalization
+ * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
+ *
+ * Returns the new string or NULL, the string must be deallocated
+ *         by the caller.
+ */
+static xmlChar *
+xmlCatalogNormalizePublic(const xmlChar *pubID)
+{
+    int ok = 1;
+    int white;
+    const xmlChar *p;
+    xmlChar *ret;
+    xmlChar *q;
+
+    if (pubID == NULL)
+        return(NULL);
+
+    white = 1;
+    for (p = pubID;*p != 0 && ok;p++) {
+        if (!xmlIsBlank_ch(*p))
+            white = 0;
+        else if (*p == 0x20 && !white)
+            white = 1;
+        else
+            ok = 0;
+    }
+    if (ok && !white)  /* is normalized */
+        return(NULL);
+
+    ret = xmlStrdup(pubID);
+    q = ret;
+    white = 0;
+    for (p = pubID;*p != 0;p++) {
+        if (xmlIsBlank_ch(*p)) {
+            if (q != ret)
+                white = 1;
+        } else {
+            if (white) {
+                *(q++) = 0x20;
+                white = 0;
+            }
+            *(q++) = *p;
+        }
+    }
+    *q = 0;
+    return(ret);
+}
+
+/************************************************************************
+ *                                                                     *
+ *                     The XML Catalog parser                          *
+ *                                                                     *
+ ************************************************************************/
+
+static xmlCatalogEntryPtr
+xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
+static void
+xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
+                          xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup);
+static xmlChar *
+xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
+                     const xmlChar *sysID);
+static xmlChar *
+xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI);
+
+
+/**
+ * xmlGetXMLCatalogEntryType:
+ * @name:  the name
+ *
+ * lookup the internal type associated to an XML catalog entry name
+ *
+ * Returns the type associated with that name
+ */
+static xmlCatalogEntryType
+xmlGetXMLCatalogEntryType(const xmlChar *name) {
+    xmlCatalogEntryType type = XML_CATA_NONE;
+    if (xmlStrEqual(name, (const xmlChar *) "system"))
+       type = XML_CATA_SYSTEM;
+    else if (xmlStrEqual(name, (const xmlChar *) "public"))
+       type = XML_CATA_PUBLIC;
+    else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem"))
+       type = XML_CATA_REWRITE_SYSTEM;
+    else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic"))
+       type = XML_CATA_DELEGATE_PUBLIC;
+    else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem"))
+       type = XML_CATA_DELEGATE_SYSTEM;
+    else if (xmlStrEqual(name, (const xmlChar *) "uri"))
+       type = XML_CATA_URI;
+    else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI"))
+       type = XML_CATA_REWRITE_URI;
+    else if (xmlStrEqual(name, (const xmlChar *) "delegateURI"))
+       type = XML_CATA_DELEGATE_URI;
+    else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog"))
+       type = XML_CATA_NEXT_CATALOG;
+    else if (xmlStrEqual(name, (const xmlChar *) "catalog"))
+       type = XML_CATA_CATALOG;
+    return(type);
+}
+
+/**
+ * xmlParseXMLCatalogOneNode:
+ * @cur:  the XML node
+ * @type:  the type of Catalog entry
+ * @name:  the name of the node
+ * @attrName:  the attribute holding the value
+ * @uriAttrName:  the attribute holding the URI-Reference
+ * @prefer:  the PUBLIC vs. SYSTEM current preference value
+ * @cgroup:  the group which includes this node
+ *
+ * Finishes the examination of an XML tree node of a catalog and build
+ * a Catalog entry from it.
+ *
+ * Returns the new Catalog entry node or NULL in case of error.
+ */
+static xmlCatalogEntryPtr
+xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type,
+                         const xmlChar *name, const xmlChar *attrName,
+                         const xmlChar *uriAttrName, xmlCatalogPrefer prefer,
+                         xmlCatalogEntryPtr cgroup) {
+    int ok = 1;
+    xmlChar *uriValue;
+    xmlChar *nameValue = NULL;
+    xmlChar *base = NULL;
+    xmlChar *URL = NULL;
+    xmlCatalogEntryPtr ret = NULL;
+
+    if (attrName != NULL) {
+       nameValue = xmlGetProp(cur, attrName);
+       if (nameValue == NULL) {
+           xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
+                         "%s entry lacks '%s'\n", name, attrName, NULL);
+           ok = 0;
+       }
+    }
+    uriValue = xmlGetProp(cur, uriAttrName);
+    if (uriValue == NULL) {
+       xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
+               "%s entry lacks '%s'\n", name, uriAttrName, NULL);
+       ok = 0;
+    }
+    if (!ok) {
+       if (nameValue != NULL)
+           xmlFree(nameValue);
+       if (uriValue != NULL)
+           xmlFree(uriValue);
+       return(NULL);
+    }
+
+    base = xmlNodeGetBase(cur->doc, cur);
+    URL = xmlBuildURI(uriValue, base);
+    if (URL != NULL) {
+       if (xmlDebugCatalogs > 1) {
+           if (nameValue != NULL)
+               xmlGenericError(xmlGenericErrorContext,
+                       "Found %s: '%s' '%s'\n", name, nameValue, URL);
+           else
+               xmlGenericError(xmlGenericErrorContext,
+                       "Found %s: '%s'\n", name, URL);
+       }
+       ret = xmlNewCatalogEntry(type, nameValue, uriValue, URL, prefer, cgroup);
+    } else {
+       xmlCatalogErr(ret, cur, XML_CATALOG_ENTRY_BROKEN,
+               "%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue);
+    }
+    if (nameValue != NULL)
+       xmlFree(nameValue);
+    if (uriValue != NULL)
+       xmlFree(uriValue);
+    if (base != NULL)
+       xmlFree(base);
+    if (URL != NULL)
+       xmlFree(URL);
+    return(ret);
+}
+
+/**
+ * xmlParseXMLCatalogNode:
+ * @cur:  the XML node
+ * @prefer:  the PUBLIC vs. SYSTEM current preference value
+ * @parent:  the parent Catalog entry
+ * @cgroup:  the group which includes this node
+ *
+ * Examines an XML tree node of a catalog and build
+ * a Catalog entry from it adding it to its parent. The examination can
+ * be recursive.
+ */
+static void
+xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer,
+                      xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup)
+{
+    xmlChar *uri = NULL;
+    xmlChar *URL = NULL;
+    xmlChar *base = NULL;
+    xmlCatalogEntryPtr entry = NULL;
+
+    if (cur == NULL)
+        return;
+    if (xmlStrEqual(cur->name, BAD_CAST "group")) {
+        xmlChar *prop;
+       xmlCatalogPrefer pref = XML_CATA_PREFER_NONE;
+
+        prop = xmlGetProp(cur, BAD_CAST "prefer");
+        if (prop != NULL) {
+            if (xmlStrEqual(prop, BAD_CAST "system")) {
+                prefer = XML_CATA_PREFER_SYSTEM;
+            } else if (xmlStrEqual(prop, BAD_CAST "public")) {
+                prefer = XML_CATA_PREFER_PUBLIC;
+            } else {
+               xmlCatalogErr(parent, cur, XML_CATALOG_PREFER_VALUE,
+                              "Invalid value for prefer: '%s'\n",
+                             prop, NULL, NULL);
+            }
+            xmlFree(prop);
+           pref = prefer;
+        }
+       prop = xmlGetProp(cur, BAD_CAST "id");
+       base = xmlGetNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE);
+       entry = xmlNewCatalogEntry(XML_CATA_GROUP, prop, base, NULL, pref, cgroup);
+       xmlFree(prop);
+    } else if (xmlStrEqual(cur->name, BAD_CAST "public")) {
+       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC,
+               BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer, cgroup);
+    } else if (xmlStrEqual(cur->name, BAD_CAST "system")) {
+       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM,
+               BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer, cgroup);
+    } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) {
+       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM,
+               BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString",
+               BAD_CAST "rewritePrefix", prefer, cgroup);
+    } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) {
+       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC,
+               BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString",
+               BAD_CAST "catalog", prefer, cgroup);
+    } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) {
+       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM,
+               BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString",
+               BAD_CAST "catalog", prefer, cgroup);
+    } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {
+       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,
+               BAD_CAST "uri", BAD_CAST "name",
+               BAD_CAST "uri", prefer, cgroup);
+    } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) {
+       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI,
+               BAD_CAST "rewriteURI", BAD_CAST "uriStartString",
+               BAD_CAST "rewritePrefix", prefer, cgroup);
+    } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) {
+       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI,
+               BAD_CAST "delegateURI", BAD_CAST "uriStartString",
+               BAD_CAST "catalog", prefer, cgroup);
+    } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
+       entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
+               BAD_CAST "nextCatalog", NULL,
+               BAD_CAST "catalog", prefer, cgroup);
+    }
+    if (entry != NULL) {
+        if (parent != NULL) {
+           entry->parent = parent;
+           if (parent->children == NULL)
+               parent->children = entry;
+           else {
+               xmlCatalogEntryPtr prev;
+
+               prev = parent->children;
+               while (prev->next != NULL)
+                   prev = prev->next;
+               prev->next = entry;
+           }
+       }
+       if (entry->type == XML_CATA_GROUP) {
+           /*
+            * Recurse to propagate prefer to the subtree
+            * (xml:base handling is automated)
+            */
+            xmlParseXMLCatalogNodeList(cur->children, prefer, parent, entry);
+       }
+    }
+    if (base != NULL)
+       xmlFree(base);
+    if (uri != NULL)
+       xmlFree(uri);
+    if (URL != NULL)
+       xmlFree(URL);
+}
+
+/**
+ * xmlParseXMLCatalogNodeList:
+ * @cur:  the XML node list of siblings
+ * @prefer:  the PUBLIC vs. SYSTEM current preference value
+ * @parent:  the parent Catalog entry
+ * @cgroup:  the group which includes this list
+ *
+ * Examines a list of XML sibling nodes of a catalog and build
+ * a list of Catalog entry from it adding it to the parent.
+ * The examination will recurse to examine node subtrees.
+ */
+static void
+xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
+                          xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup) {
+    while (cur != NULL) {
+       if ((cur->ns != NULL) && (cur->ns->href != NULL) &&
+           (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
+           xmlParseXMLCatalogNode(cur, prefer, parent, cgroup);
+       }
+       cur = cur->next;
+    }
+    /* TODO: sort the list according to REWRITE lengths and prefer value */
+}
+
+/**
+ * xmlParseXMLCatalogFile:
+ * @prefer:  the PUBLIC vs. SYSTEM current preference value
+ * @filename:  the filename for the catalog
+ *
+ * Parses the catalog file to extract the XML tree and then analyze the
+ * tree to build a list of Catalog entries corresponding to this catalog
+ * 
+ * Returns the resulting Catalog entries list
+ */
+static xmlCatalogEntryPtr
+xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) {
+    xmlDocPtr doc;
+    xmlNodePtr cur;
+    xmlChar *prop;
+    xmlCatalogEntryPtr parent = NULL;
+
+    if (filename == NULL)
+        return(NULL);
+
+    doc = xmlParseCatalogFile((const char *) filename);
+    if (doc == NULL) {
+       if (xmlDebugCatalogs)
+           xmlGenericError(xmlGenericErrorContext,
+                   "Failed to parse catalog %s\n", filename);
+       return(NULL);
+    }
+
+    if (xmlDebugCatalogs)
+       xmlGenericError(xmlGenericErrorContext,
+               "%d Parsing catalog %s\n", xmlGetThreadId(), filename);
+
+    cur = xmlDocGetRootElement(doc);
+    if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) &&
+       (cur->ns != NULL) && (cur->ns->href != NULL) &&
+       (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
+
+       parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
+                                   (const xmlChar *)filename, NULL, prefer, NULL);
+        if (parent == NULL) {
+           xmlFreeDoc(doc);
+           return(NULL);
+       }
+
+       prop = xmlGetProp(cur, BAD_CAST "prefer");
+       if (prop != NULL) {
+           if (xmlStrEqual(prop, BAD_CAST "system")) {
+               prefer = XML_CATA_PREFER_SYSTEM;
+           } else if (xmlStrEqual(prop, BAD_CAST "public")) {
+               prefer = XML_CATA_PREFER_PUBLIC;
+           } else {
+               xmlCatalogErr(NULL, cur, XML_CATALOG_PREFER_VALUE,
+                             "Invalid value for prefer: '%s'\n",
+                             prop, NULL, NULL);
+           }
+           xmlFree(prop);
+       }
+       cur = cur->children;
+       xmlParseXMLCatalogNodeList(cur, prefer, parent, NULL);
+    } else {
+       xmlCatalogErr(NULL, (xmlNodePtr) doc, XML_CATALOG_NOT_CATALOG,
+                     "File %s is not an XML Catalog\n",
+                     filename, NULL, NULL);
+       xmlFreeDoc(doc);
+       return(NULL);
+    }
+    xmlFreeDoc(doc);
+    return(parent);
+}
+
+/**
+ * xmlFetchXMLCatalogFile:
+ * @catal:  an existing but incomplete catalog entry
+ *
+ * Fetch and parse the subcatalog referenced by an entry
+ * 
+ * Returns 0 in case of success, -1 otherwise
+ */
+static int
+xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) {
+    xmlCatalogEntryPtr doc;
+
+    if (catal == NULL) 
+       return(-1);
+    if (catal->URL == NULL)
+       return(-1);
+    if (catal->children != NULL)
+       return(-1);
+
+    /*
+     * lock the whole catalog for modification
+     */
+    xmlRMutexLock(xmlCatalogMutex);
+    if (catal->children != NULL) {
+       /* Okay someone else did it in the meantime */
+       xmlRMutexUnlock(xmlCatalogMutex);
+       return(0);
+    }
+
+    if (xmlCatalogXMLFiles != NULL) {
+       doc = (xmlCatalogEntryPtr)
+           xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
+       if (doc != NULL) {
+           if (xmlDebugCatalogs)
+               xmlGenericError(xmlGenericErrorContext,
+                   "Found %s in file hash\n", catal->URL);
+
+           if (catal->type == XML_CATA_CATALOG)
+               catal->children = doc->children;
+           else
+               catal->children = doc;
+           catal->dealloc = 0;
+           xmlRMutexUnlock(xmlCatalogMutex);
+           return(0);
+       }
+       if (xmlDebugCatalogs)
+           xmlGenericError(xmlGenericErrorContext,
+               "%s not found in file hash\n", catal->URL);
+    }
+
+    /*
+     * Fetch and parse. Note that xmlParseXMLCatalogFile does not
+     * use the existing catalog, there is no recursion allowed at
+     * that level.
+     */
+    doc = xmlParseXMLCatalogFile(catal->prefer, catal->URL);
+    if (doc == NULL) {
+       catal->type = XML_CATA_BROKEN_CATALOG;
+       xmlRMutexUnlock(xmlCatalogMutex);
+       return(-1);
+    }
+
+    if (catal->type == XML_CATA_CATALOG)
+       catal->children = doc->children;
+    else
+       catal->children = doc;
+
+    doc->dealloc = 1;
+
+    if (xmlCatalogXMLFiles == NULL)
+       xmlCatalogXMLFiles = xmlHashCreate(10);
+    if (xmlCatalogXMLFiles != NULL) {
+       if (xmlDebugCatalogs)
+           xmlGenericError(xmlGenericErrorContext,
+               "%s added to file hash\n", catal->URL);
+       xmlHashAddEntry(xmlCatalogXMLFiles, catal->URL, doc);
+    }
+    xmlRMutexUnlock(xmlCatalogMutex);
+    return(0);
+}
+
+/************************************************************************
+ *                                                                     *
+ *                     XML Catalog handling                            *
+ *                                                                     *
+ ************************************************************************/
+
+/**
+ * xmlAddXMLCatalog:
+ * @catal:  top of an XML catalog
+ * @type:  the type of record to add to the catalog
+ * @orig:  the system, public or prefix to match (or NULL)
+ * @replace:  the replacement value for the match
+ *
+ * Add an entry in the XML catalog, it may overwrite existing but
+ * different entries.
+ *
+ * Returns 0 if successful, -1 otherwise
+ */
+static int
+xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type,
+             const xmlChar *orig, const xmlChar *replace) {
+    xmlCatalogEntryPtr cur;
+    xmlCatalogEntryType typ;
+    int doregister = 0;
+
+    if ((catal == NULL) || 
+       ((catal->type != XML_CATA_CATALOG) &&
+        (catal->type != XML_CATA_BROKEN_CATALOG)))
+       return(-1);
+    if (catal->children == NULL) {
+       xmlFetchXMLCatalogFile(catal);
+    }
+    if (catal->children == NULL)
+       doregister = 1;
+
+    typ = xmlGetXMLCatalogEntryType(type);
+    if (typ == XML_CATA_NONE) {
+       if (xmlDebugCatalogs)
+           xmlGenericError(xmlGenericErrorContext,
+                   "Failed to add unknown element %s to catalog\n", type);
+       return(-1);
+    }
+
+    cur = catal->children;
+    /*
+     * Might be a simple "update in place"
+     */
+    if (cur != NULL) {
+       while (cur != NULL) {
+           if ((orig != NULL) && (cur->type == typ) &&
+               (xmlStrEqual(orig, cur->name))) {
+               if (xmlDebugCatalogs)
+                   xmlGenericError(xmlGenericErrorContext,
+                           "Updating element %s to catalog\n", type);
+               if (cur->value != NULL)
+                   xmlFree(cur->value);
+               if (cur->URL != NULL)
+                   xmlFree(cur->URL);
+               cur->value = xmlStrdup(replace);
+               cur->URL = xmlStrdup(replace);
+               return(0);
+           }
+           if (cur->next == NULL)
+               break;
+           cur = cur->next;
+       }
+    }
+    if (xmlDebugCatalogs)
+       xmlGenericError(xmlGenericErrorContext,
+               "Adding element %s to catalog\n", type);
+    if (cur == NULL)
+       catal->children = xmlNewCatalogEntry(typ, orig, replace,
+                                            NULL, catal->prefer, NULL);
+    else
+       cur->next = xmlNewCatalogEntry(typ, orig, replace,
+                                      NULL, catal->prefer, NULL);
+    if (doregister) {
+       cur = xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
+       if (cur != NULL)
+           cur->children = catal->children;
+    }
+
+    return(0);
+}
+
+/**
+ * xmlDelXMLCatalog:
+ * @catal:  top of an XML catalog
+ * @value:  the value to remove from the catalog
+ *
+ * Remove entries in the XML catalog where the value or the URI
+ * is equal to @value
+ *
+ * Returns the number of entries removed if successful, -1 otherwise
+ */
+static int
+xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) {
+    xmlCatalogEntryPtr cur;
+    int ret = 0;
+
+    if ((catal == NULL) || 
+       ((catal->type != XML_CATA_CATALOG) &&
+        (catal->type != XML_CATA_BROKEN_CATALOG)))
+       return(-1);
+    if (value == NULL)
+       return(-1);
+    if (catal->children == NULL) {
+       xmlFetchXMLCatalogFile(catal);
+    }
+
+    /*
+     * Scan the children
+     */
+    cur = catal->children;
+    while (cur != NULL) {
+       if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) ||
+           (xmlStrEqual(value, cur->value))) {
+           if (xmlDebugCatalogs) {
+               if (cur->name != NULL)
+                   xmlGenericError(xmlGenericErrorContext,
+                           "Removing element %s from catalog\n", cur->name);
+               else
+                   xmlGenericError(xmlGenericErrorContext,
+                           "Removing element %s from catalog\n", cur->value);
+           }
+           cur->type = XML_CATA_REMOVED;
+       }
+       cur = cur->next;
+    }
+    return(ret);
+}
+
+/**
+ * xmlCatalogXMLResolve:
+ * @catal:  a catalog list
+ * @pubID:  the public ID string
+ * @sysID:  the system ID string
+ *
+ * Do a complete resolution lookup of an External Identifier for a
+ * list of catalog entries.
+ *
+ * Implements (or tries to) 7.1. External Identifier Resolution
+ * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
+ *
+ * Returns the URI of the resource or NULL if not found
+ */
+static xmlChar *
+xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
+                     const xmlChar *sysID) {
+    xmlChar *ret = NULL;
+    xmlCatalogEntryPtr cur;
+    int haveDelegate = 0;
+    int haveNext = 0;
+
+    /*
+     * protection against loops
+     */
+    if (catal->depth > MAX_CATAL_DEPTH) {
+       xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
+                     "Detected recursion in catalog %s\n",
+                     catal->name, NULL, NULL);
+       return(NULL);
+    }
+    catal->depth++;
+
+    /*
+     * First tries steps 2/ 3/ 4/ if a system ID is provided.
+     */
+    if (sysID != NULL) {
+       xmlCatalogEntryPtr rewrite = NULL;
+       int lenrewrite = 0, len;
+       cur = catal;
+       haveDelegate = 0;
+       while (cur != NULL) {
+           switch (cur->type) {
+               case XML_CATA_SYSTEM:
+                   if (xmlStrEqual(sysID, cur->name)) {
+                       if (xmlDebugCatalogs)
+                           xmlGenericError(xmlGenericErrorContext,
+                                   "Found system match %s\n", cur->name);
+                       catal->depth--;
+                       return(xmlStrdup(cur->URL));
+                   }
+                   break;
+               case XML_CATA_REWRITE_SYSTEM:
+                   len = xmlStrlen(cur->name);
+                   if ((len > lenrewrite) &&
+                       (!xmlStrncmp(sysID, cur->name, len))) {
+                       lenrewrite = len;
+                       rewrite = cur;
+                   }
+                   break;
+               case XML_CATA_DELEGATE_SYSTEM:
+                   if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))
+                       haveDelegate++;
+                   break;
+               case XML_CATA_NEXT_CATALOG:
+                   haveNext++;
+                   break;
+               default:
+                   break;
+           }
+           cur = cur->next;
+       }
+       if (rewrite != NULL) {
+           if (xmlDebugCatalogs)
+               xmlGenericError(xmlGenericErrorContext,
+                       "Using rewriting rule %s\n", rewrite->name);
+           ret = xmlStrdup(rewrite->URL);
+           if (ret != NULL)
+               ret = xmlStrcat(ret, &sysID[lenrewrite]);
+           catal->depth--;
+           return(ret);
+       }
+       if (haveDelegate) {
+           const xmlChar *delegates[MAX_DELEGATE];
+           int nbList = 0, i;
+
+           /*
+            * Assume the entries have been sorted by decreasing substring
+            * matches when the list was produced.
+            */
+           cur = catal;
+           while (cur != NULL) {
+               if ((cur->type == XML_CATA_DELEGATE_SYSTEM) &&
+                   (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) {
+                   for (i = 0;i < nbList;i++)
+                       if (xmlStrEqual(cur->URL, delegates[i]))
+                           break;
+                   if (i < nbList) {
+                       cur = cur->next;
+                       continue;
+                   }
+                   if (nbList < MAX_DELEGATE)
+                       delegates[nbList++] = cur->URL;
+
+                   if (cur->children == NULL) {
+                       xmlFetchXMLCatalogFile(cur);
+                   }
+                   if (cur->children != NULL) {
+                       if (xmlDebugCatalogs)
+                           xmlGenericError(xmlGenericErrorContext,
+                                   "Trying system delegate %s\n", cur->URL);
+                       ret = xmlCatalogListXMLResolve(
+                               cur->children, NULL, sysID);
+                       if (ret != NULL) {
+                           catal->depth--;
+                           return(ret);
+                       }
+                   }
+               }
+               cur = cur->next;
+           }
+           /*
+            * Apply the cut algorithm explained in 4/
+            */
+           catal->depth--;
+           return(XML_CATAL_BREAK);
+       }
+    }
+    /*
+     * Then tries 5/ 6/ if a public ID is provided
+     */
+    if (pubID != NULL) {
+       cur = catal;
+       haveDelegate = 0;
+       while (cur != NULL) {
+           switch (cur->type) {
+               case XML_CATA_PUBLIC:
+                   if (xmlStrEqual(pubID, cur->name)) {
+                       if (xmlDebugCatalogs)
+                           xmlGenericError(xmlGenericErrorContext,
+                                   "Found public match %s\n", cur->name);
+                       catal->depth--;
+                       return(xmlStrdup(cur->URL));
+                   }
+                   break;
+               case XML_CATA_DELEGATE_PUBLIC:
+                   if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) &&
+                       (cur->prefer == XML_CATA_PREFER_PUBLIC))
+                       haveDelegate++;
+                   break;
+               case XML_CATA_NEXT_CATALOG:
+                   if (sysID == NULL)
+                       haveNext++;
+                   break;
+               default:
+                   break;
+           }
+           cur = cur->next;
+       }
+       if (haveDelegate) {
+           const xmlChar *delegates[MAX_DELEGATE];
+           int nbList = 0, i;
+
+           /*
+            * Assume the entries have been sorted by decreasing substring
+            * matches when the list was produced.
+            */
+           cur = catal;
+           while (cur != NULL) {
+               if ((cur->type == XML_CATA_DELEGATE_PUBLIC) &&
+                   (cur->prefer == XML_CATA_PREFER_PUBLIC) &&
+                   (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) {
+
+                   for (i = 0;i < nbList;i++)
+                       if (xmlStrEqual(cur->URL, delegates[i]))
+                           break;
+                   if (i < nbList) {
+                       cur = cur->next;
+                       continue;
+                   }
+                   if (nbList < MAX_DELEGATE)
+                       delegates[nbList++] = cur->URL;
+                           
+                   if (cur->children == NULL) {
+                       xmlFetchXMLCatalogFile(cur);
+                   }
+                   if (cur->children != NULL) {
+                       if (xmlDebugCatalogs)
+                           xmlGenericError(xmlGenericErrorContext,
+                                   "Trying public delegate %s\n", cur->URL);
+                       ret = xmlCatalogListXMLResolve(
+                               cur->children, pubID, NULL);
+                       if (ret != NULL) {
+                           catal->depth--;
+                           return(ret);
+                       }
+                   }
+               }
+               cur = cur->next;
+           }
+           /*
+            * Apply the cut algorithm explained in 4/
+            */
+           catal->depth--;
+           return(XML_CATAL_BREAK);
+       }
+    }
+    if (haveNext) {
+       cur = catal;
+       while (cur != NULL) {
+           if (cur->type == XML_CATA_NEXT_CATALOG) {
+               if (cur->children == NULL) {
+                   xmlFetchXMLCatalogFile(cur);
+               }
+               if (cur->children != NULL) {
+                   ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID);
+                   if (ret != NULL) {
+                       catal->depth--;
+                       return(ret);
+                   }
+               }
+           }
+           cur = cur->next;
+       }
+    }
+
+    catal->depth--;
+    return(NULL);
+}
+
+/**
+ * xmlCatalogXMLResolveURI:
+ * @catal:  a catalog list
+ * @URI:  the URI
+ * @sysID:  the system ID string
+ *
+ * Do a complete resolution lookup of an External Identifier for a
+ * list of catalog entries.
+ *
+ * Implements (or tries to) 7.2.2. URI Resolution
+ * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
+ *
+ * Returns the URI of the resource or NULL if not found
+ */
+static xmlChar *
+xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
+    xmlChar *ret = NULL;
+    xmlCatalogEntryPtr cur;
+    int haveDelegate = 0;
+    int haveNext = 0;
+    xmlCatalogEntryPtr rewrite = NULL;
+    int lenrewrite = 0, len;
+
+    if (catal == NULL)
+       return(NULL);
+
+    if (URI == NULL)
+       return(NULL);
+
+    /*
+     * First tries steps 2/ 3/ 4/ if a system ID is provided.
+     */
+    cur = catal;
+    haveDelegate = 0;
+    while (cur != NULL) {
+       switch (cur->type) {
+           case XML_CATA_URI:
+               if (xmlStrEqual(URI, cur->name)) {
+                   if (xmlDebugCatalogs)
+                       xmlGenericError(xmlGenericErrorContext,
+                               "Found URI match %s\n", cur->name);
+                   return(xmlStrdup(cur->URL));
+               }
+               break;
+           case XML_CATA_REWRITE_URI:
+               len = xmlStrlen(cur->name);
+               if ((len > lenrewrite) &&
+                   (!xmlStrncmp(URI, cur->name, len))) {
+                   lenrewrite = len;
+                   rewrite = cur;
+               }
+               break;
+           case XML_CATA_DELEGATE_URI:
+               if (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))
+                   haveDelegate++;
+               break;
+           case XML_CATA_NEXT_CATALOG:
+               haveNext++;
+               break;
+           default:
+               break;
+       }
+       cur = cur->next;
+    }
+    if (rewrite != NULL) {
+       if (xmlDebugCatalogs)
+           xmlGenericError(xmlGenericErrorContext,
+                   "Using rewriting rule %s\n", rewrite->name);
+       ret = xmlStrdup(rewrite->URL);
+       if (ret != NULL)
+           ret = xmlStrcat(ret, &URI[lenrewrite]);
+       return(ret);
+    }
+    if (haveDelegate) {
+       const xmlChar *delegates[MAX_DELEGATE];
+       int nbList = 0, i;
+
+       /*
+        * Assume the entries have been sorted by decreasing substring
+        * matches when the list was produced.
+        */
+       cur = catal;
+       while (cur != NULL) {
+           if (((cur->type == XML_CATA_DELEGATE_SYSTEM) ||
+                (cur->type == XML_CATA_DELEGATE_URI)) &&
+               (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) {
+               for (i = 0;i < nbList;i++)
+                   if (xmlStrEqual(cur->URL, delegates[i]))
+                       break;
+               if (i < nbList) {
+                   cur = cur->next;
+                   continue;
+               }
+               if (nbList < MAX_DELEGATE)
+                   delegates[nbList++] = cur->URL;
+
+               if (cur->children == NULL) {
+                   xmlFetchXMLCatalogFile(cur);
+               }
+               if (cur->children != NULL) {
+                   if (xmlDebugCatalogs)
+                       xmlGenericError(xmlGenericErrorContext,
+                               "Trying URI delegate %s\n", cur->URL);
+                   ret = xmlCatalogListXMLResolveURI(
+                           cur->children, URI);
+                   if (ret != NULL)
+                       return(ret);
+               }
+           }
+           cur = cur->next;
+       }
+       /*
+        * Apply the cut algorithm explained in 4/
+        */
+       return(XML_CATAL_BREAK);
+    }
+    if (haveNext) {
+       cur = catal;
+       while (cur != NULL) {
+           if (cur->type == XML_CATA_NEXT_CATALOG) {
+               if (cur->children == NULL) {
+                   xmlFetchXMLCatalogFile(cur);
+               }
+               if (cur->children != NULL) {
+                   ret = xmlCatalogListXMLResolveURI(cur->children, URI);
+                   if (ret != NULL)
+                       return(ret);
+               }
+           }
+           cur = cur->next;
+       }
+    }
+
+    return(NULL);
+}
+
+/**
+ * xmlCatalogListXMLResolve:
+ * @catal:  a catalog list
+ * @pubID:  the public ID string
+ * @sysID:  the system ID string
+ *
+ * Do a complete resolution lookup of an External Identifier for a
+ * list of catalogs
+ *
+ * Implements (or tries to) 7.1. External Identifier Resolution
+ * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
+ *
+ * Returns the URI of the resource or NULL if not found
+ */
+static xmlChar *
+xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
+                     const xmlChar *sysID) {
+    xmlChar *ret = NULL;
+    xmlChar *urnID = NULL;
+    xmlChar *normid;
+    
+    if (catal == NULL)
+        return(NULL);
+    if ((pubID == NULL) && (sysID == NULL))
+       return(NULL);
+
+    normid = xmlCatalogNormalizePublic(pubID);
+    if (normid != NULL)
+        pubID = (*normid != 0 ? normid : NULL);
+    
+    if (!xmlStrncmp(pubID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
+       urnID = xmlCatalogUnWrapURN(pubID);
+       if (xmlDebugCatalogs) {
+           if (urnID == NULL)
+               xmlGenericError(xmlGenericErrorContext,
+                       "Public URN ID %s expanded to NULL\n", pubID);
+           else
+               xmlGenericError(xmlGenericErrorContext,
+                       "Public URN ID expanded to %s\n", urnID);
+       }
+       ret = xmlCatalogListXMLResolve(catal, urnID, sysID);
+       if (urnID != NULL)
+           xmlFree(urnID);
+       if (normid != NULL)
+           xmlFree(normid);
+       return(ret);
+    }
+    if (!xmlStrncmp(sysID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
+       urnID = xmlCatalogUnWrapURN(sysID);
+       if (xmlDebugCatalogs) {
+           if (urnID == NULL)
+               xmlGenericError(xmlGenericErrorContext,
+                       "System URN ID %s expanded to NULL\n", sysID);
+           else
+               xmlGenericError(xmlGenericErrorContext,
+                       "System URN ID expanded to %s\n", urnID);
+       }
+       if (pubID == NULL)
+           ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
+       else if (xmlStrEqual(pubID, urnID))
+           ret = xmlCatalogListXMLResolve(catal, pubID, NULL);
+       else {
+           ret = xmlCatalogListXMLResolve(catal, pubID, urnID);
+       }
+       if (urnID != NULL)
+           xmlFree(urnID);
+       if (normid != NULL)
+           xmlFree(normid);
+       return(ret);
+    }
+    while (catal != NULL) {
+       if (catal->type == XML_CATA_CATALOG) {
+           if (catal->children == NULL) {
+               xmlFetchXMLCatalogFile(catal);
+           }
+           if (catal->children != NULL) {
+               ret = xmlCatalogXMLResolve(catal->children, pubID, sysID);
+               if (ret != NULL) {
+                    if (normid != NULL)
+                        xmlFree(normid);
+                   return(ret);
+                }
+           }
+       }
+       catal = catal->next;
+    }
+       if (normid != NULL)
+           xmlFree(normid);
+    return(ret);
+}
+
+/**
+ * xmlCatalogListXMLResolveURI:
+ * @catal:  a catalog list
+ * @URI:  the URI
+ *
+ * Do a complete resolution lookup of an URI for a list of catalogs
+ *
+ * Implements (or tries to) 7.2. URI Resolution
+ * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
+ *
+ * Returns the URI of the resource or NULL if not found
+ */
+static xmlChar *
+xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
+    xmlChar *ret = NULL;
+    xmlChar *urnID = NULL;
+    
+    if (catal == NULL)
+        return(NULL);
+    if (URI == NULL)
+       return(NULL);
+
+    if (!xmlStrncmp(URI, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
+       urnID = xmlCatalogUnWrapURN(URI);
+       if (xmlDebugCatalogs) {
+           if (urnID == NULL)
+               xmlGenericError(xmlGenericErrorContext,
+                       "URN ID %s expanded to NULL\n", URI);
+           else
+               xmlGenericError(xmlGenericErrorContext,
+                       "URN ID expanded to %s\n", urnID);
+       }
+       ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
+       if (urnID != NULL)
+           xmlFree(urnID);
+       return(ret);
+    }
+    while (catal != NULL) {
+       if (catal->type == XML_CATA_CATALOG) {
+           if (catal->children == NULL) {
+               xmlFetchXMLCatalogFile(catal);
+           }
+           if (catal->children != NULL) {
+               ret = xmlCatalogXMLResolveURI(catal->children, URI);
+               if (ret != NULL)
+                   return(ret);
+           }
+       }
+       catal = catal->next;
+    }
+    return(ret);
+}
+
+/************************************************************************
+ *                                                                     *
+ *                     The SGML Catalog parser                         *
+ *                                                                     *
+ ************************************************************************/
+
+
+#define RAW *cur
+#define NEXT cur++;
+#define SKIP(x) cur += x;
+
+#define SKIP_BLANKS while (IS_BLANK_CH(*cur)) NEXT;
+
+/**
+ * xmlParseSGMLCatalogComment:
+ * @cur:  the current character
+ *
+ * Skip a comment in an SGML catalog
+ *
+ * Returns new current character
+ */
+static const xmlChar *
+xmlParseSGMLCatalogComment(const xmlChar *cur) {
+    if ((cur[0] != '-') || (cur[1] != '-')) 
+       return(cur);
+    SKIP(2);
+    while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-'))))
+       NEXT;
+    if (cur[0] == 0) {
+       return(NULL);
+    }
+    return(cur + 2);
+}
+
+/**
+ * xmlParseSGMLCatalogPubid:
+ * @cur:  the current character
+ * @id:  the return location
+ *
+ * Parse an SGML catalog ID
+ *
+ * Returns new current character and store the value in @id
+ */
+static const xmlChar *
+xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) {
+    xmlChar *buf = NULL, *tmp;
+    int len = 0;
+    int size = 50;
+    xmlChar stop;
+    int count = 0;
+
+    *id = NULL;
+
+    if (RAW == '"') {
+        NEXT;
+       stop = '"';
+    } else if (RAW == '\'') {
+        NEXT;
+       stop = '\'';
+    } else {
+       stop = ' ';
+    }
+    buf = (xmlChar *) xmlMallocAtomic(size * sizeof(xmlChar));
+    if (buf == NULL) {
+        xmlCatalogErrMemory("allocating public ID");
+       return(NULL);
+    }
+    while (IS_PUBIDCHAR_CH(*cur) || (*cur == '?')) {
+       if ((*cur == stop) && (stop != ' '))
+           break;
+       if ((stop == ' ') && (IS_BLANK_CH(*cur)))
+           break;
+       if (len + 1 >= size) {
+           size *= 2;
+           tmp = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
+           if (tmp == NULL) {
+               xmlCatalogErrMemory("allocating public ID");
+               xmlFree(buf);
+               return(NULL);
+           }
+           buf = tmp;
+       }
+       buf[len++] = *cur;
+       count++;
+       NEXT;
+    }
+    buf[len] = 0;
+    if (stop == ' ') {
+       if (!IS_BLANK_CH(*cur)) {
+           xmlFree(buf);
+           return(NULL);
+       }
+    } else {
+       if (*cur != stop) {
+           xmlFree(buf);
+           return(NULL);
+       }
+       NEXT;
+    }
+    *id = buf;
+    return(cur);
+}
+
+/**
+ * xmlParseSGMLCatalogName:
+ * @cur:  the current character
+ * @name:  the return location
+ *
+ * Parse an SGML catalog name
+ *
+ * Returns new current character and store the value in @name
+ */
+static const xmlChar *
+xmlParseSGMLCatalogName(const xmlChar *cur, xmlChar **name) {
+    xmlChar buf[XML_MAX_NAMELEN + 5];
+    int len = 0;
+    int c;
+
+    *name = NULL;
+
+    /*
+     * Handler for more complex cases
+     */
+    c = *cur;
+    if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) {
+       return(NULL);
+    }
+
+    while (((IS_LETTER(c)) || (IS_DIGIT(c)) ||
+            (c == '.') || (c == '-') ||
+           (c == '_') || (c == ':'))) {
+       buf[len++] = c;
+       cur++;
+       c = *cur;
+       if (len >= XML_MAX_NAMELEN)
+           return(NULL);
+    }
+    *name = xmlStrndup(buf, len);
+    return(cur);
+}
+
+/**
+ * xmlGetSGMLCatalogEntryType:
+ * @name:  the entry name
+ *
+ * Get the Catalog entry type for a given SGML Catalog name
+ *
+ * Returns Catalog entry type
+ */
+static xmlCatalogEntryType
+xmlGetSGMLCatalogEntryType(const xmlChar *name) {
+    xmlCatalogEntryType type = XML_CATA_NONE;
+    if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
+       type = SGML_CATA_SYSTEM;
+    else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
+       type = SGML_CATA_PUBLIC;
+    else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
+       type = SGML_CATA_DELEGATE;
+    else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
+       type = SGML_CATA_ENTITY;
+    else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
+       type = SGML_CATA_DOCTYPE;
+    else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
+       type = SGML_CATA_LINKTYPE;
+    else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
+       type = SGML_CATA_NOTATION;
+    else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
+       type = SGML_CATA_SGMLDECL;
+    else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
+       type = SGML_CATA_DOCUMENT;
+    else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
+       type = SGML_CATA_CATALOG;
+    else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
+       type = SGML_CATA_BASE;
+    return(type);
+}
+
+/**
+ * xmlParseSGMLCatalog:
+ * @catal:  the SGML Catalog
+ * @value:  the content of the SGML Catalog serialization
+ * @file:  the filepath for the catalog
+ * @super:  should this be handled as a Super Catalog in which case
+ *          parsing is not recursive
+ *
+ * Parse an SGML catalog content and fill up the @catal hash table with
+ * the new entries found.
+ *
+ * Returns 0 in case of success, -1 in case of error.
+ */
+static int
+xmlParseSGMLCatalog(xmlCatalogPtr catal, const xmlChar *value,
+                   const char *file, int super) {
+    const xmlChar *cur = value;
+    xmlChar *base = NULL;
+    int res;
+
+    if ((cur == NULL) || (file == NULL))
+        return(-1);
+    base = xmlStrdup((const xmlChar *) file);
+
+    while ((cur != NULL) && (cur[0] != 0)) {
+       SKIP_BLANKS;
+       if (cur[0] == 0)
+           break;
+       if ((cur[0] == '-') && (cur[1] == '-')) {
+           cur = xmlParseSGMLCatalogComment(cur);
+           if (cur == NULL) {
+               /* error */
+               break;
+           }
+       } else {
+           xmlChar *sysid = NULL;
+           xmlChar *name = NULL;
+           xmlCatalogEntryType type = XML_CATA_NONE;
+
+           cur = xmlParseSGMLCatalogName(cur, &name);
+           if (name == NULL) {
+               /* error */
+               break;
+           }
+           if (!IS_BLANK_CH(*cur)) {
+               /* error */
+               break;
+           }
+           SKIP_BLANKS;
+           if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
+                type = SGML_CATA_SYSTEM;
+           else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
+                type = SGML_CATA_PUBLIC;
+           else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
+                type = SGML_CATA_DELEGATE;
+           else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
+                type = SGML_CATA_ENTITY;
+           else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
+                type = SGML_CATA_DOCTYPE;
+           else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
+                type = SGML_CATA_LINKTYPE;
+           else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
+                type = SGML_CATA_NOTATION;
+           else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
+                type = SGML_CATA_SGMLDECL;
+           else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
+                type = SGML_CATA_DOCUMENT;
+           else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
+                type = SGML_CATA_CATALOG;
+           else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
+                type = SGML_CATA_BASE;
+           else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {
+               xmlFree(name);
+               cur = xmlParseSGMLCatalogName(cur, &name);
+               if (name == NULL) {
+                   /* error */
+                   break;
+               }
+               xmlFree(name);
+               continue;
+           }
+           xmlFree(name);
+           name = NULL;
+
+           switch(type) {
+               case SGML_CATA_ENTITY:
+                   if (*cur == '%')
+                       type = SGML_CATA_PENTITY;
+               case SGML_CATA_PENTITY:
+               case SGML_CATA_DOCTYPE:
+               case SGML_CATA_LINKTYPE:
+               case SGML_CATA_NOTATION:
+                   cur = xmlParseSGMLCatalogName(cur, &name);
+                   if (cur == NULL) {
+                       /* error */
+                       break;
+                   }
+                   if (!IS_BLANK_CH(*cur)) {
+                       /* error */
+                       break;
+                   }
+                   SKIP_BLANKS;
+                   cur = xmlParseSGMLCatalogPubid(cur, &sysid);
+                   if (cur == NULL) {
+                       /* error */
+                       break;
+                   }
+                   break;
+               case SGML_CATA_PUBLIC:
+               case SGML_CATA_SYSTEM:
+               case SGML_CATA_DELEGATE:
+                   cur = xmlParseSGMLCatalogPubid(cur, &name);
+                   if (cur == NULL) {
+                       /* error */
+                       break;
+                   }
+                   if (type != SGML_CATA_SYSTEM) {
+                       xmlChar *normid;
+
+                       normid = xmlCatalogNormalizePublic(name);
+                       if (normid != NULL) {
+                           if (name != NULL)
+                               xmlFree(name);
+                           if (*normid != 0)
+                               name = normid;
+                           else {
+                               xmlFree(normid);
+                               name = NULL;
+                           }
+                       }
+                   }
+                   if (!IS_BLANK_CH(*cur)) {
+                       /* error */
+                       break;
+                   }
+                   SKIP_BLANKS;
+                   cur = xmlParseSGMLCatalogPubid(cur, &sysid);
+                   if (cur == NULL) {
+                       /* error */
+                       break;
+                   }
+                   break;
+               case SGML_CATA_BASE:
+               case SGML_CATA_CATALOG:
+               case SGML_CATA_DOCUMENT:
+               case SGML_CATA_SGMLDECL:
+                   cur = xmlParseSGMLCatalogPubid(cur, &sysid);
+                   if (cur == NULL) {
+                       /* error */
+                       break;
+                   }
+                   break;
+               default:
+                   break;
+           }
+           if (cur == NULL) {
+               if (name != NULL)
+                   xmlFree(name);
+               if (sysid != NULL)
+                   xmlFree(sysid);
+               break;
+           } else if (type == SGML_CATA_BASE) {
+               if (base != NULL)
+                   xmlFree(base);
+               base = xmlStrdup(sysid);
+           } else if ((type == SGML_CATA_PUBLIC) ||
+                      (type == SGML_CATA_SYSTEM)) {
+               xmlChar *filename;
+
+               filename = xmlBuildURI(sysid, base);
+               if (filename != NULL) {
+                   xmlCatalogEntryPtr entry;
+
+                   entry = xmlNewCatalogEntry(type, name, filename,
+                                              NULL, XML_CATA_PREFER_NONE, NULL);
+                   res = xmlHashAddEntry(catal->sgml, name, entry);
+                   if (res < 0) {
+                       xmlFreeCatalogEntry(entry);
+                   }
+                   xmlFree(filename);
+               }
+
+           } else if (type == SGML_CATA_CATALOG) {
+               if (super) {
+                   xmlCatalogEntryPtr entry;
+
+                   entry = xmlNewCatalogEntry(type, sysid, NULL, NULL,
+                                              XML_CATA_PREFER_NONE, NULL);
+                   res = xmlHashAddEntry(catal->sgml, sysid, entry);
+                   if (res < 0) {
+                       xmlFreeCatalogEntry(entry);
+                   }
+               } else {
+                   xmlChar *filename;
+
+                   filename = xmlBuildURI(sysid, base);
+                   if (filename != NULL) {
+                       xmlExpandCatalog(catal, (const char *)filename);
+                       xmlFree(filename);
+                   }
+               }
+           }
+           /*
+            * drop anything else we won't handle it
+            */
+           if (name != NULL)
+               xmlFree(name);
+           if (sysid != NULL)
+               xmlFree(sysid);
+       }
+    }
+    if (base != NULL)
+       xmlFree(base);
+    if (cur == NULL)
+       return(-1);
+    return(0);
+}
+
+/************************************************************************
+ *                                                                     *
+ *                     SGML Catalog handling                           *
+ *                                                                     *
+ ************************************************************************/
+
+/**
+ * xmlCatalogGetSGMLPublic:
+ * @catal:  an SGML catalog hash
+ * @pubID:  the public ID string
+ *
+ * Try to lookup the catalog local reference associated to a public ID
+ *
+ * Returns the local resource if found or NULL otherwise.
+ */
+static const xmlChar *
+xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) {
+    xmlCatalogEntryPtr entry;
+    xmlChar *normid;
+
+    if (catal == NULL)
+       return(NULL);
+
+    normid = xmlCatalogNormalizePublic(pubID);
+    if (normid != NULL)
+        pubID = (*normid != 0 ? normid : NULL);
+
+    entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID);
+    if (entry == NULL) {
+       if (normid != NULL)
+           xmlFree(normid);
+       return(NULL);
+    }
+    if (entry->type == SGML_CATA_PUBLIC) {
+       if (normid != NULL)
+           xmlFree(normid);
+       return(entry->URL);
+    }
+    if (normid != NULL)
+        xmlFree(normid);
+    return(NULL);
+}
+
+/**
+ * xmlCatalogGetSGMLSystem:
+ * @catal:  an SGML catalog hash
+ * @sysID:  the system ID string
+ *
+ * Try to lookup the catalog local reference for a system ID
+ *
+ * Returns the local resource if found or NULL otherwise.
+ */
+static const xmlChar *
+xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) {
+    xmlCatalogEntryPtr entry;
+
+    if (catal == NULL)
+       return(NULL);
+
+    entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID);
+    if (entry == NULL)
+       return(NULL);
+    if (entry->type == SGML_CATA_SYSTEM)
+       return(entry->URL);
+    return(NULL);
+}
+
+/**
+ * xmlCatalogSGMLResolve:
+ * @catal:  the SGML catalog
+ * @pubID:  the public ID string
+ * @sysID:  the system ID string
+ *
+ * Do a complete resolution lookup of an External Identifier
+ *
+ * Returns the URI of the resource or NULL if not found
+ */
+static const xmlChar *
+xmlCatalogSGMLResolve(xmlCatalogPtr catal, const xmlChar *pubID,
+                     const xmlChar *sysID) {
+    const xmlChar *ret = NULL;
+
+    if (catal->sgml == NULL)
+       return(NULL);
+
+    if (pubID != NULL)
+       ret = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
+    if (ret != NULL)
+       return(ret);
+    if (sysID != NULL)
+       ret = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
+    return(NULL);
+}
+
+/************************************************************************
+ *                                                                     *
+ *                     Specific Public interfaces                      *
+ *                                                                     *
+ ************************************************************************/
+
+/**
+ * xmlLoadSGMLSuperCatalog:
+ * @filename:  a file path
+ *
+ * Load an SGML super catalog. It won't expand CATALOG or DELEGATE
+ * references. This is only needed for manipulating SGML Super Catalogs
+ * like adding and removing CATALOG or DELEGATE entries.
+ *
+ * Returns the catalog parsed or NULL in case of error
+ */
+xmlCatalogPtr
+xmlLoadSGMLSuperCatalog(const char *filename)
+{
+    xmlChar *content;
+    xmlCatalogPtr catal;
+    int ret;
+
+    content = xmlLoadFileContent(filename);
+    if (content == NULL)
+        return(NULL);
+
+    catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
+    if (catal == NULL) {
+       xmlFree(content);
+       return(NULL);
+    }
+
+    ret = xmlParseSGMLCatalog(catal, content, filename, 1);
+    xmlFree(content);
+    if (ret < 0) {
+       xmlFreeCatalog(catal);
+       return(NULL);
+    }
+    return (catal);
+}
+
+/**
+ * xmlLoadACatalog:
+ * @filename:  a file path
+ *
+ * Load the catalog and build the associated data structures.
+ * This can be either an XML Catalog or an SGML Catalog
+ * It will recurse in SGML CATALOG entries. On the other hand XML
+ * Catalogs are not handled recursively.
+ *
+ * Returns the catalog parsed or NULL in case of error
+ */
+xmlCatalogPtr
+xmlLoadACatalog(const char *filename)
+{
+    xmlChar *content;
+    xmlChar *first;
+    xmlCatalogPtr catal;
+    int ret;
+
+    content = xmlLoadFileContent(filename);
+    if (content == NULL)
+        return(NULL);
+
+
+    first = content;
+   
+    while ((*first != 0) && (*first != '-') && (*first != '<') &&
+          (!(((*first >= 'A') && (*first <= 'Z')) ||
+             ((*first >= 'a') && (*first <= 'z')))))
+       first++;
+
+    if (*first != '<') {
+       catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
+       if (catal == NULL) {
+           xmlFree(content);
+           return(NULL);
+       }
+        ret = xmlParseSGMLCatalog(catal, content, filename, 0);
+       if (ret < 0) {
+           xmlFreeCatalog(catal);
+           xmlFree(content);
+           return(NULL);
+       }
+    } else {
+       catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
+       if (catal == NULL) {
+           xmlFree(content);
+           return(NULL);
+       }
+        catal->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
+                      NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
+    }
+    xmlFree(content);
+    return (catal);
+}
+
+/**
+ * xmlExpandCatalog:
+ * @catal:  a catalog
+ * @filename:  a file path
+ *
+ * Load the catalog and expand the existing catal structure.
+ * This can be either an XML Catalog or an SGML Catalog
+ *
+ * Returns 0 in case of success, -1 in case of error
+ */
+static int
+xmlExpandCatalog(xmlCatalogPtr catal, const char *filename)
+{
+    int ret;
+
+    if ((catal == NULL) || (filename == NULL))
+       return(-1);
+
+
+    if (catal->type == XML_SGML_CATALOG_TYPE) {
+       xmlChar *content;
+
+       content = xmlLoadFileContent(filename);
+       if (content == NULL)
+           return(-1);
+
+        ret = xmlParseSGMLCatalog(catal, content, filename, 0);
+       if (ret < 0) {
+           xmlFree(content);
+           return(-1);
+       }
+       xmlFree(content);
+    } else {
+       xmlCatalogEntryPtr tmp, cur;
+       tmp = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
+                      NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
+
+       cur = catal->xml;
+       if (cur == NULL) {
+           catal->xml = tmp;
+       } else {
+           while (cur->next != NULL) cur = cur->next;
+           cur->next = tmp;
+       }
+    }
+    return (0);
+}
+
+/**
+ * xmlACatalogResolveSystem:
+ * @catal:  a Catalog
+ * @sysID:  the system ID string
+ *
+ * Try to lookup the catalog resource for a system ID
+ *
+ * Returns the resource if found or NULL otherwise, the value returned
+ *      must be freed by the caller.
+ */
+xmlChar *
+xmlACatalogResolveSystem(xmlCatalogPtr catal, const xmlChar *sysID) {
+    xmlChar *ret = NULL;
+
+    if ((sysID == NULL) || (catal == NULL))
+       return(NULL);
+    
+    if (xmlDebugCatalogs)
+       xmlGenericError(xmlGenericErrorContext,
+               "Resolve sysID %s\n", sysID);
+
+    if (catal->type == XML_XML_CATALOG_TYPE) {
+       ret = xmlCatalogListXMLResolve(catal->xml, NULL, sysID);
+       if (ret == XML_CATAL_BREAK)
+           ret = NULL;
+    } else {
+       const xmlChar *sgml;
+
+       sgml = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
+       if (sgml != NULL)
+           ret = xmlStrdup(sgml);
+    }
+    return(ret);
+}
+
+/**
+ * xmlACatalogResolvePublic:
+ * @catal:  a Catalog
+ * @pubID:  the public ID string
+ *
+ * Try to lookup the catalog local reference associated to a public ID in that catalog
+ *
+ * Returns the local resource if found or NULL otherwise, the value returned
+ *      must be freed by the caller.
+ */
+xmlChar *
+xmlACatalogResolvePublic(xmlCatalogPtr catal, const xmlChar *pubID) {
+    xmlChar *ret = NULL;
+
+    if ((pubID == NULL) || (catal == NULL))
+       return(NULL);
+    
+    if (xmlDebugCatalogs)
+       xmlGenericError(xmlGenericErrorContext,
+               "Resolve pubID %s\n", pubID);
+
+    if (catal->type == XML_XML_CATALOG_TYPE) {
+       ret = xmlCatalogListXMLResolve(catal->xml, pubID, NULL);
+       if (ret == XML_CATAL_BREAK)
+           ret = NULL;
+    } else {
+       const xmlChar *sgml;
+
+       sgml = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
+       if (sgml != NULL)
+           ret = xmlStrdup(sgml);
+    }
+    return(ret);
+}
+
+/**
+ * xmlACatalogResolve:
+ * @catal:  a Catalog
+ * @pubID:  the public ID string
+ * @sysID:  the system ID string
+ *
+ * Do a complete resolution lookup of an External Identifier
+ *
+ * Returns the URI of the resource or NULL if not found, it must be freed
+ *      by the caller.
+ */
+xmlChar *
+xmlACatalogResolve(xmlCatalogPtr catal, const xmlChar * pubID,
+                   const xmlChar * sysID)
+{
+    xmlChar *ret = NULL;
+
+    if (((pubID == NULL) && (sysID == NULL)) || (catal == NULL))
+        return (NULL);
+
+    if (xmlDebugCatalogs) {
+         if ((pubID != NULL) && (sysID != NULL)) {
+             xmlGenericError(xmlGenericErrorContext,
+                             "Resolve: pubID %s sysID %s\n", pubID, sysID);
+         } else if (pubID != NULL) {
+             xmlGenericError(xmlGenericErrorContext,
+                             "Resolve: pubID %s\n", pubID);
+         } else {
+             xmlGenericError(xmlGenericErrorContext,
+                             "Resolve: sysID %s\n", sysID);
+         }
+    }
+
+    if (catal->type == XML_XML_CATALOG_TYPE) {
+        ret = xmlCatalogListXMLResolve(catal->xml, pubID, sysID);
+       if (ret == XML_CATAL_BREAK)
+           ret = NULL;
+    } else {
+        const xmlChar *sgml;
+
+        sgml = xmlCatalogSGMLResolve(catal, pubID, sysID);
+        if (sgml != NULL)
+            ret = xmlStrdup(sgml);
+    }
+    return (ret);
+}
+
+/**
+ * xmlACatalogResolveURI:
+ * @catal:  a Catalog
+ * @URI:  the URI
+ *
+ * Do a complete resolution lookup of an URI
+ *
+ * Returns the URI of the resource or NULL if not found, it must be freed
+ *      by the caller.
+ */
+xmlChar *
+xmlACatalogResolveURI(xmlCatalogPtr catal, const xmlChar *URI) {
+    xmlChar *ret = NULL;
+
+    if ((URI == NULL) || (catal == NULL))
+       return(NULL);
+
+    if (xmlDebugCatalogs)
+       xmlGenericError(xmlGenericErrorContext,
+               "Resolve URI %s\n", URI);
+
+    if (catal->type == XML_XML_CATALOG_TYPE) {
+       ret = xmlCatalogListXMLResolveURI(catal->xml, URI);
+       if (ret == XML_CATAL_BREAK)
+           ret = NULL;
+    } else {
+       const xmlChar *sgml;
+
+       sgml = xmlCatalogSGMLResolve(catal, NULL, URI);
+       if (sgml != NULL)
+            sgml = xmlStrdup(sgml);
+    }
+    return(ret);
+}
+
+#ifdef LIBXML_OUTPUT_ENABLED
+/**
+ * xmlACatalogDump:
+ * @catal:  a Catalog
+ * @out:  the file.
+ *
+ * Dump the given catalog to the given file.
+ */
+void
+xmlACatalogDump(xmlCatalogPtr catal, FILE *out) {
+    if ((out == NULL) || (catal == NULL))
+       return;
+
+    if (catal->type == XML_XML_CATALOG_TYPE) {
+       xmlDumpXMLCatalog(out, catal->xml);
+    } else {
+       xmlHashScan(catal->sgml,
+                   (xmlHashScanner) xmlCatalogDumpEntry, out);
+    } 
+}
+#endif /* LIBXML_OUTPUT_ENABLED */
+
+/**
+ * xmlACatalogAdd:
+ * @catal:  a Catalog
+ * @type:  the type of record to add to the catalog
+ * @orig:  the system, public or prefix to match 
+ * @replace:  the replacement value for the match
+ *
+ * Add an entry in the catalog, it may overwrite existing but
+ * different entries.
+ *
+ * Returns 0 if successful, -1 otherwise
+ */
+int
+xmlACatalogAdd(xmlCatalogPtr catal, const xmlChar * type,
+              const xmlChar * orig, const xmlChar * replace)
+{
+    int res = -1;
+
+    if (catal == NULL)
+       return(-1);
+
+    if (catal->type == XML_XML_CATALOG_TYPE) {
+        res = xmlAddXMLCatalog(catal->xml, type, orig, replace);
+    } else {
+        xmlCatalogEntryType cattype;
+
+        cattype = xmlGetSGMLCatalogEntryType(type);
+        if (cattype != XML_CATA_NONE) {
+            xmlCatalogEntryPtr entry;
+
+            entry = xmlNewCatalogEntry(cattype, orig, replace, NULL,
+                                       XML_CATA_PREFER_NONE, NULL);
+           if (catal->sgml == NULL)
+               catal->sgml = xmlHashCreate(10);
+            res = xmlHashAddEntry(catal->sgml, orig, entry);
+        }
+    }
+    return (res);
+}
+
+/**
+ * xmlACatalogRemove:
+ * @catal:  a Catalog
+ * @value:  the value to remove
+ *
+ * Remove an entry from the catalog
+ *
+ * Returns the number of entries removed if successful, -1 otherwise
+ */
+int
+xmlACatalogRemove(xmlCatalogPtr catal, const xmlChar *value) {
+    int res = -1;
+
+    if ((catal == NULL) || (value == NULL))
+       return(-1);
+
+    if (catal->type == XML_XML_CATALOG_TYPE) {
+       res = xmlDelXMLCatalog(catal->xml, value);
+    } else {
+       res = xmlHashRemoveEntry(catal->sgml, value,
+               (xmlHashDeallocator) xmlFreeCatalogEntry);
+       if (res == 0)
+           res = 1;
+    } 
+    return(res);
+}
+
+/**
+ * xmlNewCatalog:
+ * @sgml:  should this create an SGML catalog
+ *
+ * create a new Catalog.
+ *
+ * Returns the xmlCatalogPtr or NULL in case of error
+ */
+xmlCatalogPtr
+xmlNewCatalog(int sgml) {
+    xmlCatalogPtr catal = NULL;
+
+    if (sgml) {
+       catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE,
+                                   xmlCatalogDefaultPrefer);
+        if ((catal != NULL) && (catal->sgml == NULL))
+           catal->sgml = xmlHashCreate(10);
+    } else
+       catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
+                                   xmlCatalogDefaultPrefer);
+    return(catal);
+}
+
+/**
+ * xmlCatalogIsEmpty:
+ * @catal:  should this create an SGML catalog
+ *
+ * Check is a catalog is empty
+ *
+ * Returns 1 if the catalog is empty, 0 if not, amd -1 in case of error.
+ */
+int
+xmlCatalogIsEmpty(xmlCatalogPtr catal) {
+    if (catal == NULL)
+       return(-1);
+
+    if (catal->type == XML_XML_CATALOG_TYPE) {
+       if (catal->xml == NULL)
+           return(1);
+       if ((catal->xml->type != XML_CATA_CATALOG) &&
+           (catal->xml->type != XML_CATA_BROKEN_CATALOG))
+           return(-1);
+       if (catal->xml->children == NULL)
+           return(1);
+        return(0);
+    } else {
+       int res;
+
+       if (catal->sgml == NULL)
+           return(1);
+       res = xmlHashSize(catal->sgml);
+       if (res == 0)
+           return(1);
+       if (res < 0)
+           return(-1);
+    } 
+    return(0);
+}
+
+/************************************************************************
+ *                                                                     *
+ *   Public interfaces manipulating the global shared default catalog  *
+ *                                                                     *
+ ************************************************************************/
+
+/**
+ * xmlInitializeCatalogData:
+ *
+ * Do the catalog initialization only of global data, doesn't try to load
+ * any catalog actually.
+ * this function is not thread safe, catalog initialization should
+ * preferably be done once at startup
+ */
+static void
+xmlInitializeCatalogData(void) {
+    if (xmlCatalogInitialized != 0)
+       return;
+
+    if (getenv("XML_DEBUG_CATALOG")) 
+       xmlDebugCatalogs = 1;
+    xmlCatalogMutex = xmlNewRMutex();
+
+    xmlCatalogInitialized = 1;
+}
+/**
+ * xmlInitializeCatalog:
+ *
+ * Do the catalog initialization.
+ * this function is not thread safe, catalog initialization should
+ * preferably be done once at startup
+ */
+void
+xmlInitializeCatalog(void) {
+    if (xmlCatalogInitialized != 0)
+       return;
+
+    xmlInitializeCatalogData();
+    xmlRMutexLock(xmlCatalogMutex);
+
+    if (getenv("XML_DEBUG_CATALOG")) 
+       xmlDebugCatalogs = 1;
+
+    if (xmlDefaultCatalog == NULL) {
+       const char *catalogs;
+       char *path;
+       const char *cur, *paths;
+       xmlCatalogPtr catal;
+       xmlCatalogEntryPtr *nextent;
+
+       catalogs = (const char *) getenv("XML_CATALOG_FILES");
+       if (catalogs == NULL)
+#if defined(_WIN32) && defined(_MSC_VER)
+    {
+               void* hmodule;
+               hmodule = GetModuleHandleA("libxml2.dll");
+               if (hmodule == NULL)
+                       hmodule = GetModuleHandleA(NULL);
+               if (hmodule != NULL) {
+                       char buf[256];
+                       unsigned long len = GetModuleFileNameA(hmodule, buf, 255);
+                       if (len != 0) {
+                               char* p = &(buf[len]);
+                               while (*p != '\\' && p > buf) 
+                                       p--;
+                               if (p != buf) {
+                                       xmlChar* uri;
+                                       strncpy(p, "\\..\\etc\\catalog", 255 - (p - buf));
+                                       uri = xmlCanonicPath(buf);
+                                       if (uri != NULL) {
+                                               strncpy(XML_XML_DEFAULT_CATALOG, uri, 255);
+                                               xmlFree(uri);
+                                       }
+                               }
+                       }
+               }
+               catalogs = XML_XML_DEFAULT_CATALOG;
+    }
+#else
+           catalogs = XML_XML_DEFAULT_CATALOG;
+#endif
+
+       catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, 
+               xmlCatalogDefaultPrefer);
+       if (catal != NULL) {
+           /* the XML_CATALOG_FILES envvar is allowed to contain a 
+              space-separated list of entries. */
+           cur = catalogs;
+           nextent = &catal->xml;
+           while (*cur != '\0') {
+               while (xmlIsBlank_ch(*cur)) 
+                   cur++;
+               if (*cur != 0) {
+                   paths = cur;
+                   while ((*cur != 0) && (!xmlIsBlank_ch(*cur)))
+                       cur++;
+                   path = (char *) xmlStrndup((const xmlChar *)paths, cur - paths);
+                   if (path != NULL) {
+                       *nextent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
+                               NULL, BAD_CAST path, xmlCatalogDefaultPrefer, NULL);
+                       if (*nextent != NULL)
+                           nextent = &((*nextent)->next);
+                       xmlFree(path);
+                   }
+               }
+           }
+           xmlDefaultCatalog = catal;
+       }
+    }
+
+    xmlRMutexUnlock(xmlCatalogMutex);
+}
+
+
+/**
+ * xmlLoadCatalog:
+ * @filename:  a file path
+ *
+ * Load the catalog and makes its definitions effective for the default
+ * external entity loader. It will recurse in SGML CATALOG entries.
+ * this function is not thread safe, catalog initialization should
+ * preferably be done once at startup
+ *
+ * Returns 0 in case of success -1 in case of error
+ */
+int
+xmlLoadCatalog(const char *filename)
+{
+    int ret;
+    xmlCatalogPtr catal;
+
+    if (!xmlCatalogInitialized)
+       xmlInitializeCatalogData();
+
+    xmlRMutexLock(xmlCatalogMutex);
+
+    if (xmlDefaultCatalog == NULL) {
+       catal = xmlLoadACatalog(filename);
+       if (catal == NULL) {
+           xmlRMutexUnlock(xmlCatalogMutex);
+           return(-1);
+       }
+
+       xmlDefaultCatalog = catal;
+       xmlRMutexUnlock(xmlCatalogMutex);
+       return(0);
+    }
+
+    ret = xmlExpandCatalog(xmlDefaultCatalog, filename);
+    xmlRMutexUnlock(xmlCatalogMutex);
+    return(ret);
+}
+
+/**
+ * xmlLoadCatalogs:
+ * @pathss:  a list of directories separated by a colon or a space.
+ *
+ * Load the catalogs and makes their definitions effective for the default
+ * external entity loader.
+ * this function is not thread safe, catalog initialization should
+ * preferably be done once at startup
+ */
+void
+xmlLoadCatalogs(const char *pathss) {
+    const char *cur;
+    const char *paths;
+    xmlChar *path;
+
+    if (pathss == NULL)
+       return;
+
+    cur = pathss;
+    while ((cur != NULL) && (*cur != 0)) {
+       while (xmlIsBlank_ch(*cur)) cur++;
+       if (*cur != 0) {
+           paths = cur;
+           while ((*cur != 0) && (*cur != ':') && (!xmlIsBlank_ch(*cur)))
+               cur++;
+           path = xmlStrndup((const xmlChar *)paths, cur - paths);
+           if (path != NULL) {
+               xmlLoadCatalog((const char *) path);
+               xmlFree(path);
+           }
+       }
+       while (*cur == ':')
+           cur++;
+    }
+}
+
+/**
+ * xmlCatalogCleanup:
+ *
+ * Free up all the memory associated with catalogs
+ */
+void
+xmlCatalogCleanup(void) {
+    if (xmlCatalogInitialized == 0)
+        return;
+
+    xmlRMutexLock(xmlCatalogMutex);
+    if (xmlDebugCatalogs)
+       xmlGenericError(xmlGenericErrorContext,
+               "Catalogs cleanup\n");
+    if (xmlCatalogXMLFiles != NULL)
+       xmlHashFree(xmlCatalogXMLFiles, 
+                   (xmlHashDeallocator)xmlFreeCatalogHashEntryList);
+    xmlCatalogXMLFiles = NULL;
+    if (xmlDefaultCatalog != NULL)
+       xmlFreeCatalog(xmlDefaultCatalog);
+    xmlDefaultCatalog = NULL;
+    xmlDebugCatalogs = 0;
+    xmlCatalogInitialized = 0;
+    xmlRMutexUnlock(xmlCatalogMutex);
+    xmlFreeRMutex(xmlCatalogMutex);
+}
+
+/**
+ * xmlCatalogResolveSystem:
+ * @sysID:  the system ID string
+ *
+ * Try to lookup the catalog resource for a system ID
+ *
+ * Returns the resource if found or NULL otherwise, the value returned
+ *      must be freed by the caller.
+ */
+xmlChar *
+xmlCatalogResolveSystem(const xmlChar *sysID) {
+    xmlChar *ret;
+
+    if (!xmlCatalogInitialized)
+       xmlInitializeCatalog();
+
+    ret = xmlACatalogResolveSystem(xmlDefaultCatalog, sysID);
+    return(ret);
+}
+
+/**
+ * xmlCatalogResolvePublic:
+ * @pubID:  the public ID string
+ *
+ * Try to lookup the catalog reference associated to a public ID
+ *
+ * Returns the resource if found or NULL otherwise, the value returned
+ *      must be freed by the caller.
+ */
+xmlChar *
+xmlCatalogResolvePublic(const xmlChar *pubID) {
+    xmlChar *ret;
+
+    if (!xmlCatalogInitialized)
+       xmlInitializeCatalog();
+
+    ret = xmlACatalogResolvePublic(xmlDefaultCatalog, pubID);
+    return(ret);
+}
+
+/**
+ * xmlCatalogResolve:
+ * @pubID:  the public ID string
+ * @sysID:  the system ID string
+ *
+ * Do a complete resolution lookup of an External Identifier
+ *
+ * Returns the URI of the resource or NULL if not found, it must be freed
+ *      by the caller.
+ */
+xmlChar *
+xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) {
+    xmlChar *ret;
+
+    if (!xmlCatalogInitialized)
+       xmlInitializeCatalog();
+
+    ret = xmlACatalogResolve(xmlDefaultCatalog, pubID, sysID);
+    return(ret);
+}
+
+/**
+ * xmlCatalogResolveURI:
+ * @URI:  the URI
+ *
+ * Do a complete resolution lookup of an URI
+ *
+ * Returns the URI of the resource or NULL if not found, it must be freed
+ *      by the caller.
+ */
+xmlChar *
+xmlCatalogResolveURI(const xmlChar *URI) {
+    xmlChar *ret;
+
+    if (!xmlCatalogInitialized)
+       xmlInitializeCatalog();
+
+    ret = xmlACatalogResolveURI(xmlDefaultCatalog, URI);
+    return(ret);
+}
+
+#ifdef LIBXML_OUTPUT_ENABLED
+/**
+ * xmlCatalogDump:
+ * @out:  the file.
+ *
+ * Dump all the global catalog content to the given file.
+ */
+void
+xmlCatalogDump(FILE *out) {
+    if (out == NULL)
+       return;
+
+    if (!xmlCatalogInitialized)
+       xmlInitializeCatalog();
+
+    xmlACatalogDump(xmlDefaultCatalog, out);
+}
+#endif /* LIBXML_OUTPUT_ENABLED */
+
+/**
+ * xmlCatalogAdd:
+ * @type:  the type of record to add to the catalog
+ * @orig:  the system, public or prefix to match 
+ * @replace:  the replacement value for the match
+ *
+ * Add an entry in the catalog, it may overwrite existing but
+ * different entries.
+ * If called before any other catalog routine, allows to override the
+ * default shared catalog put in place by xmlInitializeCatalog();
+ *
+ * Returns 0 if successful, -1 otherwise
+ */
+int
+xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) {
+    int res = -1;
+
+    if (!xmlCatalogInitialized)
+       xmlInitializeCatalogData();
+
+    xmlRMutexLock(xmlCatalogMutex);
+    /*
+     * Specific case where one want to override the default catalog
+     * put in place by xmlInitializeCatalog();
+     */
+    if ((xmlDefaultCatalog == NULL) &&
+       (xmlStrEqual(type, BAD_CAST "catalog"))) {
+       xmlDefaultCatalog = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
+                                         xmlCatalogDefaultPrefer);
+       xmlDefaultCatalog->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
+                                   orig, NULL,  xmlCatalogDefaultPrefer, NULL);
+
+       xmlRMutexUnlock(xmlCatalogMutex);
+       return(0);
+    } 
+
+    res = xmlACatalogAdd(xmlDefaultCatalog, type, orig, replace);
+    xmlRMutexUnlock(xmlCatalogMutex);
+    return(res);
+}
+
+/**
+ * xmlCatalogRemove:
+ * @value:  the value to remove
+ *
+ * Remove an entry from the catalog
+ *
+ * Returns the number of entries removed if successful, -1 otherwise
+ */
+int
+xmlCatalogRemove(const xmlChar *value) {
+    int res;
+
+    if (!xmlCatalogInitialized)
+       xmlInitializeCatalog();
+
+    xmlRMutexLock(xmlCatalogMutex);
+    res = xmlACatalogRemove(xmlDefaultCatalog, value);
+    xmlRMutexUnlock(xmlCatalogMutex);
+    return(res);
+}
+
+/**
+ * xmlCatalogConvert:
+ *
+ * Convert all the SGML catalog entries as XML ones
+ *
+ * Returns the number of entries converted if successful, -1 otherwise
+ */
+int
+xmlCatalogConvert(void) {
+    int res = -1;
+
+    if (!xmlCatalogInitialized)
+       xmlInitializeCatalog();
+
+    xmlRMutexLock(xmlCatalogMutex);
+    res = xmlConvertSGMLCatalog(xmlDefaultCatalog);
+    xmlRMutexUnlock(xmlCatalogMutex);
+    return(res);
+}
+
+/************************************************************************
+ *                                                                     *
+ *     Public interface manipulating the common preferences            *
+ *                                                                     *
+ ************************************************************************/
+
+/**
+ * xmlCatalogGetDefaults:
+ *
+ * Used to get the user preference w.r.t. to what catalogs should
+ * be accepted
+ *
+ * Returns the current xmlCatalogAllow value
+ */
+xmlCatalogAllow
+xmlCatalogGetDefaults(void) {
+    return(xmlCatalogDefaultAllow);
+}
+
+/**
+ * xmlCatalogSetDefaults:
+ * @allow:  what catalogs should be accepted
+ *
+ * Used to set the user preference w.r.t. to what catalogs should
+ * be accepted
+ */
+void
+xmlCatalogSetDefaults(xmlCatalogAllow allow) {
+    if (xmlDebugCatalogs) {
+       switch (allow) {
+           case XML_CATA_ALLOW_NONE:
+               xmlGenericError(xmlGenericErrorContext,
+                       "Disabling catalog usage\n");
+               break;
+           case XML_CATA_ALLOW_GLOBAL:
+               xmlGenericError(xmlGenericErrorContext,
+                       "Allowing only global catalogs\n");
+               break;
+           case XML_CATA_ALLOW_DOCUMENT:
+               xmlGenericError(xmlGenericErrorContext,
+                       "Allowing only catalogs from the document\n");
+               break;
+           case XML_CATA_ALLOW_ALL:
+               xmlGenericError(xmlGenericErrorContext,
+                       "Allowing all catalogs\n");
+               break;
+       }
+    }
+    xmlCatalogDefaultAllow = allow;
+}
+
+/**
+ * xmlCatalogSetDefaultPrefer:
+ * @prefer:  the default preference for delegation
+ *
+ * Allows to set the preference between public and system for deletion
+ * in XML Catalog resolution. C.f. section 4.1.1 of the spec
+ * Values accepted are XML_CATA_PREFER_PUBLIC or XML_CATA_PREFER_SYSTEM
+ *
+ * Returns the previous value of the default preference for delegation
+ */
+xmlCatalogPrefer
+xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer) {
+    xmlCatalogPrefer ret = xmlCatalogDefaultPrefer;
+
+    if (prefer == XML_CATA_PREFER_NONE)
+       return(ret);
+
+    if (xmlDebugCatalogs) {
+       switch (prefer) {
+           case XML_CATA_PREFER_PUBLIC:
+               xmlGenericError(xmlGenericErrorContext,
+                       "Setting catalog preference to PUBLIC\n");
+               break;
+           case XML_CATA_PREFER_SYSTEM:
+               xmlGenericError(xmlGenericErrorContext,
+                       "Setting catalog preference to SYSTEM\n");
+               break;
+           case XML_CATA_PREFER_NONE:
+               break;
+       }
+    }
+    xmlCatalogDefaultPrefer = prefer;
+    return(ret);
+}
+
+/**
+ * xmlCatalogSetDebug:
+ * @level:  the debug level of catalogs required
+ *
+ * Used to set the debug level for catalog operation, 0 disable
+ * debugging, 1 enable it
+ *
+ * Returns the previous value of the catalog debugging level
+ */
+int
+xmlCatalogSetDebug(int level) {
+    int ret = xmlDebugCatalogs;
+
+    if (level <= 0)
+        xmlDebugCatalogs = 0;
+    else
+       xmlDebugCatalogs = level;
+    return(ret);
+}
+
+/************************************************************************
+ *                                                                     *
+ *   Minimal interfaces used for per-document catalogs by the parser   *
+ *                                                                     *
+ ************************************************************************/
+
+/**
+ * xmlCatalogFreeLocal:
+ * @catalogs:  a document's list of catalogs
+ *
+ * Free up the memory associated to the catalog list
+ */
+void
+xmlCatalogFreeLocal(void *catalogs) {
+    xmlCatalogEntryPtr catal;
+
+    if (!xmlCatalogInitialized)
+       xmlInitializeCatalog();
+
+    catal = (xmlCatalogEntryPtr) catalogs;
+    if (catal != NULL)
+       xmlFreeCatalogEntryList(catal);
+}
+
+
+/**
+ * xmlCatalogAddLocal:
+ * @catalogs:  a document's list of catalogs
+ * @URL:  the URL to a new local catalog
+ *
+ * Add the new entry to the catalog list
+ *
+ * Returns the updated list
+ */
+void * 
+xmlCatalogAddLocal(void *catalogs, const xmlChar *URL) {
+    xmlCatalogEntryPtr catal, add;
+
+    if (!xmlCatalogInitialized)
+       xmlInitializeCatalog();
+
+    if (URL == NULL)
+       return(catalogs);
+
+    if (xmlDebugCatalogs)
+       xmlGenericError(xmlGenericErrorContext,
+               "Adding document catalog %s\n", URL);
+
+    add = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, URL, NULL,
+                            xmlCatalogDefaultPrefer, NULL);
+    if (add == NULL)
+       return(catalogs);
+
+    catal = (xmlCatalogEntryPtr) catalogs;
+    if (catal == NULL) 
+       return((void *) add);
+
+    while (catal->next != NULL)
+       catal = catal->next;
+    catal->next = add;
+    return(catalogs);
+}
+
+/**
+ * xmlCatalogLocalResolve:
+ * @catalogs:  a document's list of catalogs
+ * @pubID:  the public ID string
+ * @sysID:  the system ID string
+ *
+ * Do a complete resolution lookup of an External Identifier using a 
+ * document's private catalog list
+ *
+ * Returns the URI of the resource or NULL if not found, it must be freed
+ *      by the caller.
+ */
+xmlChar *
+xmlCatalogLocalResolve(void *catalogs, const xmlChar *pubID,
+                      const xmlChar *sysID) {
+    xmlCatalogEntryPtr catal;
+    xmlChar *ret;
+
+    if (!xmlCatalogInitialized)
+       xmlInitializeCatalog();
+
+    if ((pubID == NULL) && (sysID == NULL))
+       return(NULL);
+
+    if (xmlDebugCatalogs) {
+        if ((pubID != NULL) && (sysID != NULL)) {
+            xmlGenericError(xmlGenericErrorContext,
+                            "Local Resolve: pubID %s sysID %s\n", pubID, sysID);
+        } else if (pubID != NULL) {
+            xmlGenericError(xmlGenericErrorContext,
+                            "Local Resolve: pubID %s\n", pubID);
+        } else {
+            xmlGenericError(xmlGenericErrorContext,
+                            "Local Resolve: sysID %s\n", sysID);
+        }
+    }
+
+    catal = (xmlCatalogEntryPtr) catalogs;
+    if (catal == NULL)
+       return(NULL);
+    ret = xmlCatalogListXMLResolve(catal, pubID, sysID);
+    if ((ret != NULL) && (ret != XML_CATAL_BREAK))
+       return(ret);
+    return(NULL);
+}
+
+/**
+ * xmlCatalogLocalResolveURI:
+ * @catalogs:  a document's list of catalogs
+ * @URI:  the URI
+ *
+ * Do a complete resolution lookup of an URI using a 
+ * document's private catalog list
+ *
+ * Returns the URI of the resource or NULL if not found, it must be freed
+ *      by the caller.
+ */
+xmlChar *
+xmlCatalogLocalResolveURI(void *catalogs, const xmlChar *URI) {
+    xmlCatalogEntryPtr catal;
+    xmlChar *ret;
+
+    if (!xmlCatalogInitialized)
+       xmlInitializeCatalog();
+
+    if (URI == NULL)
+       return(NULL);
+
+    if (xmlDebugCatalogs)
+       xmlGenericError(xmlGenericErrorContext,
+               "Resolve URI %s\n", URI);
+
+    catal = (xmlCatalogEntryPtr) catalogs;
+    if (catal == NULL)
+       return(NULL);
+    ret = xmlCatalogListXMLResolveURI(catal, URI);
+    if ((ret != NULL) && (ret != XML_CATAL_BREAK))
+       return(ret);
+    return(NULL);
+}
+
+/************************************************************************
+ *                                                                     *
+ *                     Deprecated interfaces                           *
+ *                                                                     *
+ ************************************************************************/
+/**
+ * xmlCatalogGetSystem:
+ * @sysID:  the system ID string
+ *
+ * Try to lookup the catalog reference associated to a system ID
+ * DEPRECATED, use xmlCatalogResolveSystem()
+ *
+ * Returns the resource if found or NULL otherwise.
+ */
+const xmlChar *
+xmlCatalogGetSystem(const xmlChar *sysID) {
+    xmlChar *ret;
+    static xmlChar result[1000];
+    static int msg = 0;
+
+    if (!xmlCatalogInitialized)
+       xmlInitializeCatalog();
+
+    if (msg == 0) {
+       xmlGenericError(xmlGenericErrorContext,
+               "Use of deprecated xmlCatalogGetSystem() call\n");
+       msg++;
+    }
+
+    if (sysID == NULL)
+       return(NULL);
+    
+    /*
+     * Check first the XML catalogs
+     */
+    if (xmlDefaultCatalog != NULL) {
+       ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, NULL, sysID);
+       if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
+           snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
+           result[sizeof(result) - 1] = 0;
+           return(result);
+       }
+    }
+
+    if (xmlDefaultCatalog != NULL)
+       return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog->sgml, sysID));
+    return(NULL);
+}
+
+/**
+ * xmlCatalogGetPublic:
+ * @pubID:  the public ID string
+ *
+ * Try to lookup the catalog reference associated to a public ID
+ * DEPRECATED, use xmlCatalogResolvePublic()
+ *
+ * Returns the resource if found or NULL otherwise.
+ */
+const xmlChar *
+xmlCatalogGetPublic(const xmlChar *pubID) {
+    xmlChar *ret;
+    static xmlChar result[1000];
+    static int msg = 0;
+
+    if (!xmlCatalogInitialized)
+       xmlInitializeCatalog();
+
+    if (msg == 0) {
+       xmlGenericError(xmlGenericErrorContext,
+               "Use of deprecated xmlCatalogGetPublic() call\n");
+       msg++;
+    }
+
+    if (pubID == NULL)
+       return(NULL);
+    
+    /*
+     * Check first the XML catalogs
+     */
+    if (xmlDefaultCatalog != NULL) {
+       ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, pubID, NULL);
+       if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
+           snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
+           result[sizeof(result) - 1] = 0;
+           return(result);
+       }
+    }
+
+    if (xmlDefaultCatalog != NULL)
+       return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog->sgml, pubID));
+    return(NULL);
+}
+
+#define bottom_catalog
+#include "elfgcchack.h"
+#endif /* LIBXML_CATALOG_ENABLED */