Logo Search packages:      
Sourcecode: chromium-browser version File versions  Download package

catalog.c

/**
 * catalog.c: set of generic Catalog related routines 
 *
 * Reference:  SGML Open Technical Resolution TR9401:1997.
 *             http://www.jclark.com/sp/catalog.htm
 *
 *             XML Catalogs Working Draft 06 August 2001
 *             http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
 *
 * See Copyright for the status of this software.
 *
 * Daniel.Veillard@imag.fr
 */

#define IN_LIBXML
#include "libxml.h"

#ifdef LIBXML_CATALOG_ENABLED
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <string.h>
#include <libxml/xmlmemory.h>
#include <libxml/hash.h>
#include <libxml/uri.h>
#include <libxml/parserInternals.h>
#include <libxml/catalog.h>
#include <libxml/xmlerror.h>
#include <libxml/threads.h>
#include <libxml/globals.h>

#define MAX_DELEGATE    50
#define MAX_CATAL_DEPTH 50

#ifdef _WIN32
# define PATH_SEAPARATOR ';'
#else
# define PATH_SEAPARATOR ':'
#endif

/**
 * TODO:
 *
 * macro to flag unimplemented blocks
 * XML_CATALOG_PREFER user env to select between system/public prefered
 * option. C.f. Richard Tobin <richard@cogsci.ed.ac.uk>
 *> Just FYI, I am using an environment variable XML_CATALOG_PREFER with
 *> values "system" and "public".  I have made the default be "system" to
 *> match yours.
 */
#define TODO                                                \
    xmlGenericError(xmlGenericErrorContext,                       \
          "Unimplemented block at %s:%d\n",                       \
            __FILE__, __LINE__);

#define XML_URN_PUBID "urn:publicid:"
#define XML_CATAL_BREAK ((xmlChar *) -1)
#ifndef XML_XML_DEFAULT_CATALOG
#define XML_XML_DEFAULT_CATALOG "file:///etc/xml/catalog"
#endif
#ifndef XML_SGML_DEFAULT_CATALOG
#define XML_SGML_DEFAULT_CATALOG "file:///etc/sgml/catalog"
#endif

#if defined(_WIN32) && defined(_MSC_VER)
#undef XML_XML_DEFAULT_CATALOG
static char XML_XML_DEFAULT_CATALOG[256] = "file:///etc/xml/catalog";
#if defined(_WIN32_WCE)
/* Windows CE don't have a A variant */
#define GetModuleHandleA GetModuleHandle
#define GetModuleFileNameA GetModuleFileName
#else
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 informations
 *
 * Handle an out of memory condition
 */
static void
xmlCatalogErrMemory(const char *extra)
{
    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_CATALOG,
                    XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0,
                extra, NULL, NULL, 0, 0,
                "Memory allocation failed : %s\n", extra);
}

/**
 * xmlCatalogErr:
 * @catal: the Catalog entry
 * @node: the context node
 * @msg:  the error message
 * @extra:  extra informations
 *
 * Handle a catalog error
 */
static void
xmlCatalogErr(xmlCatalogEntryPtr catal, xmlNodePtr node, int error,
               const char *msg, const xmlChar *str1, const xmlChar *str2,
             const xmlChar *str3)
{
    __xmlRaiseError(NULL, NULL, NULL, catal, node, XML_FROM_CATALOG,
                    error, XML_ERR_ERROR, NULL, 0,
                (const char *) str1, (const char *) str2,
                (const char *) str3, 0, 0,
                msg, str1, str2, str3);
}


/************************************************************************
 *                                                    *
 *                Allocation and Freeing                    *
 *                                                    *
 ************************************************************************/

/**
 * xmlNewCatalogEntry:
 * @type:  type of entry
 * @name:  name of the entry
 * @value:  value of the entry
 * @prefer:  the PUBLIC vs. SYSTEM current preference value
 * @group:  for members of a group, the group entry 
 *
 * create a new Catalog entry, this type is shared both by XML and 
 * SGML catalogs, but the acceptable types values differs.
 *
 * Returns the xmlCatalogEntryPtr or NULL in case of error
 */
static xmlCatalogEntryPtr
xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name,
         const xmlChar *value, const xmlChar *URL, xmlCatalogPrefer prefer,
         xmlCatalogEntryPtr group) {
    xmlCatalogEntryPtr ret;
    xmlChar *normid = NULL;

    ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));
    if (ret == NULL) {
        xmlCatalogErrMemory("allocating catalog entry");
      return(NULL);
    }
    ret->next = NULL;
    ret->parent = NULL;
    ret->children = NULL;
    ret->type = type;
    if (type == XML_CATA_PUBLIC || type == XML_CATA_DELEGATE_PUBLIC) {
        normid = xmlCatalogNormalizePublic(name);
        if (normid != NULL)
            name = (*normid != 0 ? normid : NULL);
    }
    if (name != NULL)
      ret->name = xmlStrdup(name);
    else
      ret->name = NULL;
    if (normid != NULL)
        xmlFree(normid);
    if (value != NULL)
      ret->value = xmlStrdup(value);
    else
      ret->value = NULL;
    if (URL == NULL)
      URL = value;
    if (URL != NULL)
      ret->URL = xmlStrdup(URL);
    else
      ret->URL = NULL;
    ret->prefer = prefer;
    ret->dealloc = 0;
    ret->depth = 0;
    ret->group = group;
    return(ret);
}

static void
xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret);

/**
 * xmlFreeCatalogEntry:
 * @ret:  a Catalog entry
 *
 * Free the memory allocated to a Catalog entry
 */
static void
xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) {
    if (ret == NULL)
      return;
    /*
     * Entries stored in the file hash must be deallocated
     * only by the file hash cleaner !
     */
    if (ret->dealloc == 1)
      return;

    if (xmlDebugCatalogs) {
      if (ret->name != NULL)
          xmlGenericError(xmlGenericErrorContext,
                "Free catalog entry %s\n", ret->name);
      else if (ret->value != NULL)
          xmlGenericError(xmlGenericErrorContext,
                "Free catalog entry %s\n", ret->value);
      else
          xmlGenericError(xmlGenericErrorContext,
                "Free catalog entry\n");
    }

    if (ret->name != NULL)
      xmlFree(ret->name);
    if (ret->value != NULL)
      xmlFree(ret->value);
    if (ret->URL != NULL)
      xmlFree(ret->URL);
    xmlFree(ret);
}

/**
 * xmlFreeCatalogEntryList:
 * @ret:  a Catalog entry list
 *
 * Free the memory allocated to a full chained list of Catalog entries
 */
static void
xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) {
    xmlCatalogEntryPtr next;

    while (ret != NULL) {
      next = ret->next;
      xmlFreeCatalogEntry(ret);
      ret = next;
    }
}

/**
 * xmlFreeCatalogHashEntryList:
 * @ret:  a Catalog entry list
 *
 * Free the memory allocated to list of Catalog entries from the
 * catalog file hash.
 */
static void
xmlFreeCatalogHashEntryList(xmlCatalogEntryPtr catal) {
    xmlCatalogEntryPtr children, next;

    if (catal == NULL)
      return;

    children = catal->children;
    while (children != NULL) {
      next = children->next;
      children->dealloc = 0;
      children->children = NULL;
      xmlFreeCatalogEntry(children);
      children = next;
    }
    catal->dealloc = 0;
    xmlFreeCatalogEntry(catal);
}

/**
 * xmlCreateNewCatalog:
 * @type:  type of catalog
 * @prefer:  the PUBLIC vs. SYSTEM current preference value
 *
 * create a new Catalog, this type is shared both by XML and 
 * SGML catalogs, but the acceptable types values differs.
 *
 * Returns the xmlCatalogPtr or NULL in case of error
 */
static xmlCatalogPtr
xmlCreateNewCatalog(xmlCatalogType type, xmlCatalogPrefer prefer) {
    xmlCatalogPtr ret;

    ret = (xmlCatalogPtr) xmlMalloc(sizeof(xmlCatalog));
    if (ret == NULL) {
        xmlCatalogErrMemory("allocating catalog");
      return(NULL);
    }
    memset(ret, 0, sizeof(xmlCatalog));
    ret->type = type;
    ret->catalNr = 0;
    ret->catalMax = XML_MAX_SGML_CATA_DEPTH;
    ret->prefer = prefer;
    if (ret->type == XML_SGML_CATALOG_TYPE)
      ret->sgml = xmlHashCreate(10);
    return(ret);
}

/**
 * xmlFreeCatalog:
 * @catal:  a Catalog
 *
 * Free the memory allocated to a Catalog
 */
void
xmlFreeCatalog(xmlCatalogPtr catal) {
    if (catal == NULL)
      return;
    if (catal->xml != NULL)
      xmlFreeCatalogEntryList(catal->xml);
    if (catal->sgml != NULL)
      xmlHashFree(catal->sgml,
            (xmlHashDeallocator) xmlFreeCatalogEntry);
    xmlFree(catal);
}

/************************************************************************
 *                                                    *
 *                Serializing Catalogs                      *
 *                                                    *
 ************************************************************************/

#ifdef LIBXML_OUTPUT_ENABLED
/**
 * xmlCatalogDumpEntry:
 * @entry:  the catalog entry
 * @out:  the file.
 *
 * Serialize an SGML Catalog entry
 */
