Skip to content
Snippets Groups Projects
Select Git revision
  • branch/OpenVMS
  • branch/default default protected
  • wild/1a5dd97bc5c0cdedc5a1e02ebc0ea5dbad28a72c
  • wild/ef383bb403b22bb062eca037fe9eb40395e5239d
  • wild/1b27403b1257ec30ce192e8d0af922c1f428c286
  • wild/37aeffd2f03981fb3090960ba53bb5f5c962970d
  • wild/2e949a79e46e9e813e8970a3ed05ded647a5c08e
  • wild/3fe73b8a3bb36ac30a3c4eb3d3d69d6ae453614c
  • wild/9aeb5555cd4f046940492b4572fbc39864a84ae2
  • wild/897288304f9a2037a9463f6001c4688afe711d08
  • wild/2b12dd122d8382282e90998ebadddcf09e847eaf
  • wild/3888945e4fe6438fa8127b7eaf1b10dd9499b15d
  • wild/53e7fce21ef05bf6fa534f067b88e139782e3d5e
  • wild/6096c67ce603c7404e05c9c91b3114f9c0781521
  • wild/69007011f857740b53ce4072c9b8ec202f6e83f3
  • wild/79ef52d5327d1637ddba0472ff28e137245e5c5f
  • wild/ff7aa1400d0d1c4cfc70f3384b2b9dba73668d14
  • wild/8e133c9799f0891d00226eb2f94cd7867d9baf67
  • wild/51e439c2a307243d9801ed7c639396a36ddef007
  • wild/5214eca35b040c5c2b3b3b25277226635e5b1837
  • 2.11.0
21 results

catalog.c