static void
xmlCatalogDumpEntry(xmlCatalogEntryPtr entry, FILE *out) {
    if ((entry == NULL) || (out == NULL))
      return;
    switch (entry->type) {
      case SGML_CATA_ENTITY:
          fprintf(out, "ENTITY "); break;
      case SGML_CATA_PENTITY:
          fprintf(out, "ENTITY %%"); break;
      case SGML_CATA_DOCTYPE:
          fprintf(out, "DOCTYPE "); break;
      case SGML_CATA_LINKTYPE:
          fprintf(out, "LINKTYPE "); break;
      case SGML_CATA_NOTATION:
          fprintf(out, "NOTATION "); break;
      case SGML_CATA_PUBLIC:
          fprintf(out, "PUBLIC "); break;
      case SGML_CATA_SYSTEM:
          fprintf(out, "SYSTEM "); break;
      case SGML_CATA_DELEGATE:
          fprintf(out, "DELEGATE "); break;
      case SGML_CATA_BASE:
          fprintf(out, "BASE "); break;
      case SGML_CATA_CATALOG:
          fprintf(out, "CATALOG "); break;
      case SGML_CATA_DOCUMENT:
          fprintf(out, "DOCUMENT "); break;
      case SGML_CATA_SGMLDECL:
          fprintf(out, "SGMLDECL "); break;
      default:
          return;
    }
    switch (entry->type) {
      case SGML_CATA_ENTITY:
      case SGML_CATA_PENTITY:
      case SGML_CATA_DOCTYPE:
      case SGML_CATA_LINKTYPE:
      case SGML_CATA_NOTATION:
          fprintf(out, "%s", (const char *) entry->name); break;
      case SGML_CATA_PUBLIC:
      case SGML_CATA_SYSTEM:
      case SGML_CATA_SGMLDECL:
      case SGML_CATA_DOCUMENT:
      case SGML_CATA_CATALOG:
      case SGML_CATA_BASE:
      case SGML_CATA_DELEGATE:
          fprintf(out, "\"%s\"", entry->name); break;
      default:
          break;
    }
    switch (entry->type) {
      case SGML_CATA_ENTITY:
      case SGML_CATA_PENTITY:
      case SGML_CATA_DOCTYPE:
      case SGML_CATA_LINKTYPE:
      case SGML_CATA_NOTATION:
      case SGML_CATA_PUBLIC:
      case SGML_CATA_SYSTEM:
      case SGML_CATA_DELEGATE:
          fprintf(out, " \"%s\"", entry->value); break;
      default:
          break;
    }
    fprintf(out, "\n");
}

/**
 * xmlDumpXMLCatalogNode:
 * @catal:  top catalog entry
 * @catalog: pointer to the xml tree
 * @doc: the containing document
 * @ns: the current namespace
 * @cgroup: group node for group members
 *
 * Serializes a Catalog entry, called by xmlDumpXMLCatalog and recursively
 * for group entries
 */
static void xmlDumpXMLCatalogNode(xmlCatalogEntryPtr catal, xmlNodePtr catalog,
                xmlDocPtr doc, xmlNsPtr ns, xmlCatalogEntryPtr cgroup) {
    xmlNodePtr node;
    xmlCatalogEntryPtr cur;
    /*
     * add all the catalog entries
     */
    cur = catal;
    while (cur != NULL) {
        if (cur->group == cgroup) {
          switch (cur->type) {
              case XML_CATA_REMOVED:
                break;
              case XML_CATA_BROKEN_CATALOG:
              case XML_CATA_CATALOG:
                if (cur == catal) {
                  cur = cur->children;
                    continue;
                }
                break;
            case XML_CATA_NEXT_CATALOG:
                node = xmlNewDocNode(doc, ns, BAD_CAST "nextCatalog", NULL);
                xmlSetProp(node, BAD_CAST "catalog", cur->value);
                xmlAddChild(catalog, node);
                    break;
            case XML_CATA_NONE:
                break;
            case XML_CATA_GROUP:
                node = xmlNewDocNode(doc, ns, BAD_CAST "group", NULL);
                xmlSetProp(node, BAD_CAST "id", cur->name);
                if (cur->value != NULL) {
                    xmlNsPtr xns;
                  xns = xmlSearchNsByHref(doc, node, XML_XML_NAMESPACE);
                  if (xns != NULL)
                      xmlSetNsProp(node, xns, BAD_CAST "base",
                               cur->value);
                }
                switch (cur->prefer) {
                  case XML_CATA_PREFER_NONE:
                        break;
                  case XML_CATA_PREFER_PUBLIC:
                        xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "public");
                      break;
                  case XML_CATA_PREFER_SYSTEM:
                        xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "system");
                      break;
                }
                xmlDumpXMLCatalogNode(cur->next, node, doc, ns, cur);
                xmlAddChild(catalog, node);
                  break;
            case XML_CATA_PUBLIC:
                node = xmlNewDocNode(doc, ns, BAD_CAST "public", NULL);
                xmlSetProp(node, BAD_CAST "publicId", cur->name);
                xmlSetProp(node, BAD_CAST "uri", cur->value);
                xmlAddChild(catalog, node);
                break;
            case XML_CATA_SYSTEM:
                node = xmlNewDocNode(doc, ns, BAD_CAST "system", NULL);
                xmlSetProp(node, BAD_CAST "systemId", cur->name);
                xmlSetProp(node, BAD_CAST "uri", cur->value);
                xmlAddChild(catalog, node);
                break;
            case XML_CATA_REWRITE_SYSTEM:
                node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteSystem", NULL);
                xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
                xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
                xmlAddChild(catalog, node);
                break;
            case XML_CATA_DELEGATE_PUBLIC:
                node = xmlNewDocNode(doc, ns, BAD_CAST "delegatePublic", NULL);
                xmlSetProp(node, BAD_CAST "publicIdStartString", cur->name);
                xmlSetProp(node, BAD_CAST "catalog", cur->value);
                xmlAddChild(catalog, node);
                break;
            case XML_CATA_DELEGATE_SYSTEM:
                node = xmlNewDocNode(doc, ns, BAD_CAST "delegateSystem", NULL);
                xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
                xmlSetProp(node, BAD_CAST "catalog", cur->value);
                xmlAddChild(catalog, node);
                break;
            case XML_CATA_URI:
                node = xmlNewDocNode(doc, ns, BAD_CAST "uri", NULL);
                xmlSetProp(node, BAD_CAST "name", cur->name);
                xmlSetProp(node, BAD_CAST "uri", cur->value);
                xmlAddChild(catalog, node);
                break;
            case XML_CATA_REWRITE_URI:
                node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteURI", NULL);
                xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
                xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
                xmlAddChild(catalog, node);
                break;
            case XML_CATA_DELEGATE_URI:
                node = xmlNewDocNode(doc, ns, BAD_CAST "delegateURI", NULL);
                xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
                xmlSetProp(node, BAD_CAST "catalog", cur->value);
                xmlAddChild(catalog, node);
                break;
            case SGML_CATA_SYSTEM:
            case SGML_CATA_PUBLIC:
            case SGML_CATA_ENTITY:
            case SGML_CATA_PENTITY:
            case SGML_CATA_DOCTYPE:
            case SGML_CATA_LINKTYPE:
            case SGML_CATA_NOTATION:
            case SGML_CATA_DELEGATE:
            case SGML_CATA_BASE:
            case SGML_CATA_CATALOG:
            case SGML_CATA_DOCUMENT:
            case SGML_CATA_SGMLDECL:
                break;
          }
        }
      cur = cur->next;
    }
}

static int
xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) {
    int ret;
    xmlDocPtr doc;
    xmlNsPtr ns;
    xmlDtdPtr dtd;
    xmlNodePtr catalog;
    xmlOutputBufferPtr buf;

    /*
     * Rebuild a catalog
     */
    doc = xmlNewDoc(NULL);
    if (doc == NULL)
      return(-1);
    dtd = xmlNewDtd(doc, BAD_CAST "catalog",
             BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN",
BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd");

    xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);

    ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL);
    if (ns == NULL) {
      xmlFreeDoc(doc);
      return(-1);
    }
    catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL);
    if (catalog == NULL) {
      xmlFreeNs(ns);
      xmlFreeDoc(doc);
      return(-1);
    }
    catalog->nsDef = ns;
    xmlAddChild((xmlNodePtr) doc, catalog);

    xmlDumpXMLCatalogNode(catal, catalog, doc, ns, NULL);
    
    /*
     * reserialize it
     */
    buf = xmlOutputBufferCreateFile(out, NULL);
    if (buf == NULL) {
      xmlFreeDoc(doc);
      return(-1);
    }
    ret = xmlSaveFormatFileTo(buf, doc, NULL, 1);

    /*
     * Free it
     */
    xmlFreeDoc(doc);

    return(ret);
}
#endif /* LIBXML_OUTPUT_ENABLED */

/************************************************************************
 *                                                    *
 *                Converting SGML Catalogs to XML                 *
 *                                                    *
 ************************************************************************/

/**
 * xmlCatalogConvertEntry:
 * @entry:  the entry
 * @catal:  pointer to the catalog being converted
 *
 * Convert one entry from the catalog
 */
static void
xmlCatalogConvertEntry(xmlCatalogEntryPtr entry, xmlCatalogPtr catal) {
    if ((entry == NULL) || (catal == NULL) || (catal->sgml == NULL) ||
      (catal->xml == NULL))
      return;
    switch (entry->type) {
      case SGML_CATA_ENTITY:
          entry->type = XML_CATA_PUBLIC;
          break;
      case SGML_CATA_PENTITY:
          entry->type = XML_CATA_PUBLIC;
          break;
      case SGML_CATA_DOCTYPE:
          entry->type = XML_CATA_PUBLIC;
          break;
      case SGML_CATA_LINKTYPE:
          entry->type = XML_CATA_PUBLIC;
          break;
      case SGML_CATA_NOTATION:
          entry->type = XML_CATA_PUBLIC;
          break;
      case SGML_CATA_PUBLIC:
          entry->type = XML_CATA_PUBLIC;
          break;
      case SGML_CATA_SYSTEM:
          entry->type = XML_CATA_SYSTEM;
          break;
      case SGML_CATA_DELEGATE:
          entry->type = XML_CATA_DELEGATE_PUBLIC;
          break;
      case SGML_CATA_CATALOG:
          entry->type = XML_CATA_CATALOG;
          break;
      default:
          xmlHashRemoveEntry(catal->sgml, entry->name,
                           (xmlHashDeallocator) xmlFreeCatalogEntry);
          return;
    }
    /*
     * Conversion successful, remove from the SGML catalog
     * and add it to the default XML one
     */
    xmlHashRemoveEntry(catal->sgml, entry->name, NULL);
    entry->parent = catal->xml;
    entry->next = NULL;
    if (catal->xml->children == NULL)
      catal->xml->children = entry;
    else {
      xmlCatalogEntryPtr prev;

      prev = catal->xml->children;
      while (prev->next != NULL)
          prev = prev->next;
      prev->next = entry;
    }
}

/**
 * xmlConvertSGMLCatalog:
 * @catal: the catalog
 *
 * Convert all the SGML catalog entries as XML ones
 *
 * Returns the number of entries converted if successful, -1 otherwise
 */
int
xmlConvertSGMLCatalog(xmlCatalogPtr catal) {

    if ((catal == NULL) || (catal->type != XML_SGML_CATALOG_TYPE))
      return(-1);

    if (xmlDebugCatalogs) {
      xmlGenericError(xmlGenericErrorContext,
            "Converting SGML catalog to XML\n");
    }
    xmlHashScan(catal->sgml,
            (xmlHashScanner) xmlCatalogConvertEntry,
            &catal);
    return(0);
}

/************************************************************************
 *                                                    *
 *                Helper function                           *
 *                                                    *
 ************************************************************************/

/**
 * xmlCatalogUnWrapURN:
 * @urn:  an "urn:publicid:" to unwrap
 *
 * Expand the URN into the equivalent Public Identifier
 *
 * Returns the new identifier or NULL, the string must be deallocated
 *         by the caller.
 */
static xmlChar *
xmlCatalogUnWrapURN(const xmlChar *urn) {
    xmlChar result[2000];
    unsigned int i = 0;

    if (xmlStrncmp(urn, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1))
      return(NULL);
    urn += sizeof(XML_URN_PUBID) - 1;
    
    while (*urn != 0) {
      if (i > sizeof(result) - 4)
          break;
      if (*urn == '+') {
          result[i++] = ' ';
          urn++;
      } else if (*urn == ':') {
          result[i++] = '/';
          result[i++] = '/';
          urn++;
      } else if (*urn == ';') {
          result[i++] = ':';
          result[i++] = ':';
          urn++;
      } else if (*urn == '%') {
          if ((urn[1] == '2') && (urn[2] == 'B'))
            result[i++] = '+';
          else if ((urn[1] == '3') && (urn[2] == 'A'))
            result[i++] = ':';
          else if ((urn[1] == '2') && (urn[2] == 'F'))
            result[i++] = '/';
          else if ((urn[1] == '3') && (urn[2] == 'B'))
            result[i++] = ';';
          else if ((urn[1] == '2') && (urn[2] == '7'))
            result[i++] = '\'';
          else if ((urn[1] == '3') && (urn[2] == 'F'))
            result[i++] = '?';
          else if ((urn[1] == '2') && (urn[2] == '3'))
            result[i++] = '#';
          else if ((urn[1] == '2') && (urn[2] == '5'))
            result[i++] = '%';
          else {
            result[i++] = *urn;
            urn++;
            continue;
          }
          urn += 3;
      } else {
          result[i++] = *urn;
          urn++;
      }
    }
    result[i] = 0;

    return(xmlStrdup(result));
}

/**
 * xmlParseCatalogFile:
 * @filename:  the filename
 *
 * parse an XML file and build a tree. It's like xmlParseFile()
 * except it bypass all catalog lookups.
 *
 * Returns the resulting document tree or NULL in case of error
 */

xmlDocPtr
xmlParseCatalogFile(const char *filename) {
    xmlDocPtr ret;
    xmlParserCtxtPtr ctxt;
    char *directory = NULL;
    xmlParserInputPtr inputStream;
    xmlParserInputBufferPtr buf;

    ctxt = xmlNewParserCtxt();
    if (ctxt == NULL) {
#ifdef LIBXML_SAX1_ENABLED
      if (xmlDefaultSAXHandler.error != NULL) {
          xmlDefaultSAXHandler.error(NULL, "out of memory\n");
      }
#endif
      return(NULL);
    }

    buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE);
    if (buf == NULL) {
      xmlFreeParserCtxt(ctxt);
      return(NULL);
    }

    inputStream = xmlNewInputStream(ctxt);
    if (inputStream == NULL) {
      xmlFreeParserCtxt(ctxt);
      return(NULL);
    }

    inputStream->filename = (char *) xmlCanonicPath((const xmlChar *)filename);
    inputStream->buf = buf;
    inputStream->base = inputStream->buf->buffer->content;
    inputStream->cur = inputStream->buf->buffer->content;
    inputStream->end = 
      &inputStream->buf->buffer->content[inputStream->buf->buffer->use];

    inputPush(ctxt, inputStream);
    if ((ctxt->directory == NULL) && (directory == NULL))
        directory = xmlParserGetDirectory(filename);
    if ((ctxt->directory == NULL) && (directory != NULL))
        ctxt->directory = directory;
    ctxt->valid = 0;
    ctxt->validate = 0;
    ctxt->loadsubset = 0;
    ctxt->pedantic = 0;
    ctxt->dictNames = 1;

    xmlParseDocument(ctxt);

    if (ctxt->wellFormed)
      ret = ctxt->myDoc;
    else {
        ret = NULL;
        xmlFreeDoc(ctxt->myDoc);
        ctxt->myDoc = NULL;
    }
    xmlFreeParserCtxt(ctxt);
    
    return(ret);
}

/**
 * xmlLoadFileContent:
 * @filename:  a file path
 *
 * Load a file content into memory.
 *
 * Returns a pointer to the 0 terminated string or NULL in case of error
 */
static xmlChar *
xmlLoadFileContent(const char *filename)
{
#ifdef HAVE_STAT
    int fd;
#else
    FILE *fd;
#endif
    int len;
    long size;

#ifdef HAVE_STAT
    struct stat info;
#endif
    xmlChar *content;

    if (filename == NULL)
        return (NULL);

#ifdef HAVE_STAT
    if (stat(filename, &info) < 0)
        return (NULL);
#endif

#ifdef HAVE_STAT
    if ((fd = open(filename, O_RDONLY)) < 0)
#else
    if ((fd = fopen(filename, "rb")) == NULL)
#endif
    {
        return (NULL);
    }
#ifdef HAVE_STAT
    size = info.st_size;
#else
    if (fseek(fd, 0, SEEK_END) || (size = ftell(fd)) == EOF || fseek(fd, 0, SEEK_SET)) {        /* File operations denied? ok, just close and return failure */
        fclose(fd);
        return (NULL);
    }
#endif
    content = xmlMallocAtomic(size + 10);
    if (content == NULL) {
        xmlCatalogErrMemory("allocating catalog data");
        return (NULL);
    }
#ifdef HAVE_STAT
    len = read(fd, content, size);
#else
    len = fread(content, 1, size, fd);
#endif
    if (len < 0) {
        xmlFree(content);
        return (NULL);
    }
#ifdef HAVE_STAT
    close(fd);
#else
    fclose(fd);
#endif
    content[len] = 0;

    return(content);
}

/**
 * xmlCatalogNormalizePublic:
 * @pubID:  the public ID string
 *
 *  Normalizes the Public Identifier
 *
 * Implements 6.2. Public Identifier Normalization
 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
 *
 * Returns the new string or NULL, the string must be deallocated
 *         by the caller.
 */
static xmlChar *
xmlCatalogNormalizePublic(const xmlChar *pubID)
{
    int ok = 1;
    int white;
    const xmlChar *p;
    xmlChar *ret;
    xmlChar *q;

    if (pubID == NULL)
        return(NULL);

    white = 1;
    for (p = pubID;*p != 0 && ok;p++) {
        if (!xmlIsBlank_ch(*p))
            white = 0;
        else if (*p == 0x20 && !white)
            white = 1;
        else
            ok = 0;
    }
    if (ok && !white)   /* is normalized */
        return(NULL);

    ret = xmlStrdup(pubID);
    q = ret;
    white = 0;
    for (p = pubID;*p != 0;p++) {
        if (xmlIsBlank_ch(*p)) {
            if (q != ret)
                white = 1;
        } else {
            if (white) {
                *(q++) = 0x20;
                white = 0;
            }
            *(q++) = *p;
        }
    }
    *q = 0;
    return(ret);
}

/************************************************************************
 *                                                    *
 *                The XML Catalog parser                    *
 *                                                    *
 ************************************************************************/

static xmlCatalogEntryPtr
xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
static void
xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
                         xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup);
static xmlChar *
xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
                    const xmlChar *sysID);
static xmlChar *
xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI);


/**
 * xmlGetXMLCatalogEntryType:
 * @name:  the name
 *
 * lookup the internal type associated to an XML catalog entry name
 *
 * Returns the type associated with that name
 */
static xmlCatalogEntryType
xmlGetXMLCatalogEntryType(const xmlChar *name) {
    xmlCatalogEntryType type = XML_CATA_NONE;
    if (xmlStrEqual(name, (const xmlChar *) "system"))
      type = XML_CATA_SYSTEM;
    else if (xmlStrEqual(name, (const xmlChar *) "public"))
      type = XML_CATA_PUBLIC;
    else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem"))
      type = XML_CATA_REWRITE_SYSTEM;
    else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic"))
      type = XML_CATA_DELEGATE_PUBLIC;
    else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem"))
      type = XML_CATA_DELEGATE_SYSTEM;
    else if (xmlStrEqual(name, (const xmlChar *) "uri"))
      type = XML_CATA_URI;
    else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI"))
      type = XML_CATA_REWRITE_URI;
    else if (xmlStrEqual(name, (const xmlChar *) "delegateURI"))
      type = XML_CATA_DELEGATE_URI;
    else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog"))
      type = XML_CATA_NEXT_CATALOG;
    else if (xmlStrEqual(name, (const xmlChar *) "catalog"))
      type = XML_CATA_CATALOG;
    return(type);
}

/**
 * xmlParseXMLCatalogOneNode:
 * @cur:  the XML node
 * @type:  the type of Catalog entry
 * @name:  the name of the node
 * @attrName:  the attribute holding the value
 * @uriAttrName:  the attribute holding the URI-Reference
 * @prefer:  the PUBLIC vs. SYSTEM current preference value
 * @cgroup:  the group which includes this node
 *
 * Finishes the examination of an XML tree node of a catalog and build
 * a Catalog entry from it.
 *
 * Returns the new Catalog entry node or NULL in case of error.
 */