Blame
  • catalog.c 96.67 KiB
    /**
     * 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
    #include <stdlib.h>
    #include <string.h>
    #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
    #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>
    
    #include "buf.h"
    
    #define MAX_DELEGATE	50
    #define MAX_CATAL_DEPTH	50
    
    #ifdef _WIN32
    # define PATH_SEPARATOR ';'
    #else
    # define PATH_SEPARATOR ':'
    #endif
    
    /**
     * TODO:
     *
     * macro to flag unimplemented blocks
     * XML_CATALOG_PREFER user env to select between system/public preferred
     * 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://" SYSCONFDIR "/xml/catalog"
    #endif
    #ifndef XML_SGML_DEFAULT_CATALOG
    #define XML_SGML_DEFAULT_CATALOG "file://" SYSCONFDIR "/sgml/catalog"
    #endif
    
    #if defined(_WIN32) && defined(_MSC_VER)
    #undef XML_XML_DEFAULT_CATALOG
    static char XML_XML_DEFAULT_CATALOG[256] = "file://" SYSCONFDIR "/xml/catalog";
    #if !defined(_WINDOWS_)
    void* __stdcall GetModuleHandleA(const char*);
    unsigned long __stdcall GetModuleFileNameA(void*, char*, unsigned long);
    #endif
    #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 information
     *
     * 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 information
     *
     * Handle a catalog error
     */
    static void LIBXML_ATTR_FORMAT(4,0)
    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:
     * @payload:  a Catalog entry
     *
     * Free the memory allocated to a Catalog entry
     */
    static void
    xmlFreeCatalogEntry(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
        xmlCatalogEntryPtr ret = (xmlCatalogEntryPtr) payload;
        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, NULL);
    	ret = next;
        }
    }
    
    /**
     * xmlFreeCatalogHashEntryList:
     * @payload:  a Catalog entry list
     *
     * Free the memory allocated to list of Catalog entries from the
     * catalog file hash.
     */
    static void
    xmlFreeCatalogHashEntryList(void *payload,
                                const xmlChar *name ATTRIBUTE_UNUSED) {
        xmlCatalogEntryPtr catal = (xmlCatalogEntryPtr) payload;
        xmlCatalogEntryPtr children, next;
    
        if (catal == NULL)
    	return;
    
        children = catal->children;
        while (children != NULL) {
    	next = children->next;
    	children->dealloc = 0;
    	children->children = NULL;
    	xmlFreeCatalogEntry(children, NULL);
    	children = next;
        }
        catal->dealloc = 0;
        xmlFreeCatalogEntry(catal, NULL);
    }
    
    /**
     * 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, 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(void *payload, void *data,
                        const xmlChar *name ATTRIBUTE_UNUSED) {
        xmlCatalogEntryPtr entry = (xmlCatalogEntryPtr) payload;
        FILE *out = (FILE *) data;
        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(void *payload, void *data,
                           const xmlChar *name ATTRIBUTE_UNUSED) {
        xmlCatalogEntryPtr entry = (xmlCatalogEntryPtr) payload;
        xmlCatalogPtr catal = (xmlCatalogPtr) data;
        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, 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, 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) {
            xmlCatalogErrMemory("allocating parser context");
    	return(NULL);
        }
    
        buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE);
        if (buf == NULL) {
    	xmlFreeParserCtxt(ctxt);
    	return(NULL);
        }
    
        inputStream = xmlNewInputStream(ctxt);
        if (inputStream == NULL) {
    	xmlFreeParserInputBuffer(buf);
    	xmlFreeParserCtxt(ctxt);
    	return(NULL);
        }
    
        inputStream->filename = (char *) xmlCanonicPath((const xmlChar *)filename);
        inputStream->buf = buf;
        xmlBufResetInput(buf->buffer, inputStream);
    
        inputPush(ctxt, inputStream);
        if (ctxt->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 = (xmlChar*)xmlMallocAtomic(size + 10);
        if (content == NULL) {
            xmlCatalogErrMemory("allocating catalog data");
    #ifdef HAVE_STAT
    	close(fd);
    #else
    	fclose(fd);
    #endif
            return (NULL);
        }
    #ifdef HAVE_STAT
        len = read(fd, content, size);
        close(fd);
    #else
        len = fread(content, 1, size, fd);
        fclose(fd);
    #endif
        if (len < 0) {
            xmlFree(content);
            return (NULL);
        }
        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 *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);
    }
    
    /**
     * 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);
    
        /*
         * 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) {
            catal->type = XML_CATA_CATALOG;
    	cur = (xmlCatalogEntryPtr)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, using %s\n",
    				            cur->name, cur->URL);
    			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);
    		    } else if (catal->depth > MAX_CATAL_DEPTH) {
    		        return(NULL);
    		    }
    		}
    	    }
    	    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);
    
        if (catal->depth > MAX_CATAL_DEPTH) {
    	xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
    		      "Detected recursion in catalog %s\n",
    		      catal->name, NULL, 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) {
    		    break;
                    } else if (catal->children->depth > MAX_CATAL_DEPTH) {
    	            ret = NULL;
    		    break;
    	        }
    	    }
    	}
    	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 (cur == NULL || name == NULL) {
    		/* error */
    		break;
    	    }
    	    if (!IS_BLANK_CH(*cur)) {
    		/* error */
    		xmlFree(name);
    		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;
                        /* Falls through. */
    		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, NULL);
    		    }
    		    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, NULL);
    		    }
    		} 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);
        if (ret != NULL)
    	return(ret);
        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)
                ret = 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, 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, 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((const xmlChar*)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;
    #ifdef _WIN32
        int i, iLen;
    #endif
    
        if (pathss == NULL)
    	return;
    
        cur = pathss;
        while (*cur != 0) {
    	while (xmlIsBlank_ch(*cur)) cur++;
    	if (*cur != 0) {
    	    paths = cur;
    	    while ((*cur != 0) && (*cur != PATH_SEPARATOR) && (!xmlIsBlank_ch(*cur)))
    		cur++;
    	    path = xmlStrndup((const xmlChar *)paths, cur - paths);
    	    if (path != NULL) {
    #ifdef _WIN32
            iLen = strlen((const char*)path);
            for(i = 0; i < iLen; i++) {
                if(path[i] == '\\') {
                    path[i] = '/';
                }
            }
    #endif
    		xmlLoadCatalog((const char *) path);
    		xmlFree(path);
    	    }
    	}
    	while (*cur == PATH_SEPARATOR)
    	    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, 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);
    	if (xmlDefaultCatalog != NULL) {
    	   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;
    	    default:
    		return(ret);
    	}
        }
        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);
    }
    
    #endif /* LIBXML_CATALOG_ENABLED */