static xmlCatalogEntryPtr
xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type,
                    const xmlChar *name, const xmlChar *attrName,
                    const xmlChar *uriAttrName, xmlCatalogPrefer prefer,
                    xmlCatalogEntryPtr cgroup) {
    int ok = 1;
    xmlChar *uriValue;
    xmlChar *nameValue = NULL;
    xmlChar *base = NULL;
    xmlChar *URL = NULL;
    xmlCatalogEntryPtr ret = NULL;

    if (attrName != NULL) {
      nameValue = xmlGetProp(cur, attrName);
      if (nameValue == NULL) {
          xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
                    "%s entry lacks '%s'\n", name, attrName, NULL);
          ok = 0;
      }
    }
    uriValue = xmlGetProp(cur, uriAttrName);
    if (uriValue == NULL) {
      xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
            "%s entry lacks '%s'\n", name, uriAttrName, NULL);
      ok = 0;
    }
    if (!ok) {
      if (nameValue != NULL)
          xmlFree(nameValue);
      if (uriValue != NULL)
          xmlFree(uriValue);
      return(NULL);
    }

    base = xmlNodeGetBase(cur->doc, cur);
    URL = xmlBuildURI(uriValue, base);
    if (URL != NULL) {
      if (xmlDebugCatalogs > 1) {
          if (nameValue != NULL)
            xmlGenericError(xmlGenericErrorContext,
                  "Found %s: '%s' '%s'\n", name, nameValue, URL);
          else
            xmlGenericError(xmlGenericErrorContext,
                  "Found %s: '%s'\n", name, URL);
      }
      ret = xmlNewCatalogEntry(type, nameValue, uriValue, URL, prefer, cgroup);
    } else {
      xmlCatalogErr(ret, cur, XML_CATALOG_ENTRY_BROKEN,
            "%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue);
    }
    if (nameValue != NULL)
      xmlFree(nameValue);
    if (uriValue != NULL)
      xmlFree(uriValue);
    if (base != NULL)
      xmlFree(base);
    if (URL != NULL)
      xmlFree(URL);
    return(ret);
}

/**
 * xmlParseXMLCatalogNode:
 * @cur:  the XML node
 * @prefer:  the PUBLIC vs. SYSTEM current preference value
 * @parent:  the parent Catalog entry
 * @cgroup:  the group which includes this node
 *
 * Examines an XML tree node of a catalog and build
 * a Catalog entry from it adding it to its parent. The examination can
 * be recursive.
 */
static void
xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer,
                     xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup)
{
    xmlChar *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);
    if (catal->children != NULL)
      return(-1);

    /*
     * lock the whole catalog for modification
     */
    xmlRMutexLock(xmlCatalogMutex);
    if (catal->children != NULL) {
      /* Okay someone else did it in the meantime */
      xmlRMutexUnlock(xmlCatalogMutex);
      return(0);
    }

    if (xmlCatalogXMLFiles != NULL) {
      doc = (xmlCatalogEntryPtr)
          xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
      if (doc != NULL) {
          if (xmlDebugCatalogs)
            xmlGenericError(xmlGenericErrorContext,
                "Found %s in file hash\n", catal->URL);

          if (catal->type == XML_CATA_CATALOG)
            catal->children = doc->children;
          else
            catal->children = doc;
          catal->dealloc = 0;
          xmlRMutexUnlock(xmlCatalogMutex);
          return(0);
      }
      if (xmlDebugCatalogs)
          xmlGenericError(xmlGenericErrorContext,
            "%s not found in file hash\n", catal->URL);
    }

    /*
     * Fetch and parse. Note that xmlParseXMLCatalogFile does not
     * use the existing catalog, there is no recursion allowed at
     * that level.
     */
    doc = xmlParseXMLCatalogFile(catal->prefer, catal->URL);
    if (doc == NULL) {
      catal->type = XML_CATA_BROKEN_CATALOG;
      xmlRMutexUnlock(xmlCatalogMutex);
      return(-1);
    }

    if (catal->type == XML_CATA_CATALOG)
      catal->children = doc->children;
    else
      catal->children = doc;

    doc->dealloc = 1;

    if (xmlCatalogXMLFiles == NULL)
      xmlCatalogXMLFiles = xmlHashCreate(10);
    if (xmlCatalogXMLFiles != NULL) {
      if (xmlDebugCatalogs)
          xmlGenericError(xmlGenericErrorContext,
            "%s added to file hash\n", catal->URL);
      xmlHashAddEntry(xmlCatalogXMLFiles, catal->URL, doc);
    }
    xmlRMutexUnlock(xmlCatalogMutex);
    return(0);
}

/************************************************************************
 *                                                    *
 *                XML Catalog handling                      *
 *                                                    *
 ************************************************************************/

/**
 * xmlAddXMLCatalog:
 * @catal:  top of an XML catalog
 * @type:  the type of record to add to the catalog
 * @orig:  the system, public or prefix to match (or NULL)
 * @replace:  the replacement value for the match
 *
 * Add an entry in the XML catalog, it may overwrite existing but
 * different entries.
 *
 * Returns 0 if successful, -1 otherwise
 */
static int
xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type,
            const xmlChar *orig, const xmlChar *replace) {
    xmlCatalogEntryPtr cur;
    xmlCatalogEntryType typ;
    int doregister = 0;

    if ((catal == NULL) || 
      ((catal->type != XML_CATA_CATALOG) &&
       (catal->type != XML_CATA_BROKEN_CATALOG)))
      return(-1);
    if (catal->children == NULL) {
      xmlFetchXMLCatalogFile(catal);
    }
    if (catal->children == NULL)
      doregister = 1;

    typ = xmlGetXMLCatalogEntryType(type);
    if (typ == XML_CATA_NONE) {
      if (xmlDebugCatalogs)
          xmlGenericError(xmlGenericErrorContext,
                "Failed to add unknown element %s to catalog\n", type);
      return(-1);
    }

    cur = catal->children;
    /*
     * Might be a simple "update in place"
     */
    if (cur != NULL) {
      while (cur != NULL) {
          if ((orig != NULL) && (cur->type == typ) &&
            (xmlStrEqual(orig, cur->name))) {
            if (xmlDebugCatalogs)
                xmlGenericError(xmlGenericErrorContext,
                      "Updating element %s to catalog\n", type);
            if (cur->value != NULL)
                xmlFree(cur->value);
            if (cur->URL != NULL)
                xmlFree(cur->URL);
            cur->value = xmlStrdup(replace);
            cur->URL = xmlStrdup(replace);
            return(0);
          }
          if (cur->next == NULL)
            break;
          cur = cur->next;
      }
    }
    if (xmlDebugCatalogs)
      xmlGenericError(xmlGenericErrorContext,
            "Adding element %s to catalog\n", type);
    if (cur == NULL)
      catal->children = xmlNewCatalogEntry(typ, orig, replace,
                                         NULL, catal->prefer, NULL);
    else
      cur->next = xmlNewCatalogEntry(typ, orig, replace,
                                   NULL, catal->prefer, NULL);
    if (doregister) {
        catal->type = XML_CATA_CATALOG;
      cur = xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
      if (cur != NULL)
          cur->children = catal->children;
    }

    return(0);
}

/**
 * xmlDelXMLCatalog:
 * @catal:  top of an XML catalog
 * @value:  the value to remove from the catalog
 *
 * Remove entries in the XML catalog where the value or the URI
 * is equal to @value
 *
 * Returns the number of entries removed if successful, -1 otherwise
 */
static int
xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) {
    xmlCatalogEntryPtr cur;
    int ret = 0;

    if ((catal == NULL) || 
      ((catal->type != XML_CATA_CATALOG) &&
       (catal->type != XML_CATA_BROKEN_CATALOG)))
      return(-1);
    if (value == NULL)
      return(-1);
    if (catal->children == NULL) {
      xmlFetchXMLCatalogFile(catal);
    }

    /*
     * Scan the children
     */
    cur = catal->children;
    while (cur != NULL) {
      if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) ||
          (xmlStrEqual(value, cur->value))) {
          if (xmlDebugCatalogs) {
            if (cur->name != NULL)
                xmlGenericError(xmlGenericErrorContext,
                      "Removing element %s from catalog\n", cur->name);
            else
                xmlGenericError(xmlGenericErrorContext,
                      "Removing element %s from catalog\n", cur->value);
          }
          cur->type = XML_CATA_REMOVED;
      }
      cur = cur->next;
    }
    return(ret);
}

/**
 * xmlCatalogXMLResolve:
 * @catal:  a catalog list
 * @pubID:  the public ID string
 * @sysID:  the system ID string
 *
 * Do a complete resolution lookup of an External Identifier for a
 * list of catalog entries.
 *
 * Implements (or tries to) 7.1. External Identifier Resolution
 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
 *
 * Returns the URI of the resource or NULL if not found
 */
static xmlChar *
xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
                    const xmlChar *sysID) {
    xmlChar *ret = NULL;
    xmlCatalogEntryPtr cur;
    int haveDelegate = 0;
    int haveNext = 0;

    /*
     * protection against loops
     */
    if (catal->depth > MAX_CATAL_DEPTH) {
      xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
                  "Detected recursion in catalog %s\n",
                  catal->name, NULL, NULL);
      return(NULL);
    }
    catal->depth++;

    /*
     * First tries steps 2/ 3/ 4/ if a system ID is provided.
     */
    if (sysID != NULL) {
      xmlCatalogEntryPtr rewrite = NULL;
      int lenrewrite = 0, len;
      cur = catal;
      haveDelegate = 0;
      while (cur != NULL) {
          switch (cur->type) {
            case XML_CATA_SYSTEM:
                if (xmlStrEqual(sysID, cur->name)) {
                  if (xmlDebugCatalogs)
                      xmlGenericError(xmlGenericErrorContext,
                            "Found system match %s, 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 != NULL) &&
                       (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 (name == NULL) {
            /* error */
            break;
          }
          if (!IS_BLANK_CH(*cur)) {
            /* error */
            break;
          }
          SKIP_BLANKS;
          if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
                type = SGML_CATA_SYSTEM;
          else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
                type = SGML_CATA_PUBLIC;
          else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
                type = SGML_CATA_DELEGATE;
          else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
                type = SGML_CATA_ENTITY;
          else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
                type = SGML_CATA_DOCTYPE;
          else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
                type = SGML_CATA_LINKTYPE;
          else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
                type = SGML_CATA_NOTATION;
          else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
                type = SGML_CATA_SGMLDECL;
          else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
                type = SGML_CATA_DOCUMENT;
          else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
                type = SGML_CATA_CATALOG;
          else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
                type = SGML_CATA_BASE;
          else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {
            xmlFree(name);
            cur = xmlParseSGMLCatalogName(cur, &name);
            if (name == NULL) {
                /* error */
                break;
            }
            xmlFree(name);
            continue;
          }
          xmlFree(name);
          name = NULL;

          switch(type) {
            case SGML_CATA_ENTITY:
                if (*cur == '%')
                  type = SGML_CATA_PENTITY;
            case SGML_CATA_PENTITY:
            case SGML_CATA_DOCTYPE:
            case SGML_CATA_LINKTYPE:
            case SGML_CATA_NOTATION:
                cur = xmlParseSGMLCatalogName(cur, &name);
                if (cur == NULL) {
                  /* error */
                  break;
                }
                if (!IS_BLANK_CH(*cur)) {
                  /* error */
                  break;
                }
                SKIP_BLANKS;
                cur = xmlParseSGMLCatalogPubid(cur, &sysid);
                if (cur == NULL) {
                  /* error */
                  break;
                }
                break;
            case SGML_CATA_PUBLIC:
            case SGML_CATA_SYSTEM:
            case SGML_CATA_DELEGATE:
                cur = xmlParseSGMLCatalogPubid(cur, &name);
                if (cur == NULL) {
                  /* error */
                  break;
                }
                if (type != SGML_CATA_SYSTEM) {
                    xmlChar *normid;

                    normid = xmlCatalogNormalizePublic(name);
                    if (normid != NULL) {
                        if (name != NULL)
                            xmlFree(name);
                        if (*normid != 0)
                            name = normid;
                        else {
                            xmlFree(normid);
                            name = NULL;
                        }
                    }
                }
                if (!IS_BLANK_CH(*cur)) {
                  /* error */
                  break;
                }
                SKIP_BLANKS;
                cur = xmlParseSGMLCatalogPubid(cur, &sysid);
                if (cur == NULL) {
                  /* error */
                  break;
                }
                break;
            case SGML_CATA_BASE:
            case SGML_CATA_CATALOG:
            case SGML_CATA_DOCUMENT:
            case SGML_CATA_SGMLDECL:
                cur = xmlParseSGMLCatalogPubid(cur, &sysid);
                if (cur == NULL) {
                  /* error */
                  break;
                }
                break;
            default:
                break;
          }
          if (cur == NULL) {
            if (name != NULL)
                xmlFree(name);
            if (sysid != NULL)
                xmlFree(sysid);
            break;
          } else if (type == SGML_CATA_BASE) {
            if (base != NULL)
                xmlFree(base);
            base = xmlStrdup(sysid);
          } else if ((type == SGML_CATA_PUBLIC) ||
                   (type == SGML_CATA_SYSTEM)) {
            xmlChar *filename;

            filename = xmlBuildURI(sysid, base);
            if (filename != NULL) {
                xmlCatalogEntryPtr entry;

                entry = xmlNewCatalogEntry(type, name, filename,
                                         NULL, XML_CATA_PREFER_NONE, NULL);
                res = xmlHashAddEntry(catal->sgml, name, entry);
                if (res < 0) {
                  xmlFreeCatalogEntry(entry);
                }
                xmlFree(filename);
            }

          } else if (type == SGML_CATA_CATALOG) {
            if (super) {
                xmlCatalogEntryPtr entry;

                entry = xmlNewCatalogEntry(type, sysid, NULL, NULL,
                                         XML_CATA_PREFER_NONE, NULL);
                res = xmlHashAddEntry(catal->sgml, sysid, entry);
                if (res < 0) {
                  xmlFreeCatalogEntry(entry);
                }
            } else {
                xmlChar *filename;

                filename = xmlBuildURI(sysid, base);
                if (filename != NULL) {
                  xmlExpandCatalog(catal, (const char *)filename);
                  xmlFree(filename);
                }
            }
          }
          /*
           * drop anything else we won't handle it
           */
          if (name != NULL)
            xmlFree(name);
          if (sysid != NULL)
            xmlFree(sysid);
      }
    }
    if (base != NULL)
      xmlFree(base);
    if (cur == NULL)
      return(-1);
    return(0);
}

/************************************************************************
 *                                                    *
 *                SGML Catalog handling                     *
 *                                                    *
 ************************************************************************/

/**
 * xmlCatalogGetSGMLPublic:
 * @catal:  an SGML catalog hash
 * @pubID:  the public ID string
 *
 * Try to lookup the catalog local reference associated to a public ID
 *
 * Returns the local resource if found or NULL otherwise.
 */
static const xmlChar *
xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) {
    xmlCatalogEntryPtr entry;
    xmlChar *normid;

    if (catal == NULL)
      return(NULL);

    normid = xmlCatalogNormalizePublic(pubID);
    if (normid != NULL)
        pubID = (*normid != 0 ? normid : NULL);

    entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID);
    if (entry == NULL) {
      if (normid != NULL)
          xmlFree(normid);
      return(NULL);
    }
    if (entry->type == SGML_CATA_PUBLIC) {
      if (normid != NULL)
          xmlFree(normid);
      return(entry->URL);
    }
    if (normid != NULL)
        xmlFree(normid);
    return(NULL);
}

/**
 * xmlCatalogGetSGMLSystem:
 * @catal:  an SGML catalog hash
 * @sysID:  the system ID string
 *
 * Try to lookup the catalog local reference for a system ID
 *
 * Returns the local resource if found or NULL otherwise.
 */
static const xmlChar *
xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) {
    xmlCatalogEntryPtr entry;

    if (catal == NULL)
      return(NULL);

    entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID);
    if (entry == NULL)
      return(NULL);
    if (entry->type == SGML_CATA_SYSTEM)
      return(entry->URL);
    return(NULL);
}

/**
 * xmlCatalogSGMLResolve:
 * @catal:  the SGML catalog
 * @pubID:  the public ID string
 * @sysID:  the system ID string
 *
 * Do a complete resolution lookup of an External Identifier
 *
 * Returns the URI of the resource or NULL if not found
 */
static const xmlChar *
xmlCatalogSGMLResolve(xmlCatalogPtr catal, const xmlChar *pubID,
                    const xmlChar *sysID) {
    const xmlChar *ret = NULL;

    if (catal->sgml == NULL)
      return(NULL);

    if (pubID != NULL)
      ret = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
    if (ret != NULL)
      return(ret);
    if (sysID != NULL)
      ret = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
    return(NULL);
}

/************************************************************************
 *                                                    *
 *                Specific Public interfaces                *
 *                                                    *
 ************************************************************************/

/**
 * xmlLoadSGMLSuperCatalog:
 * @filename:  a file path
 *
 * Load an SGML super catalog. It won't expand CATALOG or DELEGATE
 * references. This is only needed for manipulating SGML Super Catalogs
 * like adding and removing CATALOG or DELEGATE entries.
 *
 * Returns the catalog parsed or NULL in case of error
 */
xmlCatalogPtr
xmlLoadSGMLSuperCatalog(const char *filename)
{
    xmlChar *content;
    xmlCatalogPtr catal;
    int ret;

    content = xmlLoadFileContent(filename);
    if (content == NULL)
        return(NULL);

    catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
    if (catal == NULL) {
      xmlFree(content);
      return(NULL);
    }

    ret = xmlParseSGMLCatalog(catal, content, filename, 1);
    xmlFree(content);
    if (ret < 0) {
      xmlFreeCatalog(catal);
      return(NULL);
    }
    return (catal);
}

/**
 * xmlLoadACatalog:
 * @filename:  a file path
 *
 * Load the catalog and build the associated data structures.
 * This can be either an XML Catalog or an SGML Catalog
 * It will recurse in SGML CATALOG entries. On the other hand XML
 * Catalogs are not handled recursively.
 *
 * Returns the catalog parsed or NULL in case of error
 */
xmlCatalogPtr
xmlLoadACatalog(const char *filename)
{
    xmlChar *content;
    xmlChar *first;
    xmlCatalogPtr catal;
    int ret;

    content = xmlLoadFileContent(filename);
    if (content == NULL)
        return(NULL);


    first = content;
   
    while ((*first != 0) && (*first != '-') && (*first != '<') &&
         (!(((*first >= 'A') && (*first <= 'Z')) ||
            ((*first >= 'a') && (*first <= 'z')))))
      first++;

    if (*first != '<') {
      catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
      if (catal == NULL) {
          xmlFree(content);
          return(NULL);
      }
        ret = xmlParseSGMLCatalog(catal, content, filename, 0);
      if (ret < 0) {
          xmlFreeCatalog(catal);
          xmlFree(content);
          return(NULL);
      }
    } else {
      catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
      if (catal == NULL) {
          xmlFree(content);
          return(NULL);
      }
        catal->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
                   NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
    }
    xmlFree(content);
    return (catal);
}

/**
 * xmlExpandCatalog:
 * @catal:  a catalog
 * @filename:  a file path
 *
 * Load the catalog and expand the existing catal structure.
 * This can be either an XML Catalog or an SGML Catalog
 *
 * Returns 0 in case of success, -1 in case of error
 */
static int
xmlExpandCatalog(xmlCatalogPtr catal, const char *filename)
{
    int ret;

    if ((catal == NULL) || (filename == NULL))
      return(-1);


    if (catal->type == XML_SGML_CATALOG_TYPE) {
      xmlChar *content;

      content = xmlLoadFileContent(filename);
      if (content == NULL)
          return(-1);

        ret = xmlParseSGMLCatalog(catal, content, filename, 0);
      if (ret < 0) {
          xmlFree(content);
          return(-1);
      }
      xmlFree(content);
    } else {
      xmlCatalogEntryPtr tmp, cur;
      tmp = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
                   NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);

      cur = catal->xml;
      if (cur == NULL) {
          catal->xml = tmp;
      } else {
          while (cur->next != NULL) cur = cur->next;
          cur->next = tmp;
      }
    }
    return (0);
}

/**
 * xmlACatalogResolveSystem:
 * @catal:  a Catalog
 * @sysID:  the system ID string
 *
 * Try to lookup the catalog resource for a system ID
 *
 * Returns the resource if found or NULL otherwise, the value returned
 *      must be freed by the caller.
 */
xmlChar *
xmlACatalogResolveSystem(xmlCatalogPtr catal, const xmlChar *sysID) {
    xmlChar *ret = NULL;

    if ((sysID == NULL) || (catal == NULL))
      return(NULL);
    
    if (xmlDebugCatalogs)
      xmlGenericError(xmlGenericErrorContext,
            "Resolve sysID %s\n", sysID);

    if (catal->type == XML_XML_CATALOG_TYPE) {
      ret = xmlCatalogListXMLResolve(catal->xml, NULL, sysID);
      if (ret == XML_CATAL_BREAK)
          ret = NULL;
    } else {
      const xmlChar *sgml;

      sgml = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
      if (sgml != NULL)
          ret = xmlStrdup(sgml);
    }
    return(ret);
}

/**
 * xmlACatalogResolvePublic:
 * @catal:  a Catalog
 * @pubID:  the public ID string
 *
 * Try to lookup the catalog local reference associated to a public ID in that catalog
 *
 * Returns the local resource if found or NULL otherwise, the value returned
 *      must be freed by the caller.
 */
xmlChar *
xmlACatalogResolvePublic(xmlCatalogPtr catal, const xmlChar *pubID) {
    xmlChar *ret = NULL;

    if ((pubID == NULL) || (catal == NULL))
      return(NULL);
    
    if (xmlDebugCatalogs)
      xmlGenericError(xmlGenericErrorContext,
            "Resolve pubID %s\n", pubID);

    if (catal->type == XML_XML_CATALOG_TYPE) {
      ret = xmlCatalogListXMLResolve(catal->xml, pubID, NULL);
      if (ret == XML_CATAL_BREAK)
          ret = NULL;
    } else {
      const xmlChar *sgml;

      sgml = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
      if (sgml != NULL)
          ret = xmlStrdup(sgml);
    }
    return(ret);
}

/**
 * xmlACatalogResolve:
 * @catal:  a Catalog
 * @pubID:  the public ID string
 * @sysID:  the system ID string
 *
 * Do a complete resolution lookup of an External Identifier
 *
 * Returns the URI of the resource or NULL if not found, it must be freed
 *      by the caller.
 */
xmlChar *
xmlACatalogResolve(xmlCatalogPtr catal, const xmlChar * pubID,
                   const xmlChar * sysID)
{
    xmlChar *ret = NULL;

    if (((pubID == NULL) && (sysID == NULL)) || (catal == NULL))
        return (NULL);

    if (xmlDebugCatalogs) {
         if ((pubID != NULL) && (sysID != NULL)) {
             xmlGenericError(xmlGenericErrorContext,
                             "Resolve: pubID %s sysID %s\n", pubID, sysID);
         } else if (pubID != NULL) {
             xmlGenericError(xmlGenericErrorContext,
                             "Resolve: pubID %s\n", pubID);
         } else {
             xmlGenericError(xmlGenericErrorContext,
                             "Resolve: sysID %s\n", sysID);
         }
    }

    if (catal->type == XML_XML_CATALOG_TYPE) {
        ret = xmlCatalogListXMLResolve(catal->xml, pubID, sysID);
      if (ret == XML_CATAL_BREAK)
          ret = NULL;
    } else {
        const xmlChar *sgml;

        sgml = xmlCatalogSGMLResolve(catal, pubID, sysID);
        if (sgml != NULL)
            ret = xmlStrdup(sgml);
    }
    return (ret);
}

/**
 * xmlACatalogResolveURI:
 * @catal:  a Catalog
 * @URI:  the URI
 *
 * Do a complete resolution lookup of an URI
 *
 * Returns the URI of the resource or NULL if not found, it must be freed
 *      by the caller.
 */
xmlChar *
xmlACatalogResolveURI(xmlCatalogPtr catal, const xmlChar *URI) {
    xmlChar *ret = NULL;

    if ((URI == NULL) || (catal == NULL))
      return(NULL);

    if (xmlDebugCatalogs)
      xmlGenericError(xmlGenericErrorContext,
            "Resolve URI %s\n", URI);

    if (catal->type == XML_XML_CATALOG_TYPE) {
      ret = xmlCatalogListXMLResolveURI(catal->xml, URI);
      if (ret == XML_CATAL_BREAK)
          ret = NULL;
    } else {
      const xmlChar *sgml;

      sgml = xmlCatalogSGMLResolve(catal, NULL, URI);
      if (sgml != NULL)
            sgml = xmlStrdup(sgml);
    }
    return(ret);
}

#ifdef LIBXML_OUTPUT_ENABLED
/**
 * xmlACatalogDump:
 * @catal:  a Catalog
 * @out:  the file.
 *
 * Dump the given catalog to the given file.
 */
void
xmlACatalogDump(xmlCatalogPtr catal, FILE *out) {
    if ((out == NULL) || (catal == NULL))
      return;

    if (catal->type == XML_XML_CATALOG_TYPE) {
      xmlDumpXMLCatalog(out, catal->xml);
    } else {
      xmlHashScan(catal->sgml,
                (xmlHashScanner) xmlCatalogDumpEntry, out);
    } 
}
#endif /* LIBXML_OUTPUT_ENABLED */

/**
 * xmlACatalogAdd:
 * @catal:  a Catalog
 * @type:  the type of record to add to the catalog
 * @orig:  the system, public or prefix to match 
 * @replace:  the replacement value for the match
 *
 * Add an entry in the catalog, it may overwrite existing but
 * different entries.
 *
 * Returns 0 if successful, -1 otherwise
 */
int
xmlACatalogAdd(xmlCatalogPtr catal, const xmlChar * type,
              const xmlChar * orig, const xmlChar * replace)
{
    int res = -1;

    if (catal == NULL)
      return(-1);

    if (catal->type == XML_XML_CATALOG_TYPE) {
        res = xmlAddXMLCatalog(catal->xml, type, orig, replace);
    } else {
        xmlCatalogEntryType cattype;

        cattype = xmlGetSGMLCatalogEntryType(type);
        if (cattype != XML_CATA_NONE) {
            xmlCatalogEntryPtr entry;

            entry = xmlNewCatalogEntry(cattype, orig, replace, NULL,
                                       XML_CATA_PREFER_NONE, NULL);
          if (catal->sgml == NULL)
            catal->sgml = xmlHashCreate(10);
            res = xmlHashAddEntry(catal->sgml, orig, entry);
        }
    }
    return (res);
}

/**
 * xmlACatalogRemove:
 * @catal:  a Catalog
 * @value:  the value to remove
 *
 * Remove an entry from the catalog
 *
 * Returns the number of entries removed if successful, -1 otherwise
 */
int
xmlACatalogRemove(xmlCatalogPtr catal, const xmlChar *value) {
    int res = -1;

    if ((catal == NULL) || (value == NULL))
      return(-1);

    if (catal->type == XML_XML_CATALOG_TYPE) {
      res = xmlDelXMLCatalog(catal->xml, value);
    } else {
      res = xmlHashRemoveEntry(catal->sgml, value,
            (xmlHashDeallocator) xmlFreeCatalogEntry);
      if (res == 0)
          res = 1;
    } 
    return(res);
}

/**
 * xmlNewCatalog:
 * @sgml:  should this create an SGML catalog
 *
 * create a new Catalog.
 *
 * Returns the xmlCatalogPtr or NULL in case of error
 */
xmlCatalogPtr
xmlNewCatalog(int sgml) {
    xmlCatalogPtr catal = NULL;

    if (sgml) {
      catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE,
                                xmlCatalogDefaultPrefer);
        if ((catal != NULL) && (catal->sgml == NULL))
          catal->sgml = xmlHashCreate(10);
    } else
      catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
                                xmlCatalogDefaultPrefer);
    return(catal);
}

/**
 * xmlCatalogIsEmpty:
 * @catal:  should this create an SGML catalog
 *
 * Check is a catalog is empty
 *
 * Returns 1 if the catalog is empty, 0 if not, amd -1 in case of error.
 */
int
xmlCatalogIsEmpty(xmlCatalogPtr catal) {
    if (catal == NULL)
      return(-1);

    if (catal->type == XML_XML_CATALOG_TYPE) {
      if (catal->xml == NULL)
          return(1);
      if ((catal->xml->type != XML_CATA_CATALOG) &&
          (catal->xml->type != XML_CATA_BROKEN_CATALOG))
          return(-1);
      if (catal->xml->children == NULL)
          return(1);
        return(0);
    } else {
      int res;

      if (catal->sgml == NULL)
          return(1);
      res = xmlHashSize(catal->sgml);
      if (res == 0)
          return(1);
      if (res < 0)
          return(-1);
    } 
    return(0);
}

/************************************************************************
 *                                                    *
 *   Public interfaces manipulating the global shared default catalog   *
 *                                                    *
 ************************************************************************/

/**
 * xmlInitializeCatalogData:
 *
 * Do the catalog initialization only of global data, doesn't try to load
 * any catalog actually.
 * this function is not thread safe, catalog initialization should
 * preferably be done once at startup
 */
static void
xmlInitializeCatalogData(void) {
    if (xmlCatalogInitialized != 0)
      return;

    if (getenv("XML_DEBUG_CATALOG")) 
      xmlDebugCatalogs = 1;
    xmlCatalogMutex = xmlNewRMutex();

    xmlCatalogInitialized = 1;
}
/**
 * xmlInitializeCatalog:
 *
 * Do the catalog initialization.
 * this function is not thread safe, catalog initialization should
 * preferably be done once at startup
 */
void
xmlInitializeCatalog(void) {
    if (xmlCatalogInitialized != 0)
      return;

    xmlInitializeCatalogData();
    xmlRMutexLock(xmlCatalogMutex);

    if (getenv("XML_DEBUG_CATALOG")) 
      xmlDebugCatalogs = 1;

    if (xmlDefaultCatalog == NULL) {
      const char *catalogs;
      char *path;
      const char *cur, *paths;
      xmlCatalogPtr catal;
      xmlCatalogEntryPtr *nextent;

      catalogs = (const char *) getenv("XML_CATALOG_FILES");
      if (catalogs == NULL)
#if defined(_WIN32) && defined(_MSC_VER)
    {
            void* hmodule;
            hmodule = GetModuleHandleA("libxml2.dll");
            if (hmodule == NULL)
                  hmodule = GetModuleHandleA(NULL);
            if (hmodule != NULL) {
                  char buf[256];
                  unsigned long len = GetModuleFileNameA(hmodule, buf, 255);
                  if (len != 0) {
                        char* p = &(buf[len]);
                        while (*p != '\\' && p > buf) 
                              p--;
                        if (p != buf) {
                              xmlChar* uri;
                              strncpy(p, "\\..\\etc\\catalog", 255 - (p - buf));
                              uri = xmlCanonicPath(buf);
                              if (uri != NULL) {
                                    strncpy(XML_XML_DEFAULT_CATALOG, uri, 255);
                                    xmlFree(uri);
                              }
                        }
                  }
            }
            catalogs = XML_XML_DEFAULT_CATALOG;
    }
#else
          catalogs = XML_XML_DEFAULT_CATALOG;
#endif

      catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, 
            xmlCatalogDefaultPrefer);
      if (catal != NULL) {
          /* the XML_CATALOG_FILES envvar is allowed to contain a 
             space-separated list of entries. */
          cur = catalogs;
          nextent = &catal->xml;
          while (*cur != '\0') {
            while (xmlIsBlank_ch(*cur)) 
                cur++;
            if (*cur != 0) {
                paths = cur;
                while ((*cur != 0) && (!xmlIsBlank_ch(*cur)))
                  cur++;
                path = (char *) xmlStrndup((const xmlChar *)paths, cur - paths);
                if (path != NULL) {
                  *nextent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
                        NULL, BAD_CAST path, xmlCatalogDefaultPrefer, NULL);
                  if (*nextent != NULL)
                      nextent = &((*nextent)->next);
                  xmlFree(path);
                }
            }
          }
          xmlDefaultCatalog = catal;
      }
    }

    xmlRMutexUnlock(xmlCatalogMutex);
}


/**
 * xmlLoadCatalog:
 * @filename:  a file path
 *
 * Load the catalog and makes its definitions effective for the default
 * external entity loader. It will recurse in SGML CATALOG entries.
 * this function is not thread safe, catalog initialization should
 * preferably be done once at startup
 *
 * Returns 0 in case of success -1 in case of error
 */
int
xmlLoadCatalog(const char *filename)
{
    int ret;
    xmlCatalogPtr catal;

    if (!xmlCatalogInitialized)
      xmlInitializeCatalogData();

    xmlRMutexLock(xmlCatalogMutex);

    if (xmlDefaultCatalog == NULL) {
      catal = xmlLoadACatalog(filename);
      if (catal == NULL) {
          xmlRMutexUnlock(xmlCatalogMutex);
          return(-1);
      }

      xmlDefaultCatalog = catal;
      xmlRMutexUnlock(xmlCatalogMutex);
      return(0);
    }

    ret = xmlExpandCatalog(xmlDefaultCatalog, filename);
    xmlRMutexUnlock(xmlCatalogMutex);
    return(ret);
}

/**
 * xmlLoadCatalogs:
 * @pathss:  a list of directories separated by a colon or a space.
 *
 * Load the catalogs and makes their definitions effective for the default
 * external entity loader.
 * this function is not thread safe, catalog initialization should
 * preferably be done once at startup
 */
void
xmlLoadCatalogs(const char *pathss) {
    const char *cur;
    const char *paths;
    xmlChar *path;
#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_SEAPARATOR) && (!xmlIsBlank_ch(*cur)))
            cur++;
          path = xmlStrndup((const xmlChar *)paths, cur - paths);
#ifdef _WIN32
        iLen = strlen(path);
        for(i = 0; i < iLen; i++) {
            if(path[i] == '\\') {
                path[i] = '/';
            }
        }
#endif
          if (path != NULL) {
            xmlLoadCatalog((const char *) path);
            xmlFree(path);
          }
      }
      while (*cur == PATH_SEAPARATOR)
          cur++;
    }
}

/**
 * xmlCatalogCleanup:
 *
 * Free up all the memory associated with catalogs
 */
void
xmlCatalogCleanup(void) {
    if (xmlCatalogInitialized == 0)
        return;

    xmlRMutexLock(xmlCatalogMutex);
    if (xmlDebugCatalogs)
      xmlGenericError(xmlGenericErrorContext,
            "Catalogs cleanup\n");
    if (xmlCatalogXMLFiles != NULL)
      xmlHashFree(xmlCatalogXMLFiles, 
                (xmlHashDeallocator)xmlFreeCatalogHashEntryList);
    xmlCatalogXMLFiles = NULL;
    if (xmlDefaultCatalog != NULL)
      xmlFreeCatalog(xmlDefaultCatalog);
    xmlDefaultCatalog = NULL;
    xmlDebugCatalogs = 0;
    xmlCatalogInitialized = 0;
    xmlRMutexUnlock(xmlCatalogMutex);
    xmlFreeRMutex(xmlCatalogMutex);
}

/**
 * xmlCatalogResolveSystem:
 * @sysID:  the system ID string
 *
 * Try to lookup the catalog resource for a system ID
 *
 * Returns the resource if found or NULL otherwise, the value returned
 *      must be freed by the caller.
 */
xmlChar *
xmlCatalogResolveSystem(const xmlChar *sysID) {
    xmlChar *ret;

    if (!xmlCatalogInitialized)
      xmlInitializeCatalog();

    ret = xmlACatalogResolveSystem(xmlDefaultCatalog, sysID);
    return(ret);
}

/**
 * xmlCatalogResolvePublic:
 * @pubID:  the public ID string
 *
 * Try to lookup the catalog reference associated to a public ID
 *
 * Returns the resource if found or NULL otherwise, the value returned
 *      must be freed by the caller.
 */
xmlChar *
xmlCatalogResolvePublic(const xmlChar *pubID) {
    xmlChar *ret;

    if (!xmlCatalogInitialized)
      xmlInitializeCatalog();

    ret = xmlACatalogResolvePublic(xmlDefaultCatalog, pubID);
    return(ret);
}

/**
 * xmlCatalogResolve:
 * @pubID:  the public ID string
 * @sysID:  the system ID string
 *
 * Do a complete resolution lookup of an External Identifier
 *
 * Returns the URI of the resource or NULL if not found, it must be freed
 *      by the caller.
 */
xmlChar *
xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) {
    xmlChar *ret;

    if (!xmlCatalogInitialized)
      xmlInitializeCatalog();

    ret = xmlACatalogResolve(xmlDefaultCatalog, pubID, sysID);
    return(ret);
}

/**
 * xmlCatalogResolveURI:
 * @URI:  the URI
 *
 * Do a complete resolution lookup of an URI
 *
 * Returns the URI of the resource or NULL if not found, it must be freed
 *      by the caller.
 */
xmlChar *
xmlCatalogResolveURI(const xmlChar *URI) {
    xmlChar *ret;

    if (!xmlCatalogInitialized)
      xmlInitializeCatalog();

    ret = xmlACatalogResolveURI(xmlDefaultCatalog, URI);
    return(ret);
}

#ifdef LIBXML_OUTPUT_ENABLED
/**
 * xmlCatalogDump:
 * @out:  the file.
 *
 * Dump all the global catalog content to the given file.
 */
void
xmlCatalogDump(FILE *out) {
    if (out == NULL)
      return;

    if (!xmlCatalogInitialized)
      xmlInitializeCatalog();

    xmlACatalogDump(xmlDefaultCatalog, out);
}
#endif /* LIBXML_OUTPUT_ENABLED */

/**
 * xmlCatalogAdd:
 * @type:  the type of record to add to the catalog
 * @orig:  the system, public or prefix to match 
 * @replace:  the replacement value for the match
 *
 * Add an entry in the catalog, it may overwrite existing but
 * different entries.
 * If called before any other catalog routine, allows to override the
 * default shared catalog put in place by xmlInitializeCatalog();
 *
 * Returns 0 if successful, -1 otherwise
 */
int
xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) {
    int res = -1;

    if (!xmlCatalogInitialized)
      xmlInitializeCatalogData();

    xmlRMutexLock(xmlCatalogMutex);
    /*
     * Specific case where one want to override the default catalog
     * put in place by xmlInitializeCatalog();
     */
    if ((xmlDefaultCatalog == NULL) &&
      (xmlStrEqual(type, BAD_CAST "catalog"))) {
      xmlDefaultCatalog = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
                                      xmlCatalogDefaultPrefer);
      xmlDefaultCatalog->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
                            orig, NULL,  xmlCatalogDefaultPrefer, NULL);

      xmlRMutexUnlock(xmlCatalogMutex);
      return(0);
    } 

    res = xmlACatalogAdd(xmlDefaultCatalog, type, orig, replace);
    xmlRMutexUnlock(xmlCatalogMutex);
    return(res);
}

/**
 * xmlCatalogRemove:
 * @value:  the value to remove
 *
 * Remove an entry from the catalog
 *
 * Returns the number of entries removed if successful, -1 otherwise
 */
int
xmlCatalogRemove(const xmlChar *value) {
    int res;

    if (!xmlCatalogInitialized)
      xmlInitializeCatalog();

    xmlRMutexLock(xmlCatalogMutex);
    res = xmlACatalogRemove(xmlDefaultCatalog, value);
    xmlRMutexUnlock(xmlCatalogMutex);
    return(res);
}

/**
 * xmlCatalogConvert:
 *
 * Convert all the SGML catalog entries as XML ones
 *
 * Returns the number of entries converted if successful, -1 otherwise
 */
int
xmlCatalogConvert(void) {
    int res = -1;

    if (!xmlCatalogInitialized)
      xmlInitializeCatalog();

    xmlRMutexLock(xmlCatalogMutex);
    res = xmlConvertSGMLCatalog(xmlDefaultCatalog);
    xmlRMutexUnlock(xmlCatalogMutex);
    return(res);
}

/************************************************************************
 *                                                    *
 *    Public interface manipulating the common preferences        *
 *                                                    *
 ************************************************************************/

/**
 * xmlCatalogGetDefaults:
 *
 * Used to get the user preference w.r.t. to what catalogs should
 * be accepted
 *
 * Returns the current xmlCatalogAllow value
 */
xmlCatalogAllow
xmlCatalogGetDefaults(void) {
    return(xmlCatalogDefaultAllow);
}

/**
 * xmlCatalogSetDefaults:
 * @allow:  what catalogs should be accepted
 *
 * Used to set the user preference w.r.t. to what catalogs should
 * be accepted
 */
void
xmlCatalogSetDefaults(xmlCatalogAllow allow) {
    if (xmlDebugCatalogs) {
      switch (allow) {
          case XML_CATA_ALLOW_NONE:
            xmlGenericError(xmlGenericErrorContext,
                  "Disabling catalog usage\n");
            break;
          case XML_CATA_ALLOW_GLOBAL:
            xmlGenericError(xmlGenericErrorContext,
                  "Allowing only global catalogs\n");
            break;
          case XML_CATA_ALLOW_DOCUMENT:
            xmlGenericError(xmlGenericErrorContext,
                  "Allowing only catalogs from the document\n");
            break;
          case XML_CATA_ALLOW_ALL:
            xmlGenericError(xmlGenericErrorContext,
                  "Allowing all catalogs\n");
            break;
      }
    }
    xmlCatalogDefaultAllow = allow;
}

/**
 * xmlCatalogSetDefaultPrefer:
 * @prefer:  the default preference for delegation
 *
 * Allows to set the preference between public and system for deletion
 * in XML Catalog resolution. C.f. section 4.1.1 of the spec
 * Values accepted are XML_CATA_PREFER_PUBLIC or XML_CATA_PREFER_SYSTEM
 *
 * Returns the previous value of the default preference for delegation
 */
xmlCatalogPrefer
xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer) {
    xmlCatalogPrefer ret = xmlCatalogDefaultPrefer;

    if (prefer == XML_CATA_PREFER_NONE)
      return(ret);

    if (xmlDebugCatalogs) {
      switch (prefer) {
          case XML_CATA_PREFER_PUBLIC:
            xmlGenericError(xmlGenericErrorContext,
                  "Setting catalog preference to PUBLIC\n");
            break;
          case XML_CATA_PREFER_SYSTEM:
            xmlGenericError(xmlGenericErrorContext,
                  "Setting catalog preference to SYSTEM\n");
            break;
          case XML_CATA_PREFER_NONE:
            break;
      }
    }
    xmlCatalogDefaultPrefer = prefer;
    return(ret);
}

/**
 * xmlCatalogSetDebug:
 * @level:  the debug level of catalogs required
 *
 * Used to set the debug level for catalog operation, 0 disable
 * debugging, 1 enable it
 *
 * Returns the previous value of the catalog debugging level
 */
int
xmlCatalogSetDebug(int level) {
    int ret = xmlDebugCatalogs;

    if (level <= 0)
        xmlDebugCatalogs = 0;
    else
      xmlDebugCatalogs = level;
    return(ret);
}

/************************************************************************
 *                                                    *
 *   Minimal interfaces used for per-document catalogs by the parser    *
 *                                                    *
 ************************************************************************/

/**
 * xmlCatalogFreeLocal:
 * @catalogs:  a document's list of catalogs
 *
 * Free up the memory associated to the catalog list
 */
void
xmlCatalogFreeLocal(void *catalogs) {
    xmlCatalogEntryPtr catal;

    if (!xmlCatalogInitialized)
      xmlInitializeCatalog();

    catal = (xmlCatalogEntryPtr) catalogs;
    if (catal != NULL)
      xmlFreeCatalogEntryList(catal);
}


/**
 * xmlCatalogAddLocal:
 * @catalogs:  a document's list of catalogs
 * @URL:  the URL to a new local catalog
 *
 * Add the new entry to the catalog list
 *
 * Returns the updated list
 */
void *      
xmlCatalogAddLocal(void *catalogs, const xmlChar *URL) {
    xmlCatalogEntryPtr catal, add;

    if (!xmlCatalogInitialized)
      xmlInitializeCatalog();

    if (URL == NULL)
      return(catalogs);

    if (xmlDebugCatalogs)
      xmlGenericError(xmlGenericErrorContext,
            "Adding document catalog %s\n", URL);

    add = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, URL, NULL,
                           xmlCatalogDefaultPrefer, NULL);
    if (add == NULL)
      return(catalogs);

    catal = (xmlCatalogEntryPtr) catalogs;
    if (catal == NULL) 
      return((void *) add);

    while (catal->next != NULL)
      catal = catal->next;
    catal->next = add;
    return(catalogs);
}

/**
 * xmlCatalogLocalResolve:
 * @catalogs:  a document's list of catalogs
 * @pubID:  the public ID string
 * @sysID:  the system ID string
 *
 * Do a complete resolution lookup of an External Identifier using a 
 * document's private catalog list
 *
 * Returns the URI of the resource or NULL if not found, it must be freed
 *      by the caller.
 */
xmlChar *
xmlCatalogLocalResolve(void *catalogs, const xmlChar *pubID,
                     const xmlChar *sysID) {
    xmlCatalogEntryPtr catal;
    xmlChar *ret;

    if (!xmlCatalogInitialized)
      xmlInitializeCatalog();

    if ((pubID == NULL) && (sysID == NULL))
      return(NULL);

    if (xmlDebugCatalogs) {
        if ((pubID != NULL) && (sysID != NULL)) {
            xmlGenericError(xmlGenericErrorContext,
                            "Local Resolve: pubID %s sysID %s\n", pubID, sysID);
        } else if (pubID != NULL) {
            xmlGenericError(xmlGenericErrorContext,
                            "Local Resolve: pubID %s\n", pubID);
        } else {
            xmlGenericError(xmlGenericErrorContext,
                            "Local Resolve: sysID %s\n", sysID);
        }
    }

    catal = (xmlCatalogEntryPtr) catalogs;
    if (catal == NULL)
      return(NULL);
    ret = xmlCatalogListXMLResolve(catal, pubID, sysID);
    if ((ret != NULL) && (ret != XML_CATAL_BREAK))
      return(ret);
    return(NULL);
}

/**
 * xmlCatalogLocalResolveURI:
 * @catalogs:  a document's list of catalogs
 * @URI:  the URI
 *
 * Do a complete resolution lookup of an URI using a 
 * document's private catalog list
 *
 * Returns the URI of the resource or NULL if not found, it must be freed
 *      by the caller.
 */
xmlChar *
xmlCatalogLocalResolveURI(void *catalogs, const xmlChar *URI) {
    xmlCatalogEntryPtr catal;
    xmlChar *ret;

    if (!xmlCatalogInitialized)
      xmlInitializeCatalog();

    if (URI == NULL)
      return(NULL);

    if (xmlDebugCatalogs)
      xmlGenericError(xmlGenericErrorContext,
            "Resolve URI %s\n", URI);

    catal = (xmlCatalogEntryPtr) catalogs;
    if (catal == NULL)
      return(NULL);
    ret = xmlCatalogListXMLResolveURI(catal, URI);
    if ((ret != NULL) && (ret != XML_CATAL_BREAK))
      return(ret);
    return(NULL);
}

/************************************************************************
 *                                                    *
 *                Deprecated interfaces                     *
 *                                                    *
 ************************************************************************/
/**
 * xmlCatalogGetSystem:
 * @sysID:  the system ID string
 *
 * Try to lookup the catalog reference associated to a system ID
 * DEPRECATED, use xmlCatalogResolveSystem()
 *
 * Returns the resource if found or NULL otherwise.
 */
const xmlChar *
xmlCatalogGetSystem(const xmlChar *sysID) {
    xmlChar *ret;
    static xmlChar result[1000];
    static int msg = 0;

    if (!xmlCatalogInitialized)
      xmlInitializeCatalog();

    if (msg == 0) {
      xmlGenericError(xmlGenericErrorContext,
            "Use of deprecated xmlCatalogGetSystem() call\n");
      msg++;
    }

    if (sysID == NULL)
      return(NULL);
    
    /*
     * Check first the XML catalogs
     */
    if (xmlDefaultCatalog != NULL) {
      ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, NULL, sysID);
      if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
          snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
          result[sizeof(result) - 1] = 0;
          return(result);
      }
    }

    if (xmlDefaultCatalog != NULL)
      return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog->sgml, sysID));
    return(NULL);
}

/**
 * xmlCatalogGetPublic:
 * @pubID:  the public ID string
 *
 * Try to lookup the catalog reference associated to a public ID
 * DEPRECATED, use xmlCatalogResolvePublic()
 *
 * Returns the resource if found or NULL otherwise.
 */
const xmlChar *
xmlCatalogGetPublic(const xmlChar *pubID) {
    xmlChar *ret;
    static xmlChar result[1000];
    static int msg = 0;

    if (!xmlCatalogInitialized)
      xmlInitializeCatalog();

    if (msg == 0) {
      xmlGenericError(xmlGenericErrorContext,
            "Use of deprecated xmlCatalogGetPublic() call\n");
      msg++;
    }

    if (pubID == NULL)
      return(NULL);
    
    /*
     * Check first the XML catalogs
     */
    if (xmlDefaultCatalog != NULL) {
      ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, pubID, NULL);
      if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
          snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
          result[sizeof(result) - 1] = 0;
          return(result);
      }
    }

    if (xmlDefaultCatalog != NULL)
      return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog->sgml, pubID));
    return(NULL);
}

#define bottom_catalog
#include "elfgcchack.h"
#endif /* LIBXML_CATALOG_ENABLED */

Generated by  Doxygen 1.6.0   Back to index